162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Updated, and converted to generic GPIO based driver by Russell King. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written by Ben Dooks <ben@simtec.co.uk> 662306a36Sopenharmony_ci * Based on 2.4 version by Mark Whittaker 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * © 2004 Simtec Electronics 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Device driver for NAND flash that uses a memory mapped interface to 1162306a36Sopenharmony_ci * read/write the NAND commands and data, and GPIO pins for control signals 1262306a36Sopenharmony_ci * (the DT binding refers to this as "GPIO assisted NAND flash") 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/err.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2362306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2462306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2562306a36Sopenharmony_ci#include <linux/mtd/nand-gpio.h> 2662306a36Sopenharmony_ci#include <linux/of.h> 2762306a36Sopenharmony_ci#include <linux/of_address.h> 2862306a36Sopenharmony_ci#include <linux/delay.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct gpiomtd { 3162306a36Sopenharmony_ci struct nand_controller base; 3262306a36Sopenharmony_ci void __iomem *io; 3362306a36Sopenharmony_ci void __iomem *io_sync; 3462306a36Sopenharmony_ci struct nand_chip nand_chip; 3562306a36Sopenharmony_ci struct gpio_nand_platdata plat; 3662306a36Sopenharmony_ci struct gpio_desc *nce; /* Optional chip enable */ 3762306a36Sopenharmony_ci struct gpio_desc *cle; 3862306a36Sopenharmony_ci struct gpio_desc *ale; 3962306a36Sopenharmony_ci struct gpio_desc *rdy; 4062306a36Sopenharmony_ci struct gpio_desc *nwp; /* Optional write protection */ 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic inline struct gpiomtd *gpio_nand_getpriv(struct mtd_info *mtd) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct gpiomtd, nand_chip); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifdef CONFIG_ARM 5062306a36Sopenharmony_ci/* gpio_nand_dosync() 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Make sure the GPIO state changes occur in-order with writes to NAND 5362306a36Sopenharmony_ci * memory region. 5462306a36Sopenharmony_ci * Needed on PXA due to bus-reordering within the SoC itself (see section on 5562306a36Sopenharmony_ci * I/O ordering in PXA manual (section 2.3, p35) 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic void gpio_nand_dosync(struct gpiomtd *gpiomtd) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned long tmp; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (gpiomtd->io_sync) { 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * Linux memory barriers don't cater for what's required here. 6462306a36Sopenharmony_ci * What's required is what's here - a read from a separate 6562306a36Sopenharmony_ci * region with a dependency on that read. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci tmp = readl(gpiomtd->io_sync); 6862306a36Sopenharmony_ci asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci#else 7262306a36Sopenharmony_cistatic inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} 7362306a36Sopenharmony_ci#endif 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int gpio_nand_exec_instr(struct nand_chip *chip, 7662306a36Sopenharmony_ci const struct nand_op_instr *instr) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); 7962306a36Sopenharmony_ci unsigned int i; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci switch (instr->type) { 8262306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 8362306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 8462306a36Sopenharmony_ci gpiod_set_value(gpiomtd->cle, 1); 8562306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 8662306a36Sopenharmony_ci writeb(instr->ctx.cmd.opcode, gpiomtd->io); 8762306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 8862306a36Sopenharmony_ci gpiod_set_value(gpiomtd->cle, 0); 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 9262306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 9362306a36Sopenharmony_ci gpiod_set_value(gpiomtd->ale, 1); 9462306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 9562306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 9662306a36Sopenharmony_ci writeb(instr->ctx.addr.addrs[i], gpiomtd->io); 9762306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 9862306a36Sopenharmony_ci gpiod_set_value(gpiomtd->ale, 0); 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 10262306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 10362306a36Sopenharmony_ci if ((chip->options & NAND_BUSWIDTH_16) && 10462306a36Sopenharmony_ci !instr->ctx.data.force_8bit) 10562306a36Sopenharmony_ci ioread16_rep(gpiomtd->io, instr->ctx.data.buf.in, 10662306a36Sopenharmony_ci instr->ctx.data.len / 2); 10762306a36Sopenharmony_ci else 10862306a36Sopenharmony_ci ioread8_rep(gpiomtd->io, instr->ctx.data.buf.in, 10962306a36Sopenharmony_ci instr->ctx.data.len); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 11362306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 11462306a36Sopenharmony_ci if ((chip->options & NAND_BUSWIDTH_16) && 11562306a36Sopenharmony_ci !instr->ctx.data.force_8bit) 11662306a36Sopenharmony_ci iowrite16_rep(gpiomtd->io, instr->ctx.data.buf.out, 11762306a36Sopenharmony_ci instr->ctx.data.len / 2); 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci iowrite8_rep(gpiomtd->io, instr->ctx.data.buf.out, 12062306a36Sopenharmony_ci instr->ctx.data.len); 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 12462306a36Sopenharmony_ci if (!gpiomtd->rdy) 12562306a36Sopenharmony_ci return nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return nand_gpio_waitrdy(chip, gpiomtd->rdy, 12862306a36Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci default: 13162306a36Sopenharmony_ci return -EINVAL; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int gpio_nand_exec_op(struct nand_chip *chip, 13862306a36Sopenharmony_ci const struct nand_operation *op, 13962306a36Sopenharmony_ci bool check_only) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct gpiomtd *gpiomtd = gpio_nand_getpriv(nand_to_mtd(chip)); 14262306a36Sopenharmony_ci unsigned int i; 14362306a36Sopenharmony_ci int ret = 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (check_only) 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 14962306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 15062306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 15162306a36Sopenharmony_ci ret = gpio_nand_exec_instr(chip, &op->instrs[i]); 15262306a36Sopenharmony_ci if (ret) 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (op->instrs[i].delay_ns) 15662306a36Sopenharmony_ci ndelay(op->instrs[i].delay_ns); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci gpio_nand_dosync(gpiomtd); 15962306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 1); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int gpio_nand_attach_chip(struct nand_chip *chip) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT && 16762306a36Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 16862306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic const struct nand_controller_ops gpio_nand_ops = { 17462306a36Sopenharmony_ci .exec_op = gpio_nand_exec_op, 17562306a36Sopenharmony_ci .attach_chip = gpio_nand_attach_chip, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#ifdef CONFIG_OF 17962306a36Sopenharmony_cistatic const struct of_device_id gpio_nand_id_table[] = { 18062306a36Sopenharmony_ci { .compatible = "gpio-control-nand" }, 18162306a36Sopenharmony_ci {} 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpio_nand_id_table); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int gpio_nand_get_config_of(const struct device *dev, 18662306a36Sopenharmony_ci struct gpio_nand_platdata *plat) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci u32 val; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!dev->of_node) 19162306a36Sopenharmony_ci return -ENODEV; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { 19462306a36Sopenharmony_ci if (val == 2) { 19562306a36Sopenharmony_ci plat->options |= NAND_BUSWIDTH_16; 19662306a36Sopenharmony_ci } else if (val != 1) { 19762306a36Sopenharmony_ci dev_err(dev, "invalid bank-width %u\n", val); 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!of_property_read_u32(dev->of_node, "chip-delay", &val)) 20362306a36Sopenharmony_ci plat->chip_delay = val; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct resource *r; 21162306a36Sopenharmony_ci u64 addr; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (of_property_read_u64(pdev->dev.of_node, 21462306a36Sopenharmony_ci "gpio-control-nand,io-sync-reg", &addr)) 21562306a36Sopenharmony_ci return NULL; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci r = devm_kzalloc(&pdev->dev, sizeof(*r), GFP_KERNEL); 21862306a36Sopenharmony_ci if (!r) 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci r->start = addr; 22262306a36Sopenharmony_ci r->end = r->start + 0x3; 22362306a36Sopenharmony_ci r->flags = IORESOURCE_MEM; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return r; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci#else /* CONFIG_OF */ 22862306a36Sopenharmony_cistatic inline int gpio_nand_get_config_of(const struct device *dev, 22962306a36Sopenharmony_ci struct gpio_nand_platdata *plat) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci return -ENOSYS; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline struct resource * 23562306a36Sopenharmony_cigpio_nand_get_io_sync_of(struct platform_device *pdev) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return NULL; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci#endif /* CONFIG_OF */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline int gpio_nand_get_config(const struct device *dev, 24262306a36Sopenharmony_ci struct gpio_nand_platdata *plat) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci int ret = gpio_nand_get_config_of(dev, plat); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!ret) 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (dev_get_platdata(dev)) { 25062306a36Sopenharmony_ci memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return -EINVAL; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic inline struct resource * 25862306a36Sopenharmony_cigpio_nand_get_io_sync(struct platform_device *pdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct resource *r = gpio_nand_get_io_sync_of(pdev); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (r) 26362306a36Sopenharmony_ci return r; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return platform_get_resource(pdev, IORESOURCE_MEM, 1); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void gpio_nand_remove(struct platform_device *pdev) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); 27162306a36Sopenharmony_ci struct nand_chip *chip = &gpiomtd->nand_chip; 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 27562306a36Sopenharmony_ci WARN_ON(ret); 27662306a36Sopenharmony_ci nand_cleanup(chip); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Enable write protection and disable the chip */ 27962306a36Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 28062306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nwp, 0); 28162306a36Sopenharmony_ci if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) 28262306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int gpio_nand_probe(struct platform_device *pdev) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct gpiomtd *gpiomtd; 28862306a36Sopenharmony_ci struct nand_chip *chip; 28962306a36Sopenharmony_ci struct mtd_info *mtd; 29062306a36Sopenharmony_ci struct resource *res; 29162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 29262306a36Sopenharmony_ci int ret = 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!dev->of_node && !dev_get_platdata(dev)) 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci gpiomtd = devm_kzalloc(dev, sizeof(*gpiomtd), GFP_KERNEL); 29862306a36Sopenharmony_ci if (!gpiomtd) 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci chip = &gpiomtd->nand_chip; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci gpiomtd->io = devm_platform_ioremap_resource(pdev, 0); 30462306a36Sopenharmony_ci if (IS_ERR(gpiomtd->io)) 30562306a36Sopenharmony_ci return PTR_ERR(gpiomtd->io); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci res = gpio_nand_get_io_sync(pdev); 30862306a36Sopenharmony_ci if (res) { 30962306a36Sopenharmony_ci gpiomtd->io_sync = devm_ioremap_resource(dev, res); 31062306a36Sopenharmony_ci if (IS_ERR(gpiomtd->io_sync)) 31162306a36Sopenharmony_ci return PTR_ERR(gpiomtd->io_sync); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = gpio_nand_get_config(dev, &gpiomtd->plat); 31562306a36Sopenharmony_ci if (ret) 31662306a36Sopenharmony_ci return ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Just enable the chip */ 31962306a36Sopenharmony_ci gpiomtd->nce = devm_gpiod_get_optional(dev, "nce", GPIOD_OUT_HIGH); 32062306a36Sopenharmony_ci if (IS_ERR(gpiomtd->nce)) 32162306a36Sopenharmony_ci return PTR_ERR(gpiomtd->nce); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* We disable write protection once we know probe() will succeed */ 32462306a36Sopenharmony_ci gpiomtd->nwp = devm_gpiod_get_optional(dev, "nwp", GPIOD_OUT_LOW); 32562306a36Sopenharmony_ci if (IS_ERR(gpiomtd->nwp)) { 32662306a36Sopenharmony_ci ret = PTR_ERR(gpiomtd->nwp); 32762306a36Sopenharmony_ci goto out_ce; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci gpiomtd->ale = devm_gpiod_get(dev, "ale", GPIOD_OUT_LOW); 33162306a36Sopenharmony_ci if (IS_ERR(gpiomtd->ale)) { 33262306a36Sopenharmony_ci ret = PTR_ERR(gpiomtd->ale); 33362306a36Sopenharmony_ci goto out_ce; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci gpiomtd->cle = devm_gpiod_get(dev, "cle", GPIOD_OUT_LOW); 33762306a36Sopenharmony_ci if (IS_ERR(gpiomtd->cle)) { 33862306a36Sopenharmony_ci ret = PTR_ERR(gpiomtd->cle); 33962306a36Sopenharmony_ci goto out_ce; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci gpiomtd->rdy = devm_gpiod_get_optional(dev, "rdy", GPIOD_IN); 34362306a36Sopenharmony_ci if (IS_ERR(gpiomtd->rdy)) { 34462306a36Sopenharmony_ci ret = PTR_ERR(gpiomtd->rdy); 34562306a36Sopenharmony_ci goto out_ce; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci nand_controller_init(&gpiomtd->base); 34962306a36Sopenharmony_ci gpiomtd->base.ops = &gpio_nand_ops; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci nand_set_flash_node(chip, pdev->dev.of_node); 35262306a36Sopenharmony_ci chip->options = gpiomtd->plat.options; 35362306a36Sopenharmony_ci chip->controller = &gpiomtd->base; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci mtd = nand_to_mtd(chip); 35662306a36Sopenharmony_ci mtd->dev.parent = dev; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci platform_set_drvdata(pdev, gpiomtd); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Disable write protection, if wired up */ 36162306a36Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 36262306a36Sopenharmony_ci gpiod_direction_output(gpiomtd->nwp, 1); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* 36562306a36Sopenharmony_ci * This driver assumes that the default ECC engine should be TYPE_SOFT. 36662306a36Sopenharmony_ci * Set ->engine_type before registering the NAND devices in order to 36762306a36Sopenharmony_ci * provide a driver specific default value. 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = nand_scan(chip, 1); 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci goto err_wp; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (gpiomtd->plat.adjust_parts) 37662306a36Sopenharmony_ci gpiomtd->plat.adjust_parts(&gpiomtd->plat, mtd->size); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = mtd_device_register(mtd, gpiomtd->plat.parts, 37962306a36Sopenharmony_ci gpiomtd->plat.num_parts); 38062306a36Sopenharmony_ci if (!ret) 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cierr_wp: 38462306a36Sopenharmony_ci if (gpiomtd->nwp && !IS_ERR(gpiomtd->nwp)) 38562306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nwp, 0); 38662306a36Sopenharmony_ciout_ce: 38762306a36Sopenharmony_ci if (gpiomtd->nce && !IS_ERR(gpiomtd->nce)) 38862306a36Sopenharmony_ci gpiod_set_value(gpiomtd->nce, 0); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic struct platform_driver gpio_nand_driver = { 39462306a36Sopenharmony_ci .probe = gpio_nand_probe, 39562306a36Sopenharmony_ci .remove_new = gpio_nand_remove, 39662306a36Sopenharmony_ci .driver = { 39762306a36Sopenharmony_ci .name = "gpio-nand", 39862306a36Sopenharmony_ci .of_match_table = of_match_ptr(gpio_nand_id_table), 39962306a36Sopenharmony_ci }, 40062306a36Sopenharmony_ci}; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cimodule_platform_driver(gpio_nand_driver); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 40562306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 40662306a36Sopenharmony_ciMODULE_DESCRIPTION("GPIO NAND Driver"); 407