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_chip chip; 18 void __iomem *io_base; 19 }; 20 21 /* 22 * Probe for the NAND device. 23 */ 24 static int plat_nand_probe(struct platform_device *pdev) 25 { 26 struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 27 struct plat_nand_data *data; 28 struct mtd_info *mtd; 29 struct resource *res; 30 const char **part_types; 31 int err = 0; 32 33 if (!pdata) { 34 dev_err(&pdev->dev, "platform_nand_data is missing\n"); 35 return -EINVAL; 36 } 37 38 if (pdata->chip.nr_chips < 1) { 39 dev_err(&pdev->dev, "invalid number of chips specified\n"); 40 return -EINVAL; 41 } 42 43 /* Allocate memory for the device structure (and zero it) */ 44 data = devm_kzalloc(&pdev->dev, sizeof(struct plat_nand_data), 45 GFP_KERNEL); 46 if (!data) 47 return -ENOMEM; 48 49 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 50 data->io_base = devm_ioremap_resource(&pdev->dev, res); 51 if (IS_ERR(data->io_base)) 52 return PTR_ERR(data->io_base); 53 54 nand_set_flash_node(&data->chip, pdev->dev.of_node); 55 mtd = nand_to_mtd(&data->chip); 56 mtd->dev.parent = &pdev->dev; 57 58 data->chip.legacy.IO_ADDR_R = data->io_base; 59 data->chip.legacy.IO_ADDR_W = data->io_base; 60 data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl; 61 data->chip.legacy.dev_ready = pdata->ctrl.dev_ready; 62 data->chip.legacy.select_chip = pdata->ctrl.select_chip; 63 data->chip.legacy.write_buf = pdata->ctrl.write_buf; 64 data->chip.legacy.read_buf = pdata->ctrl.read_buf; 65 data->chip.legacy.chip_delay = pdata->chip.chip_delay; 66 data->chip.options |= pdata->chip.options; 67 data->chip.bbt_options |= pdata->chip.bbt_options; 68 69 data->chip.ecc.mode = NAND_ECC_SOFT; 70 data->chip.ecc.algo = NAND_ECC_HAMMING; 71 72 platform_set_drvdata(pdev, data); 73 74 /* Handle any platform specific setup */ 75 if (pdata->ctrl.probe) { 76 err = pdata->ctrl.probe(pdev); 77 if (err) 78 goto out; 79 } 80 81 /* Scan to find existence of the device */ 82 err = nand_scan(&data->chip, pdata->chip.nr_chips); 83 if (err) 84 goto out; 85 86 part_types = pdata->chip.part_probe_types; 87 88 err = mtd_device_parse_register(mtd, part_types, NULL, 89 pdata->chip.partitions, 90 pdata->chip.nr_partitions); 91 92 if (!err) 93 return err; 94 95 nand_release(&data->chip); 96 out: 97 if (pdata->ctrl.remove) 98 pdata->ctrl.remove(pdev); 99 return err; 100 } 101 102 /* 103 * Remove a NAND device. 104 */ 105 static int plat_nand_remove(struct platform_device *pdev) 106 { 107 struct plat_nand_data *data = platform_get_drvdata(pdev); 108 struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); 109 110 nand_release(&data->chip); 111 if (pdata->ctrl.remove) 112 pdata->ctrl.remove(pdev); 113 114 return 0; 115 } 116 117 static const struct of_device_id plat_nand_match[] = { 118 { .compatible = "gen_nand" }, 119 {}, 120 }; 121 MODULE_DEVICE_TABLE(of, plat_nand_match); 122 123 static struct platform_driver plat_nand_driver = { 124 .probe = plat_nand_probe, 125 .remove = plat_nand_remove, 126 .driver = { 127 .name = "gen_nand", 128 .of_match_table = plat_nand_match, 129 }, 130 }; 131 132 module_platform_driver(plat_nand_driver); 133 134 MODULE_LICENSE("GPL"); 135 MODULE_AUTHOR("Vitaly Wool"); 136 MODULE_DESCRIPTION("Simple generic NAND driver"); 137 MODULE_ALIAS("platform:gen_nand"); 138