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