Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mtd: rawnand: add support for ts72xx

Technologic Systems has it's own nand controller implementation in CPLD.

Signed-off-by: Nikita Shubin <nikita.shubin@maquefel.me>
Tested-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

authored by

Nikita Shubin and committed by
Arnd Bergmann
853034c7 1d4f2ff1

+229
+6
drivers/mtd/nand/raw/Kconfig
··· 448 448 Enables support for the NAND controller found on Renesas R-Car 449 449 Gen3 and RZ/N1 SoC families. 450 450 451 + config MTD_NAND_TS72XX 452 + tristate "ts72xx NAND controller" 453 + depends on ARCH_EP93XX && HAS_IOMEM 454 + help 455 + Enables support for NAND controller on ts72xx SBCs. 456 + 451 457 comment "Misc" 452 458 453 459 config MTD_SM_COMMON
+1
drivers/mtd/nand/raw/Makefile
··· 34 34 obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o 35 35 obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o 36 36 obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o 37 + obj-$(CONFIG_MTD_NAND_TS72XX) += technologic-nand-controller.o 37 38 obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o 38 39 obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o 39 40 obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
+222
drivers/mtd/nand/raw/technologic-nand-controller.c
··· 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_new = 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");