1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Technologic Systems TS72xx NAND controller driver 4 * 5 * Copyright (C) 2023 Nikita Shubin <nikita.shubin@maquefel.me> 6 * 7 * Derived from: plat_nand.c 8 * 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 32 struct 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 40 static 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 45 static 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 60 static 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 68 static 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 112 static 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 130 static const struct nand_controller_ops ts72xx_nand_ops = { 131 .attach_chip = ts72xx_nand_attach_chip, 132 .exec_op = ts72xx_nand_exec_op, 133 }; 134 135 static 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 to 169 * 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 184 err_clean_nand: 185 nand_cleanup(&data->chip); 186 err_handle_put: 187 fwnode_handle_put(child); 188 return err; 189 } 190 191 static 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 204 static const struct of_device_id ts72xx_id_table[] = { 205 { .compatible = "technologic,ts7200-nand" }, 206 { /* sentinel */ } 207 }; 208 MODULE_DEVICE_TABLE(of, ts72xx_id_table); 209 210 static 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 = ts72xx_nand_remove, 217 }; 218 module_platform_driver(ts72xx_nand_driver); 219 220 MODULE_AUTHOR("Nikita Shubin <nikita.shubin@maquefel.me>"); 221 MODULE_DESCRIPTION("Technologic Systems TS72xx NAND controller driver"); 222 MODULE_LICENSE("GPL"); 223