223 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Technologic Systems TS72xx NAND controller driver4 *5 * Copyright (C) 2023 Nikita Shubin <nikita.shubin@maquefel.me>6 *7 * Derived from: plat_nand.c8 * Author: Vitaly Wool <vitalywool@gmail.com>9 */10 11#include <linux/bits.h>12#include <linux/err.h>13#include <linux/io.h>14#include <linux/iopoll.h>15#include <linux/module.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18 19#include <linux/mtd/mtd.h>20#include <linux/mtd/platnand.h>21 22#define TS72XX_NAND_CONTROL_ADDR_LINE BIT(22) /* 0xN0400000 */23#define TS72XX_NAND_BUSY_ADDR_LINE BIT(23) /* 0xN0800000 */24 25#define TS72XX_NAND_ALE BIT(0)26#define TS72XX_NAND_CLE BIT(1)27#define TS72XX_NAND_NCE BIT(2)28 29#define TS72XX_NAND_CTRL_CLE (TS72XX_NAND_NCE | TS72XX_NAND_CLE)30#define TS72XX_NAND_CTRL_ALE (TS72XX_NAND_NCE | TS72XX_NAND_ALE)31 32struct ts72xx_nand_data {33 struct nand_controller controller;34 struct nand_chip chip;35 void __iomem *base;36 void __iomem *ctrl;37 void __iomem *busy;38};39 40static inline struct ts72xx_nand_data *chip_to_ts72xx(struct nand_chip *chip)41{42 return container_of(chip, struct ts72xx_nand_data, chip);43}44 45static int ts72xx_nand_attach_chip(struct nand_chip *chip)46{47 switch (chip->ecc.engine_type) {48 case NAND_ECC_ENGINE_TYPE_ON_HOST:49 return -EINVAL;50 case NAND_ECC_ENGINE_TYPE_SOFT:51 if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN)52 chip->ecc.algo = NAND_ECC_ALGO_HAMMING;53 chip->ecc.algo = NAND_ECC_ALGO_HAMMING;54 fallthrough;55 default:56 return 0;57 }58}59 60static void ts72xx_nand_ctrl(struct nand_chip *chip, u8 value)61{62 struct ts72xx_nand_data *data = chip_to_ts72xx(chip);63 unsigned char bits = ioread8(data->ctrl) & ~GENMASK(2, 0);64 65 iowrite8(bits | value, data->ctrl);66}67 68static int ts72xx_nand_exec_instr(struct nand_chip *chip,69 const struct nand_op_instr *instr)70{71 struct ts72xx_nand_data *data = chip_to_ts72xx(chip);72 unsigned int timeout_us;73 u32 status;74 int ret;75 76 switch (instr->type) {77 case NAND_OP_CMD_INSTR:78 ts72xx_nand_ctrl(chip, TS72XX_NAND_CTRL_CLE);79 iowrite8(instr->ctx.cmd.opcode, data->base);80 ts72xx_nand_ctrl(chip, TS72XX_NAND_NCE);81 break;82 83 case NAND_OP_ADDR_INSTR:84 ts72xx_nand_ctrl(chip, TS72XX_NAND_CTRL_ALE);85 iowrite8_rep(data->base, instr->ctx.addr.addrs, instr->ctx.addr.naddrs);86 ts72xx_nand_ctrl(chip, TS72XX_NAND_NCE);87 break;88 89 case NAND_OP_DATA_IN_INSTR:90 ioread8_rep(data->base, instr->ctx.data.buf.in, instr->ctx.data.len);91 break;92 93 case NAND_OP_DATA_OUT_INSTR:94 iowrite8_rep(data->base, instr->ctx.data.buf.in, instr->ctx.data.len);95 break;96 97 case NAND_OP_WAITRDY_INSTR:98 timeout_us = instr->ctx.waitrdy.timeout_ms * 1000;99 ret = readb_poll_timeout(data->busy, status, status & BIT(5), 0, timeout_us);100 if (ret)101 return ret;102 103 break;104 }105 106 if (instr->delay_ns)107 ndelay(instr->delay_ns);108 109 return 0;110}111 112static int ts72xx_nand_exec_op(struct nand_chip *chip,113 const struct nand_operation *op, bool check_only)114{115 unsigned int i;116 int ret;117 118 if (check_only)119 return 0;120 121 for (i = 0; i < op->ninstrs; i++) {122 ret = ts72xx_nand_exec_instr(chip, &op->instrs[i]);123 if (ret)124 return ret;125 }126 127 return 0;128}129 130static const struct nand_controller_ops ts72xx_nand_ops = {131 .attach_chip = ts72xx_nand_attach_chip,132 .exec_op = ts72xx_nand_exec_op,133};134 135static int ts72xx_nand_probe(struct platform_device *pdev)136{137 struct ts72xx_nand_data *data;138 struct fwnode_handle *child;139 struct mtd_info *mtd;140 int err;141 142 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);143 if (!data)144 return -ENOMEM;145 146 nand_controller_init(&data->controller);147 data->controller.ops = &ts72xx_nand_ops;148 data->chip.controller = &data->controller;149 150 data->base = devm_platform_ioremap_resource(pdev, 0);151 if (IS_ERR(data->base))152 return PTR_ERR(data->base);153 data->ctrl = data->base + TS72XX_NAND_CONTROL_ADDR_LINE;154 data->busy = data->base + TS72XX_NAND_BUSY_ADDR_LINE;155 156 child = fwnode_get_next_child_node(dev_fwnode(&pdev->dev), NULL);157 if (!child)158 return dev_err_probe(&pdev->dev, -ENXIO,159 "ts72xx controller node should have exactly one child\n");160 161 nand_set_flash_node(&data->chip, to_of_node(child));162 mtd = nand_to_mtd(&data->chip);163 mtd->dev.parent = &pdev->dev;164 platform_set_drvdata(pdev, data);165 166 /*167 * This driver assumes that the default ECC engine should be TYPE_SOFT.168 * Set ->engine_type before registering the NAND devices in order to169 * provide a driver specific default value.170 */171 data->chip.ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;172 173 /* Scan to find existence of the device */174 err = nand_scan(&data->chip, 1);175 if (err)176 goto err_handle_put;177 178 err = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);179 if (err)180 goto err_clean_nand;181 182 return 0;183 184err_clean_nand:185 nand_cleanup(&data->chip);186err_handle_put:187 fwnode_handle_put(child);188 return err;189}190 191static void ts72xx_nand_remove(struct platform_device *pdev)192{193 struct ts72xx_nand_data *data = platform_get_drvdata(pdev);194 struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);195 struct nand_chip *chip = &data->chip;196 int ret;197 198 ret = mtd_device_unregister(nand_to_mtd(chip));199 WARN_ON(ret);200 nand_cleanup(chip);201 fwnode_handle_put(fwnode);202}203 204static const struct of_device_id ts72xx_id_table[] = {205 { .compatible = "technologic,ts7200-nand" },206 { /* sentinel */ }207};208MODULE_DEVICE_TABLE(of, ts72xx_id_table);209 210static struct platform_driver ts72xx_nand_driver = {211 .driver = {212 .name = "ts72xx-nand",213 .of_match_table = ts72xx_id_table,214 },215 .probe = ts72xx_nand_probe,216 .remove_new = ts72xx_nand_remove,217};218module_platform_driver(ts72xx_nand_driver);219 220MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>");221MODULE_DESCRIPTION("Technologic Systems TS72xx NAND controller driver");222MODULE_LICENSE("GPL");223