18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Updated, and converted to generic GPIO based driver by Russell King. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Written by Ben Dooks <ben@simtec.co.uk> 68c2ecf20Sopenharmony_ci * Based on 2.4 version by Mark Whittaker 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * © 2004 Simtec Electronics 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Device driver for NAND flash that uses a memory mapped interface to 118c2ecf20Sopenharmony_ci * read/write the NAND commands and data, and GPIO pins for control signals 128c2ecf20Sopenharmony_ci * (the DT binding refers to this as "GPIO assisted NAND flash") 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 248c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 258c2ecf20Sopenharmony_ci#include <linux/mtd/nand-gpio.h> 268c2ecf20Sopenharmony_ci#include <linux/of.h> 278c2ecf20Sopenharmony_ci#include <linux/of_address.h> 288c2ecf20Sopenharmony_ci#include <linux/delay.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct gpiomtd { 318c2ecf20Sopenharmony_ci struct nand_controller base; 328c2ecf20Sopenharmony_ci void __iomem *io; 338c2ecf20Sopenharmony_ci void __iomem *io_sync; 348c2ecf20Sopenharmony_ci struct nand_chip nand_chip; 358c2ecf20Sopenharmony_ci struct gpio_nand_platdata plat; 368c2ecf20Sopenharmony_ci struct gpio_desc *nce; /* Optional chip enable */ 378c2ecf20Sopenharmony_ci struct gpio_desc *cle; 388c2ecf20Sopenharmony_ci struct gpio_desc *ale; 398c2ecf20Sopenharmony_ci struct gpio_desc *rdy; 408c2ecf20Sopenharmony_ci struct gpio_desc *nwp; /* Optional write protection */ 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM 508c2ecf20Sopenharmony_ci/* gpio_nand_dosync() 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * Make sure the GPIO state changes occur in-order with writes to NAND 538c2ecf20Sopenharmony_ci * memory region. 548c2ecf20Sopenharmony_ci * Needed on PXA due to bus-reordering within the SoC itself (see section on 558c2ecf20Sopenharmony_ci * I/O ordering in PXA manual (section 2.3, p35) 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic void gpio_nand_dosync(struct gpiomtd *gpiomtd) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci unsigned long tmp; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (gpiomtd->io_sync) { 628c2ecf20Sopenharmony_ci /* 638c2ecf20Sopenharmony_ci * Linux memory barriers don't cater for what's required here. 648c2ecf20Sopenharmony_ci * What's required is what's here - a read from a separate 658c2ecf20Sopenharmony_ci * region with a dependency on that read. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci tmp = readl(gpiomtd->io_sync); 688c2ecf20Sopenharmony_ci asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci#else 728c2ecf20Sopenharmony_cistatic inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int gpio_nand_exec_instr(struct nand_chip *chip, 768c2ecf20Sopenharmony_ci const struct nand_op_instr *instr) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); 798c2ecf20Sopenharmony_ci unsigned int i; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci switch (instr->type) { 828c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 838c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 848c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->cle, 1); 858c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 868c2ecf20Sopenharmony_ci writeb(instr->ctx.cmd.opcode, gpiomtd->io); 878c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 888c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->cle, 0); 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 928c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 938c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->ale, 1); 948c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 958c2ecf20Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 968c2ecf20Sopenharmony_ci writeb(instr->ctx.addr.addrs[i], gpiomtd->io); 978c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 988c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->ale, 0); 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 1028c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 1038c2ecf20Sopenharmony_ci if ((chip->options & NAND_BUSWIDTH_16) && 1048c2ecf20Sopenharmony_ci !instr->ctx.data.force_8bit) 1058c2ecf20Sopenharmony_ci ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in, 1068c2ecf20Sopenharmony_ci instr->ctx.data.len / 2); 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in, 1098c2ecf20Sopenharmony_ci instr->ctx.data.len); 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 1138c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 1148c2ecf20Sopenharmony_ci if ((chip->options & NAND_BUSWIDTH_16) && 1158c2ecf20Sopenharmony_ci !instr->ctx.data.force_8bit) 1168c2ecf20Sopenharmony_ci iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out, 1178c2ecf20Sopenharmony_ci instr->ctx.data.len / 2); 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out, 1208c2ecf20Sopenharmony_ci instr->ctx.data.len); 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 1248c2ecf20Sopenharmony_ci if (!gpiomtd->rdy) 1258c2ecf20Sopenharmony_ci return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return nand_gpio_waitrdy(chip, gpiomtd->rdy, 1288c2ecf20Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci default: 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int gpio_nand_exec_op(struct nand_chip *chip, 1388c2ecf20Sopenharmony_ci const struct nand_operation *op, 1398c2ecf20Sopenharmony_ci bool check_only) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); 1428c2ecf20Sopenharmony_ci unsigned int i; 1438c2ecf20Sopenharmony_ci int ret = 0; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (check_only) 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 1498c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 1508c2ecf20Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 1518c2ecf20Sopenharmony_ci ret = gpio_nand_exec_instr(chip, &op->instrs[i]); 1528c2ecf20Sopenharmony_ci if (ret) 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (op->instrs[i].delay_ns) 1568c2ecf20Sopenharmony_ci ndelay(op->instrs[i].delay_ns); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci gpio_nand_dosync(gpiomtd); 1598c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 1); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int gpio_nand_attach_chip(struct nand_chip *chip) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && 1678c2ecf20Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 1688c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic const struct nand_controller_ops gpio_nand_ops = { 1748c2ecf20Sopenharmony_ci .exec_op = gpio_nand_exec_op, 1758c2ecf20Sopenharmony_ci .attach_chip = gpio_nand_attach_chip, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 1798c2ecf20Sopenharmony_cistatic const struct of_device_id gpio_nand_id_table[] = { 1808c2ecf20Sopenharmony_ci { .compatible = "gpio-control-nand" }, 1818c2ecf20Sopenharmony_ci {} 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_nand_id_table); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int gpio_nand_get_config_of(const struct device *dev, 1868c2ecf20Sopenharmony_ci struct gpio_nand_platdata *plat) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci u32 val; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!dev->of_node) 1918c2ecf20Sopenharmony_ci return -ENODEV; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { 1948c2ecf20Sopenharmony_ci if (val == 2) { 1958c2ecf20Sopenharmony_ci plat->options |= NAND_BUSWIDTH_16; 1968c2ecf20Sopenharmony_ci } else if (val != 1) { 1978c2ecf20Sopenharmony_ci dev_err(dev, "invalid bank-width %u\n", val); 1988c2ecf20Sopenharmony_ci return -EINVAL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) 2038c2ecf20Sopenharmony_ci plat->chip_delay = val; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct resource *r; 2118c2ecf20Sopenharmony_ci u64 addr; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (of_property_read_u64(pdev->dev.of_node, 2148c2ecf20Sopenharmony_ci "gpio-control-nand,io-sync-reg", &addr)) 2158c2ecf20Sopenharmony_ci return NULL; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); 2188c2ecf20Sopenharmony_ci if (!r) 2198c2ecf20Sopenharmony_ci return NULL; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci r->start = addr; 2228c2ecf20Sopenharmony_ci r->end = r->start + 0x3; 2238c2ecf20Sopenharmony_ci r->flags = IORESOURCE_MEM; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return r; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci#else /* CONFIG_OF */ 2288c2ecf20Sopenharmony_cistatic inline int gpio_nand_get_config_of(const struct device *dev, 2298c2ecf20Sopenharmony_ci struct gpio_nand_platdata *plat) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci return -ENOSYS; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic inline struct resource * 2358c2ecf20Sopenharmony_cigpio_nand_get_io_sync_of(struct platform_device *pdev) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci return NULL; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic inline int gpio_nand_get_config(const struct device *dev, 2428c2ecf20Sopenharmony_ci struct gpio_nand_platdata *plat) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci int ret = gpio_nand_get_config_of(dev, plat); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (dev_get_platdata(dev)) { 2508c2ecf20Sopenharmony_ci memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return -EINVAL; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic inline struct resource * 2588c2ecf20Sopenharmony_cigpio_nand_get_io_sync(struct platform_device *pdev) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct resource *r = gpio_nand_get_io_sync_of(pdev); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (r) 2638c2ecf20Sopenharmony_ci return r; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci return platform_get_resource(pdev, IORESOURCE_MEM, 1); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int gpio_nand_remove(struct platform_device *pdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); 2718c2ecf20Sopenharmony_ci struct nand_chip *chip = &gpiomtd->nand_chip; 2728c2ecf20Sopenharmony_ci int ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 2758c2ecf20Sopenharmony_ci WARN_ON(ret); 2768c2ecf20Sopenharmony_ci nand_cleanup(chip); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Enable write protection and disable the chip */ 2798c2ecf20Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 2808c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nwp, 0); 2818c2ecf20Sopenharmony_ci if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) 2828c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int gpio_nand_probe(struct platform_device *pdev) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct gpiomtd *gpiomtd; 2908c2ecf20Sopenharmony_ci struct nand_chip *chip; 2918c2ecf20Sopenharmony_ci struct mtd_info *mtd; 2928c2ecf20Sopenharmony_ci struct resource *res; 2938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2948c2ecf20Sopenharmony_ci int ret = 0; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!dev->of_node && !dev_get_platdata(dev)) 2978c2ecf20Sopenharmony_ci return -EINVAL; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL); 3008c2ecf20Sopenharmony_ci if (!gpiomtd) 3018c2ecf20Sopenharmony_ci return -ENOMEM; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci chip = &gpiomtd->nand_chip; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3068c2ecf20Sopenharmony_ci gpiomtd->io = devm_ioremap_resource(dev, res); 3078c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->io)) 3088c2ecf20Sopenharmony_ci return PTR_ERR(gpiomtd->io); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci res = gpio_nand_get_io_sync(pdev); 3118c2ecf20Sopenharmony_ci if (res) { 3128c2ecf20Sopenharmony_ci gpiomtd->io_sync = devm_ioremap_resource(dev, res); 3138c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->io_sync)) 3148c2ecf20Sopenharmony_ci return PTR_ERR(gpiomtd->io_sync); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = gpio_nand_get_config(dev, &gpiomtd->plat); 3188c2ecf20Sopenharmony_ci if (ret) 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Just enable the chip */ 3228c2ecf20Sopenharmony_ci gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH); 3238c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->nce)) 3248c2ecf20Sopenharmony_ci return PTR_ERR(gpiomtd->nce); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* We disable write protection once we know probe() will succeed */ 3278c2ecf20Sopenharmony_ci gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW); 3288c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->nwp)) { 3298c2ecf20Sopenharmony_ci ret = PTR_ERR(gpiomtd->nwp); 3308c2ecf20Sopenharmony_ci goto out_ce; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW); 3348c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->ale)) { 3358c2ecf20Sopenharmony_ci ret = PTR_ERR(gpiomtd->ale); 3368c2ecf20Sopenharmony_ci goto out_ce; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW); 3408c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->cle)) { 3418c2ecf20Sopenharmony_ci ret = PTR_ERR(gpiomtd->cle); 3428c2ecf20Sopenharmony_ci goto out_ce; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN); 3468c2ecf20Sopenharmony_ci if (IS_ERR(gpiomtd->rdy)) { 3478c2ecf20Sopenharmony_ci ret = PTR_ERR(gpiomtd->rdy); 3488c2ecf20Sopenharmony_ci goto out_ce; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci nand_controller_init(&gpiomtd->base); 3528c2ecf20Sopenharmony_ci gpiomtd->base.ops = &gpio_nand_ops; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci nand_set_flash_node(chip, pdev->dev.of_node); 3558c2ecf20Sopenharmony_ci chip->options = gpiomtd->plat.options; 3568c2ecf20Sopenharmony_ci chip->controller = &gpiomtd->base; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci mtd = nand_to_mtd(chip); 3598c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, gpiomtd); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Disable write protection, if wired up */ 3648c2ecf20Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 3658c2ecf20Sopenharmony_ci gpiod_direction_output(gpiomtd->nwp, 1); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * This driver assumes that the default ECC engine should be TYPE_SOFT. 3698c2ecf20Sopenharmony_ci * Set ->engine_type before registering the NAND devices in order to 3708c2ecf20Sopenharmony_ci * provide a driver specific default value. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = nand_scan(chip, 1); 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci goto err_wp; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (gpiomtd->plat.adjust_parts) 3798c2ecf20Sopenharmony_ci gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, gpiomtd->plat.parts, 3828c2ecf20Sopenharmony_ci gpiomtd->plat.num_parts); 3838c2ecf20Sopenharmony_ci if (!ret) 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cierr_wp: 3878c2ecf20Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 3888c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nwp, 0); 3898c2ecf20Sopenharmony_ciout_ce: 3908c2ecf20Sopenharmony_ci if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) 3918c2ecf20Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic struct platform_driver gpio_nand_driver = { 3978c2ecf20Sopenharmony_ci .probe = gpio_nand_probe, 3988c2ecf20Sopenharmony_ci .remove = gpio_nand_remove, 3998c2ecf20Sopenharmony_ci .driver = { 4008c2ecf20Sopenharmony_ci .name = "gpio-nand", 4018c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(gpio_nand_id_table), 4028c2ecf20Sopenharmony_ci }, 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cimodule_platform_driver(gpio_nand_driver); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 4098c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO NAND Driver"); 410