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/io.h> 13#include <linux/module.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16#include <linux/mtd/mtd.h> 17#include <linux/mtd/nand.h> 18#include <linux/mtd/partitions.h> 19 20struct plat_nand_data { 21 struct nand_chip chip; 22 struct mtd_info mtd; 23 void __iomem *io_base; 24#ifdef CONFIG_MTD_PARTITIONS 25 int nr_parts; 26 struct mtd_partition *parts; 27#endif 28}; 29 30/* 31 * Probe for the NAND device. 32 */ 33static int __init plat_nand_probe(struct platform_device *pdev) 34{ 35 struct platform_nand_data *pdata = pdev->dev.platform_data; 36 struct plat_nand_data *data; 37 int res = 0; 38 39 /* Allocate memory for the device structure (and zero it) */ 40 data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL); 41 if (!data) { 42 dev_err(&pdev->dev, "failed to allocate device structure.\n"); 43 return -ENOMEM; 44 } 45 46 data->io_base = ioremap(pdev->resource[0].start, 47 pdev->resource[0].end - pdev->resource[0].start + 1); 48 if (data->io_base == NULL) { 49 dev_err(&pdev->dev, "ioremap failed\n"); 50 kfree(data); 51 return -EIO; 52 } 53 54 data->chip.priv = &data; 55 data->mtd.priv = &data->chip; 56 data->mtd.owner = THIS_MODULE; 57 58 data->chip.IO_ADDR_R = data->io_base; 59 data->chip.IO_ADDR_W = data->io_base; 60 data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; 61 data->chip.dev_ready = pdata->ctrl.dev_ready; 62 data->chip.select_chip = pdata->ctrl.select_chip; 63 data->chip.chip_delay = pdata->chip.chip_delay; 64 data->chip.options |= pdata->chip.options; 65 66 data->chip.ecc.hwctl = pdata->ctrl.hwcontrol; 67 data->chip.ecc.layout = pdata->chip.ecclayout; 68 data->chip.ecc.mode = NAND_ECC_SOFT; 69 70 platform_set_drvdata(pdev, data); 71 72 /* Scan to find existance of the device */ 73 if (nand_scan(&data->mtd, 1)) { 74 res = -ENXIO; 75 goto out; 76 } 77 78#ifdef CONFIG_MTD_PARTITIONS 79 if (pdata->chip.part_probe_types) { 80 res = parse_mtd_partitions(&data->mtd, 81 pdata->chip.part_probe_types, 82 &data->parts, 0); 83 if (res > 0) { 84 add_mtd_partitions(&data->mtd, data->parts, res); 85 return 0; 86 } 87 } 88 if (pdata->chip.partitions) { 89 data->parts = pdata->chip.partitions; 90 res = add_mtd_partitions(&data->mtd, data->parts, 91 pdata->chip.nr_partitions); 92 } else 93#endif 94 res = add_mtd_device(&data->mtd); 95 96 if (!res) 97 return res; 98 99 nand_release(&data->mtd); 100out: 101 platform_set_drvdata(pdev, NULL); 102 iounmap(data->io_base); 103 kfree(data); 104 return res; 105} 106 107/* 108 * Remove a NAND device. 109 */ 110static int __devexit plat_nand_remove(struct platform_device *pdev) 111{ 112 struct plat_nand_data *data = platform_get_drvdata(pdev); 113 struct platform_nand_data *pdata = pdev->dev.platform_data; 114 115 nand_release(&data->mtd); 116#ifdef CONFIG_MTD_PARTITIONS 117 if (data->parts && data->parts != pdata->chip.partitions) 118 kfree(data->parts); 119#endif 120 iounmap(data->io_base); 121 kfree(data); 122 123 return 0; 124} 125 126static struct platform_driver plat_nand_driver = { 127 .probe = plat_nand_probe, 128 .remove = plat_nand_remove, 129 .driver = { 130 .name = "gen_nand", 131 .owner = THIS_MODULE, 132 }, 133}; 134 135static int __init plat_nand_init(void) 136{ 137 return platform_driver_register(&plat_nand_driver); 138} 139 140static void __exit plat_nand_exit(void) 141{ 142 platform_driver_unregister(&plat_nand_driver); 143} 144 145module_init(plat_nand_init); 146module_exit(plat_nand_exit); 147 148MODULE_LICENSE("GPL"); 149MODULE_AUTHOR("Vitaly Wool"); 150MODULE_DESCRIPTION("Simple generic NAND driver"); 151