1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Generic NAND driver 4 * 5 * Author: Vitaly Wool <vitalywool@gmail.com> 6 */ 7 8 #include <linux/err.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 #include <linux/mtd/mtd.h> 14 #include <linux/mtd/platnand.h> 15 16 struct plat_nand_data { 17 struct nand_controller controller; 18 struct nand_chip chip; 19 void __iomem *io_base; 20 }; 21 22 static int plat_nand_attach_chip(struct nand_chip *chip) 23 { 24 chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 25 chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 26 27 return 0; 28 } 29 30 static const struct nand_controller_ops plat_nand_ops = { 31 .attach_chip = plat_nand_attach_chip, 32 }; 33 34 /* 35 * Probe for the NAND device. 36 */ 37 static int plat_nand_probe(struct platform_device *pdev) 38 { 39 struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 40 struct plat_nand_data *data; 41 struct mtd_info *mtd; 42 struct resource *res; 43 const char **part_types; 44 int err = 0; 45 46 if (!pdata) { 47 dev_err(&pdev->dev, "platform_nand_data is missing\n"); 48 return -EINVAL; 49 } 50 51 if (pdata->chip.nr_chips < 1) { 52 dev_err(&pdev->dev, "invalid number of chips specified\n"); 53 return -EINVAL; 54 } 55 56 /* Allocate memory for the device structure (and zero it) */ 57 data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), 58 GFP_KERNEL); 59 if (!data) 60 return -ENOMEM; 61 62 data->controller.ops = &plat_nand_ops; 63 nand_controller_init(&data->controller); 64 data->chip.controller = &data->controller; 65 66 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 67 data->io_base = devm_ioremap_resource(&pdev->dev, res); 68 if (IS_ERR(data->io_base)) 69 return PTR_ERR(data->io_base); 70 71 nand_set_flash_node(&data->chip, pdev->dev.of_node); 72 mtd = nand_to_mtd(&data->chip); 73 mtd->dev.parent = &pdev->dev; 74 75 data->chip.legacy.IO_ADDR_R = data->io_base; 76 data->chip.legacy.IO_ADDR_W = data->io_base; 77 data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; 78 data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; 79 data->chip.legacy.select_chip = pdata->ctrl.select_chip; 80 data->chip.legacy.write_buf = pdata->ctrl.write_buf; 81 data->chip.legacy.read_buf = pdata->ctrl.read_buf; 82 data->chip.legacy.chip_delay = pdata->chip.chip_delay; 83 data->chip.options |= pdata->chip.options; 84 data->chip.bbt_options |= pdata->chip.bbt_options; 85 86 platform_set_drvdata(pdev, data); 87 88 /* Handle any platform specific setup */ 89 if (pdata->ctrl.probe) { 90 err = pdata->ctrl.probe(pdev); 91 if (err) 92 goto out; 93 } 94 95 /* Scan to find existence of the device */ 96 err = nand_scan(&data->chip, pdata->chip.nr_chips); 97 if (err) 98 goto out; 99 100 part_types = pdata->chip.part_probe_types; 101 102 err = mtd_device_parse_register(mtd, part_types, NULL, 103 pdata->chip.partitions, 104 pdata->chip.nr_partitions); 105 106 if (!err) 107 return err; 108 109 nand_cleanup(&data->chip); 110 out: 111 if (pdata->ctrl.remove) 112 pdata->ctrl.remove(pdev); 113 return err; 114 } 115 116 /* 117 * Remove a NAND device. 118 */ 119 static int plat_nand_remove(struct platform_device *pdev) 120 { 121 struct plat_nand_data *data = platform_get_drvdata(pdev); 122 struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 123 struct nand_chip *chip = &data->chip; 124 int ret; 125 126 ret = mtd_device_unregister(nand_to_mtd(chip)); 127 WARN_ON(ret); 128 nand_cleanup(chip); 129 if (pdata->ctrl.remove) 130 pdata->ctrl.remove(pdev); 131 132 return 0; 133 } 134 135 static const struct of_device_id plat_nand_match[] = { 136 { .compatible = "gen_nand" }, 137 {}, 138 }; 139 MODULE_DEVICE_TABLE(of, plat_nand_match); 140 141 static struct platform_driver plat_nand_driver = { 142 .probe = plat_nand_probe, 143 .remove = plat_nand_remove, 144 .driver = { 145 .name = "gen_nand", 146 .of_match_table = plat_nand_match, 147 }, 148 }; 149 150 module_platform_driver(plat_nand_driver); 151 152 MODULE_LICENSE("GPL"); 153 MODULE_AUTHOR("Vitaly Wool"); 154 MODULE_DESCRIPTION("Simple generic NAND driver"); 155 MODULE_ALIAS("platform:gen_nand"); 156