1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2711fdf62SVitaly Wool /* 3711fdf62SVitaly Wool * Generic NAND driver 4711fdf62SVitaly Wool * 5711fdf62SVitaly Wool * Author: Vitaly Wool <vitalywool@gmail.com> 6711fdf62SVitaly Wool */ 7711fdf62SVitaly Wool 8ffdac7cdSJingoo Han #include <linux/err.h> 9711fdf62SVitaly Wool #include <linux/io.h> 10711fdf62SVitaly Wool #include <linux/module.h> 11711fdf62SVitaly Wool #include <linux/platform_device.h> 12711fdf62SVitaly Wool #include <linux/slab.h> 13711fdf62SVitaly Wool #include <linux/mtd/mtd.h> 14c7921bb3SBoris Brezillon #include <linux/mtd/platnand.h> 15711fdf62SVitaly Wool 16711fdf62SVitaly Wool struct plat_nand_data { 17612e048eSMiquel Raynal struct nand_controller controller; 18711fdf62SVitaly Wool struct nand_chip chip; 19711fdf62SVitaly Wool void __iomem *io_base; 20711fdf62SVitaly Wool }; 21711fdf62SVitaly Wool 22612e048eSMiquel Raynal static int plat_nand_attach_chip(struct nand_chip *chip) 23612e048eSMiquel Raynal { 24612e048eSMiquel Raynal chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 25*148b4f16SMiquel Raynal 26*148b4f16SMiquel Raynal if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) 27612e048eSMiquel Raynal chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 28612e048eSMiquel Raynal 29612e048eSMiquel Raynal return 0; 30612e048eSMiquel Raynal } 31612e048eSMiquel Raynal 32612e048eSMiquel Raynal static const struct nand_controller_ops plat_nand_ops = { 33612e048eSMiquel Raynal .attach_chip = plat_nand_attach_chip, 34612e048eSMiquel Raynal }; 35612e048eSMiquel Raynal 36711fdf62SVitaly Wool /* 37711fdf62SVitaly Wool * Probe for the NAND device. 38711fdf62SVitaly Wool */ 3906f25510SBill Pemberton static int plat_nand_probe(struct platform_device *pdev) 40711fdf62SVitaly Wool { 41453810b7SJingoo Han struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 42711fdf62SVitaly Wool struct plat_nand_data *data; 43a0260d21SBoris BREZILLON struct mtd_info *mtd; 442d098a72SH Hartley Sweeten struct resource *res; 45f2e5a244SH Hartley Sweeten const char **part_types; 462d098a72SH Hartley Sweeten int err = 0; 472d098a72SH Hartley Sweeten 48da3888cbSJohn Crispin if (!pdata) { 49da3888cbSJohn Crispin dev_err(&pdev->dev, "platform_nand_data is missing\n"); 50da3888cbSJohn Crispin return -EINVAL; 51da3888cbSJohn Crispin } 52da3888cbSJohn Crispin 5301cd2abaSMarek Vasut if (pdata->chip.nr_chips < 1) { 5401cd2abaSMarek Vasut dev_err(&pdev->dev, "invalid number of chips specified\n"); 5501cd2abaSMarek Vasut return -EINVAL; 5601cd2abaSMarek Vasut } 5701cd2abaSMarek Vasut 58711fdf62SVitaly Wool /* Allocate memory for the device structure (and zero it) */ 59ffdac7cdSJingoo Han data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), 60ffdac7cdSJingoo Han GFP_KERNEL); 61a01eb204SJingoo Han if (!data) 62711fdf62SVitaly Wool return -ENOMEM; 63711fdf62SVitaly Wool 64612e048eSMiquel Raynal data->controller.ops = &plat_nand_ops; 65612e048eSMiquel Raynal nand_controller_init(&data->controller); 66612e048eSMiquel Raynal data->chip.controller = &data->controller; 67612e048eSMiquel Raynal 68840f53c3SWei Yongjun res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 69ffdac7cdSJingoo Han data->io_base = devm_ioremap_resource(&pdev->dev, res); 70ffdac7cdSJingoo Han if (IS_ERR(data->io_base)) 71ffdac7cdSJingoo Han return PTR_ERR(data->io_base); 72711fdf62SVitaly Wool 73a61ae81aSBrian Norris nand_set_flash_node(&data->chip, pdev->dev.of_node); 74a0260d21SBoris BREZILLON mtd = nand_to_mtd(&data->chip); 75a0260d21SBoris BREZILLON mtd->dev.parent = &pdev->dev; 76711fdf62SVitaly Wool 7782fc5099SBoris Brezillon data->chip.legacy.IO_ADDR_R = data->io_base; 7882fc5099SBoris Brezillon data->chip.legacy.IO_ADDR_W = data->io_base; 79bf6065c6SBoris Brezillon data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; 808395b753SBoris Brezillon data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; 817d6c37e9SBoris Brezillon data->chip.legacy.select_chip = pdata->ctrl.select_chip; 82716bbbabSBoris Brezillon data->chip.legacy.write_buf = pdata->ctrl.write_buf; 83716bbbabSBoris Brezillon data->chip.legacy.read_buf = pdata->ctrl.read_buf; 843cece3abSBoris Brezillon data->chip.legacy.chip_delay = pdata->chip.chip_delay; 85711fdf62SVitaly Wool data->chip.options |= pdata->chip.options; 86a40f7341SBrian Norris data->chip.bbt_options |= pdata->chip.bbt_options; 87711fdf62SVitaly Wool 88711fdf62SVitaly Wool platform_set_drvdata(pdev, data); 89711fdf62SVitaly Wool 90bf95efd4SH Hartley Sweeten /* Handle any platform specific setup */ 91bf95efd4SH Hartley Sweeten if (pdata->ctrl.probe) { 922d098a72SH Hartley Sweeten err = pdata->ctrl.probe(pdev); 932d098a72SH Hartley Sweeten if (err) 94bf95efd4SH Hartley Sweeten goto out; 95bf95efd4SH Hartley Sweeten } 96bf95efd4SH Hartley Sweeten 9725985edcSLucas De Marchi /* Scan to find existence of the device */ 9800ad378fSBoris Brezillon err = nand_scan(&data->chip, pdata->chip.nr_chips); 99ce2eaca7SMasahiro Yamada if (err) 100711fdf62SVitaly Wool goto out; 101711fdf62SVitaly Wool 1028bf57b0dSBrian Norris part_types = pdata->chip.part_probe_types; 103f2e5a244SH Hartley Sweeten 104a0260d21SBoris BREZILLON err = mtd_device_parse_register(mtd, part_types, NULL, 10542d7fbe2SArtem Bityutskiy pdata->chip.partitions, 10642d7fbe2SArtem Bityutskiy pdata->chip.nr_partitions); 107711fdf62SVitaly Wool 1082d098a72SH Hartley Sweeten if (!err) 1092d098a72SH Hartley Sweeten return err; 110711fdf62SVitaly Wool 1115284024bSMiquel Raynal nand_cleanup(&data->chip); 112711fdf62SVitaly Wool out: 113bf95efd4SH Hartley Sweeten if (pdata->ctrl.remove) 114bf95efd4SH Hartley Sweeten pdata->ctrl.remove(pdev); 1152d098a72SH Hartley Sweeten return err; 116711fdf62SVitaly Wool } 117711fdf62SVitaly Wool 118711fdf62SVitaly Wool /* 119711fdf62SVitaly Wool * Remove a NAND device. 120711fdf62SVitaly Wool */ 121810b7e06SBill Pemberton static int plat_nand_remove(struct platform_device *pdev) 122711fdf62SVitaly Wool { 123711fdf62SVitaly Wool struct plat_nand_data *data = platform_get_drvdata(pdev); 124453810b7SJingoo Han struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 125d1aae005SMiquel Raynal struct nand_chip *chip = &data->chip; 126d1aae005SMiquel Raynal int ret; 127711fdf62SVitaly Wool 128d1aae005SMiquel Raynal ret = mtd_device_unregister(nand_to_mtd(chip)); 129d1aae005SMiquel Raynal WARN_ON(ret); 130d1aae005SMiquel Raynal nand_cleanup(chip); 131bf95efd4SH Hartley Sweeten if (pdata->ctrl.remove) 132bf95efd4SH Hartley Sweeten pdata->ctrl.remove(pdev); 133711fdf62SVitaly Wool 134711fdf62SVitaly Wool return 0; 135711fdf62SVitaly Wool } 136711fdf62SVitaly Wool 137a4f20351SJohn Crispin static const struct of_device_id plat_nand_match[] = { 138a4f20351SJohn Crispin { .compatible = "gen_nand" }, 139a4f20351SJohn Crispin {}, 140a4f20351SJohn Crispin }; 141a4f20351SJohn Crispin MODULE_DEVICE_TABLE(of, plat_nand_match); 142a4f20351SJohn Crispin 143711fdf62SVitaly Wool static struct platform_driver plat_nand_driver = { 144711fdf62SVitaly Wool .probe = plat_nand_probe, 1455153b88cSBill Pemberton .remove = plat_nand_remove, 146711fdf62SVitaly Wool .driver = { 147711fdf62SVitaly Wool .name = "gen_nand", 148a4f20351SJohn Crispin .of_match_table = plat_nand_match, 149711fdf62SVitaly Wool }, 150711fdf62SVitaly Wool }; 151711fdf62SVitaly Wool 152f99640deSAxel Lin module_platform_driver(plat_nand_driver); 153711fdf62SVitaly Wool 154711fdf62SVitaly Wool MODULE_LICENSE("GPL"); 155711fdf62SVitaly Wool MODULE_AUTHOR("Vitaly Wool"); 156711fdf62SVitaly Wool MODULE_DESCRIPTION("Simple generic NAND driver"); 1571ff18422SKay Sievers MODULE_ALIAS("platform:gen_nand"); 158