1/* 2 * linux/drivers/mtd/onenand/generic.c 3 * 4 * Copyright (c) 2005 Samsung Electronics 5 * Kyungmin Park <kyungmin.park@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * Overview: 12 * This is a device driver for the OneNAND flash for generic boards. 13 */ 14 15#include <linux/module.h> 16#include <linux/init.h> 17#include <linux/slab.h> 18#include <linux/platform_device.h> 19#include <linux/mtd/mtd.h> 20#include <linux/mtd/onenand.h> 21#include <linux/mtd/partitions.h> 22#include <asm/io.h> 23 24/* 25 * Note: Driver name and platform data format have been updated! 26 * 27 * This version of the driver is named "onenand-flash" and takes struct 28 * onenand_platform_data as platform data. The old ARM-specific version 29 * with the name "onenand" used to take struct flash_platform_data. 30 */ 31#define DRIVER_NAME "onenand-flash" 32 33#ifdef CONFIG_MTD_PARTITIONS 34static const char *part_probes[] = { "cmdlinepart", NULL, }; 35#endif 36 37struct onenand_info { 38 struct mtd_info mtd; 39 struct mtd_partition *parts; 40 struct onenand_chip onenand; 41}; 42 43static int __devinit generic_onenand_probe(struct platform_device *pdev) 44{ 45 struct onenand_info *info; 46 struct onenand_platform_data *pdata = pdev->dev.platform_data; 47 struct resource *res = pdev->resource; 48 unsigned long size = resource_size(res); 49 int err; 50 51 info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); 52 if (!info) 53 return -ENOMEM; 54 55 if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { 56 err = -EBUSY; 57 goto out_free_info; 58 } 59 60 info->onenand.base = ioremap(res->start, size); 61 if (!info->onenand.base) { 62 err = -ENOMEM; 63 goto out_release_mem_region; 64 } 65 66 info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0; 67 info->onenand.irq = platform_get_irq(pdev, 0); 68 69 info->mtd.name = dev_name(&pdev->dev); 70 info->mtd.priv = &info->onenand; 71 info->mtd.owner = THIS_MODULE; 72 73 if (onenand_scan(&info->mtd, 1)) { 74 err = -ENXIO; 75 goto out_iounmap; 76 } 77 78#ifdef CONFIG_MTD_PARTITIONS 79 err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); 80 if (err > 0) 81 add_mtd_partitions(&info->mtd, info->parts, err); 82 else if (err <= 0 && pdata && pdata->parts) 83 add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); 84 else 85#endif 86 err = add_mtd_device(&info->mtd); 87 88 platform_set_drvdata(pdev, info); 89 90 return 0; 91 92out_iounmap: 93 iounmap(info->onenand.base); 94out_release_mem_region: 95 release_mem_region(res->start, size); 96out_free_info: 97 kfree(info); 98 99 return err; 100} 101 102static int __devexit generic_onenand_remove(struct platform_device *pdev) 103{ 104 struct onenand_info *info = platform_get_drvdata(pdev); 105 struct resource *res = pdev->resource; 106 unsigned long size = resource_size(res); 107 108 platform_set_drvdata(pdev, NULL); 109 110 if (info) { 111 if (info->parts) 112 del_mtd_partitions(&info->mtd); 113 else 114 del_mtd_device(&info->mtd); 115 116 onenand_release(&info->mtd); 117 release_mem_region(res->start, size); 118 iounmap(info->onenand.base); 119 kfree(info); 120 } 121 122 return 0; 123} 124 125static struct platform_driver generic_onenand_driver = { 126 .driver = { 127 .name = DRIVER_NAME, 128 .owner = THIS_MODULE, 129 }, 130 .probe = generic_onenand_probe, 131 .remove = __devexit_p(generic_onenand_remove), 132}; 133 134MODULE_ALIAS(DRIVER_NAME); 135 136static int __init generic_onenand_init(void) 137{ 138 return platform_driver_register(&generic_onenand_driver); 139} 140 141static void __exit generic_onenand_exit(void) 142{ 143 platform_driver_unregister(&generic_onenand_driver); 144} 145 146module_init(generic_onenand_init); 147module_exit(generic_onenand_exit); 148 149MODULE_LICENSE("GPL"); 150MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); 151MODULE_DESCRIPTION("Glue layer for OneNAND flash on generic boards"); 152