1*d2912cb1SThomas 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 { 17711fdf62SVitaly Wool struct nand_chip chip; 18711fdf62SVitaly Wool void __iomem *io_base; 19711fdf62SVitaly Wool }; 20711fdf62SVitaly Wool 21711fdf62SVitaly Wool /* 22711fdf62SVitaly Wool * Probe for the NAND device. 23711fdf62SVitaly Wool */ 2406f25510SBill Pemberton static int plat_nand_probe(struct platform_device *pdev) 25711fdf62SVitaly Wool { 26453810b7SJingoo Han struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 27711fdf62SVitaly Wool struct plat_nand_data *data; 28a0260d21SBoris BREZILLON struct mtd_info *mtd; 292d098a72SH Hartley Sweeten struct resource *res; 30f2e5a244SH Hartley Sweeten const char **part_types; 312d098a72SH Hartley Sweeten int err = 0; 322d098a72SH Hartley Sweeten 33da3888cbSJohn Crispin if (!pdata) { 34da3888cbSJohn Crispin dev_err(&pdev->dev, "platform_nand_data is missing\n"); 35da3888cbSJohn Crispin return -EINVAL; 36da3888cbSJohn Crispin } 37da3888cbSJohn Crispin 3801cd2abaSMarek Vasut if (pdata->chip.nr_chips < 1) { 3901cd2abaSMarek Vasut dev_err(&pdev->dev, "invalid number of chips specified\n"); 4001cd2abaSMarek Vasut return -EINVAL; 4101cd2abaSMarek Vasut } 4201cd2abaSMarek Vasut 43711fdf62SVitaly Wool /* Allocate memory for the device structure (and zero it) */ 44ffdac7cdSJingoo Han data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), 45ffdac7cdSJingoo Han GFP_KERNEL); 46a01eb204SJingoo Han if (!data) 47711fdf62SVitaly Wool return -ENOMEM; 48711fdf62SVitaly Wool 49840f53c3SWei Yongjun res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 50ffdac7cdSJingoo Han data->io_base = devm_ioremap_resource(&pdev->dev, res); 51ffdac7cdSJingoo Han if (IS_ERR(data->io_base)) 52ffdac7cdSJingoo Han return PTR_ERR(data->io_base); 53711fdf62SVitaly Wool 54a61ae81aSBrian Norris nand_set_flash_node(&data->chip, pdev->dev.of_node); 55a0260d21SBoris BREZILLON mtd = nand_to_mtd(&data->chip); 56a0260d21SBoris BREZILLON mtd->dev.parent = &pdev->dev; 57711fdf62SVitaly Wool 5882fc5099SBoris Brezillon data->chip.legacy.IO_ADDR_R = data->io_base; 5982fc5099SBoris Brezillon data->chip.legacy.IO_ADDR_W = data->io_base; 60bf6065c6SBoris Brezillon data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; 618395b753SBoris Brezillon data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; 627d6c37e9SBoris Brezillon data->chip.legacy.select_chip = pdata->ctrl.select_chip; 63716bbbabSBoris Brezillon data->chip.legacy.write_buf = pdata->ctrl.write_buf; 64716bbbabSBoris Brezillon data->chip.legacy.read_buf = pdata->ctrl.read_buf; 653cece3abSBoris Brezillon data->chip.legacy.chip_delay = pdata->chip.chip_delay; 66711fdf62SVitaly Wool data->chip.options |= pdata->chip.options; 67a40f7341SBrian Norris data->chip.bbt_options |= pdata->chip.bbt_options; 68711fdf62SVitaly Wool 69711fdf62SVitaly Wool data->chip.ecc.mode = NAND_ECC_SOFT; 7041ccb49eSRafał Miłecki data->chip.ecc.algo = NAND_ECC_HAMMING; 71711fdf62SVitaly Wool 72711fdf62SVitaly Wool platform_set_drvdata(pdev, data); 73711fdf62SVitaly Wool 74bf95efd4SH Hartley Sweeten /* Handle any platform specific setup */ 75bf95efd4SH Hartley Sweeten if (pdata->ctrl.probe) { 762d098a72SH Hartley Sweeten err = pdata->ctrl.probe(pdev); 772d098a72SH Hartley Sweeten if (err) 78bf95efd4SH Hartley Sweeten goto out; 79bf95efd4SH Hartley Sweeten } 80bf95efd4SH Hartley Sweeten 8125985edcSLucas De Marchi /* Scan to find existence of the device */ 8200ad378fSBoris Brezillon err = nand_scan(&data->chip, pdata->chip.nr_chips); 83ce2eaca7SMasahiro Yamada if (err) 84711fdf62SVitaly Wool goto out; 85711fdf62SVitaly Wool 868bf57b0dSBrian Norris part_types = pdata->chip.part_probe_types; 87f2e5a244SH Hartley Sweeten 88a0260d21SBoris BREZILLON err = mtd_device_parse_register(mtd, part_types, NULL, 8942d7fbe2SArtem Bityutskiy pdata->chip.partitions, 9042d7fbe2SArtem Bityutskiy pdata->chip.nr_partitions); 91711fdf62SVitaly Wool 922d098a72SH Hartley Sweeten if (!err) 932d098a72SH Hartley Sweeten return err; 94711fdf62SVitaly Wool 9559ac276fSBoris Brezillon nand_release(&data->chip); 96711fdf62SVitaly Wool out: 97bf95efd4SH Hartley Sweeten if (pdata->ctrl.remove) 98bf95efd4SH Hartley Sweeten pdata->ctrl.remove(pdev); 992d098a72SH Hartley Sweeten return err; 100711fdf62SVitaly Wool } 101711fdf62SVitaly Wool 102711fdf62SVitaly Wool /* 103711fdf62SVitaly Wool * Remove a NAND device. 104711fdf62SVitaly Wool */ 105810b7e06SBill Pemberton static int plat_nand_remove(struct platform_device *pdev) 106711fdf62SVitaly Wool { 107711fdf62SVitaly Wool struct plat_nand_data *data = platform_get_drvdata(pdev); 108453810b7SJingoo Han struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 109711fdf62SVitaly Wool 11059ac276fSBoris Brezillon nand_release(&data->chip); 111bf95efd4SH Hartley Sweeten if (pdata->ctrl.remove) 112bf95efd4SH Hartley Sweeten pdata->ctrl.remove(pdev); 113711fdf62SVitaly Wool 114711fdf62SVitaly Wool return 0; 115711fdf62SVitaly Wool } 116711fdf62SVitaly Wool 117a4f20351SJohn Crispin static const struct of_device_id plat_nand_match[] = { 118a4f20351SJohn Crispin { .compatible = "gen_nand" }, 119a4f20351SJohn Crispin {}, 120a4f20351SJohn Crispin }; 121a4f20351SJohn Crispin MODULE_DEVICE_TABLE(of, plat_nand_match); 122a4f20351SJohn Crispin 123711fdf62SVitaly Wool static struct platform_driver plat_nand_driver = { 124711fdf62SVitaly Wool .probe = plat_nand_probe, 1255153b88cSBill Pemberton .remove = plat_nand_remove, 126711fdf62SVitaly Wool .driver = { 127711fdf62SVitaly Wool .name = "gen_nand", 128a4f20351SJohn Crispin .of_match_table = plat_nand_match, 129711fdf62SVitaly Wool }, 130711fdf62SVitaly Wool }; 131711fdf62SVitaly Wool 132f99640deSAxel Lin module_platform_driver(plat_nand_driver); 133711fdf62SVitaly Wool 134711fdf62SVitaly Wool MODULE_LICENSE("GPL"); 135711fdf62SVitaly Wool MODULE_AUTHOR("Vitaly Wool"); 136711fdf62SVitaly Wool MODULE_DESCRIPTION("Simple generic NAND driver"); 1371ff18422SKay Sievers MODULE_ALIAS("platform:gen_nand"); 138