1/*====================================================================== 2 3 drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver 4 5 Copyright (C) 2000 ARM Limited 6 Copyright (C) 2003 Deep Blue Solutions Ltd. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22 This is access code for flashes using ARM's flash partitioning 23 standards. 24 25======================================================================*/ 26 27#include <linux/module.h> 28#include <linux/types.h> 29#include <linux/kernel.h> 30#include <linux/slab.h> 31#include <linux/ioport.h> 32#include <linux/platform_device.h> 33#include <linux/init.h> 34#include <linux/io.h> 35 36#include <linux/mtd/mtd.h> 37#include <linux/mtd/map.h> 38#include <linux/mtd/partitions.h> 39#include <linux/mtd/concat.h> 40 41#include <asm/mach/flash.h> 42#include <mach/hardware.h> 43#include <asm/system.h> 44 45struct armflash_subdev_info { 46 char *name; 47 struct mtd_info *mtd; 48 struct map_info map; 49 struct flash_platform_data *plat; 50}; 51 52struct armflash_info { 53 struct resource *res; 54 struct mtd_partition *parts; 55 struct mtd_info *mtd; 56 int nr_subdev; 57 struct armflash_subdev_info subdev[0]; 58}; 59 60static void armflash_set_vpp(struct map_info *map, int on) 61{ 62 struct armflash_subdev_info *info = 63 container_of(map, struct armflash_subdev_info, map); 64 65 if (info->plat && info->plat->set_vpp) 66 info->plat->set_vpp(on); 67} 68 69static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; 70 71static int armflash_subdev_probe(struct armflash_subdev_info *subdev, 72 struct resource *res) 73{ 74 struct flash_platform_data *plat = subdev->plat; 75 resource_size_t size = res->end - res->start + 1; 76 void __iomem *base; 77 int err = 0; 78 79 if (!request_mem_region(res->start, size, subdev->name)) { 80 err = -EBUSY; 81 goto out; 82 } 83 84 base = ioremap(res->start, size); 85 if (!base) { 86 err = -ENOMEM; 87 goto no_mem; 88 } 89 90 /* 91 * look for CFI based flash parts fitted to this board 92 */ 93 subdev->map.size = size; 94 subdev->map.bankwidth = plat->width; 95 subdev->map.phys = res->start; 96 subdev->map.virt = base; 97 subdev->map.name = subdev->name; 98 subdev->map.set_vpp = armflash_set_vpp; 99 100 simple_map_init(&subdev->map); 101 102 /* 103 * Also, the CFI layer automatically works out what size 104 * of chips we have, and does the necessary identification 105 * for us automatically. 106 */ 107 subdev->mtd = do_map_probe(plat->map_name, &subdev->map); 108 if (!subdev->mtd) { 109 err = -ENXIO; 110 goto no_device; 111 } 112 113 subdev->mtd->owner = THIS_MODULE; 114 115 /* Successful? */ 116 if (err == 0) 117 return err; 118 119 if (subdev->mtd) 120 map_destroy(subdev->mtd); 121 no_device: 122 iounmap(base); 123 no_mem: 124 release_mem_region(res->start, size); 125 out: 126 return err; 127} 128 129static void armflash_subdev_remove(struct armflash_subdev_info *subdev) 130{ 131 if (subdev->mtd) 132 map_destroy(subdev->mtd); 133 if (subdev->map.virt) 134 iounmap(subdev->map.virt); 135 kfree(subdev->name); 136 subdev->name = NULL; 137 release_mem_region(subdev->map.phys, subdev->map.size); 138} 139 140static int armflash_probe(struct platform_device *dev) 141{ 142 struct flash_platform_data *plat = dev->dev.platform_data; 143 unsigned int size; 144 struct armflash_info *info; 145 int i, nr, err; 146 147 /* Count the number of devices */ 148 for (nr = 0; ; nr++) 149 if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) 150 break; 151 if (nr == 0) { 152 err = -ENODEV; 153 goto out; 154 } 155 156 size = sizeof(struct armflash_info) + 157 sizeof(struct armflash_subdev_info) * nr; 158 info = kzalloc(size, GFP_KERNEL); 159 if (!info) { 160 err = -ENOMEM; 161 goto out; 162 } 163 164 if (plat && plat->init) { 165 err = plat->init(); 166 if (err) 167 goto no_resource; 168 } 169 170 for (i = 0; i < nr; i++) { 171 struct armflash_subdev_info *subdev = &info->subdev[i]; 172 struct resource *res; 173 174 res = platform_get_resource(dev, IORESOURCE_MEM, i); 175 if (!res) 176 break; 177 178 if (nr == 1) 179 /* No MTD concatenation, just use the default name */ 180 subdev->name = kstrdup(dev_name(&dev->dev), GFP_KERNEL); 181 else 182 subdev->name = kasprintf(GFP_KERNEL, "%s-%d", 183 dev_name(&dev->dev), i); 184 if (!subdev->name) { 185 err = -ENOMEM; 186 break; 187 } 188 subdev->plat = plat; 189 190 err = armflash_subdev_probe(subdev, res); 191 if (err) { 192 kfree(subdev->name); 193 subdev->name = NULL; 194 break; 195 } 196 } 197 info->nr_subdev = i; 198 199 if (err) 200 goto subdev_err; 201 202 if (info->nr_subdev == 1) 203 info->mtd = info->subdev[0].mtd; 204 else if (info->nr_subdev > 1) { 205#ifdef CONFIG_MTD_CONCAT 206 struct mtd_info *cdev[info->nr_subdev]; 207 208 /* 209 * We detected multiple devices. Concatenate them together. 210 */ 211 for (i = 0; i < info->nr_subdev; i++) 212 cdev[i] = info->subdev[i].mtd; 213 214 info->mtd = mtd_concat_create(cdev, info->nr_subdev, 215 dev_name(&dev->dev)); 216 if (info->mtd == NULL) 217 err = -ENXIO; 218#else 219 printk(KERN_ERR "armflash: multiple devices found but " 220 "MTD concat support disabled.\n"); 221 err = -ENXIO; 222#endif 223 } 224 225 if (err < 0) 226 goto cleanup; 227 228 err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); 229 if (err > 0) { 230 err = add_mtd_partitions(info->mtd, info->parts, err); 231 if (err) 232 printk(KERN_ERR 233 "mtd partition registration failed: %d\n", err); 234 } 235 236 if (err == 0) { 237 platform_set_drvdata(dev, info); 238 return err; 239 } 240 241 /* 242 * We got an error, free all resources. 243 */ 244 cleanup: 245 if (info->mtd) { 246 del_mtd_partitions(info->mtd); 247#ifdef CONFIG_MTD_CONCAT 248 if (info->mtd != info->subdev[0].mtd) 249 mtd_concat_destroy(info->mtd); 250#endif 251 } 252 kfree(info->parts); 253 subdev_err: 254 for (i = info->nr_subdev - 1; i >= 0; i--) 255 armflash_subdev_remove(&info->subdev[i]); 256 no_resource: 257 if (plat && plat->exit) 258 plat->exit(); 259 kfree(info); 260 out: 261 return err; 262} 263 264static int armflash_remove(struct platform_device *dev) 265{ 266 struct armflash_info *info = platform_get_drvdata(dev); 267 struct flash_platform_data *plat = dev->dev.platform_data; 268 int i; 269 270 platform_set_drvdata(dev, NULL); 271 272 if (info) { 273 if (info->mtd) { 274 del_mtd_partitions(info->mtd); 275#ifdef CONFIG_MTD_CONCAT 276 if (info->mtd != info->subdev[0].mtd) 277 mtd_concat_destroy(info->mtd); 278#endif 279 } 280 kfree(info->parts); 281 282 for (i = info->nr_subdev - 1; i >= 0; i--) 283 armflash_subdev_remove(&info->subdev[i]); 284 285 if (plat && plat->exit) 286 plat->exit(); 287 288 kfree(info); 289 } 290 291 return 0; 292} 293 294static struct platform_driver armflash_driver = { 295 .probe = armflash_probe, 296 .remove = armflash_remove, 297 .driver = { 298 .name = "armflash", 299 .owner = THIS_MODULE, 300 }, 301}; 302 303static int __init armflash_init(void) 304{ 305 return platform_driver_register(&armflash_driver); 306} 307 308static void __exit armflash_exit(void) 309{ 310 platform_driver_unregister(&armflash_driver); 311} 312 313module_init(armflash_init); 314module_exit(armflash_exit); 315 316MODULE_AUTHOR("ARM Ltd"); 317MODULE_DESCRIPTION("ARM Integrator CFI map driver"); 318MODULE_LICENSE("GPL"); 319MODULE_ALIAS("platform:armflash"); 320