1/* 2 * Flash memory access on SA11x0 based devices 3 * 4 * (C) 2000 Nicolas Pitre <nico@cam.org> 5 * 6 * $Id: sa1100-flash.c,v 1.1.1.1 2007/08/03 18:52:44 Exp $ 7 */ 8#include <linux/module.h> 9#include <linux/types.h> 10#include <linux/ioport.h> 11#include <linux/kernel.h> 12#include <linux/init.h> 13#include <linux/errno.h> 14#include <linux/slab.h> 15#include <linux/platform_device.h> 16#include <linux/err.h> 17 18#include <linux/mtd/mtd.h> 19#include <linux/mtd/map.h> 20#include <linux/mtd/partitions.h> 21#include <linux/mtd/concat.h> 22 23#include <asm/hardware.h> 24#include <asm/io.h> 25#include <asm/sizes.h> 26#include <asm/mach/flash.h> 27 28 29struct sa_subdev_info { 30 char name[16]; 31 struct map_info map; 32 struct mtd_info *mtd; 33 struct flash_platform_data *plat; 34}; 35 36struct sa_info { 37 struct mtd_partition *parts; 38 struct mtd_info *mtd; 39 int num_subdev; 40 unsigned int nr_parts; 41 struct sa_subdev_info subdev[0]; 42}; 43 44static void sa1100_set_vpp(struct map_info *map, int on) 45{ 46 struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map); 47 subdev->plat->set_vpp(on); 48} 49 50static void sa1100_destroy_subdev(struct sa_subdev_info *subdev) 51{ 52 if (subdev->mtd) 53 map_destroy(subdev->mtd); 54 if (subdev->map.virt) 55 iounmap(subdev->map.virt); 56 release_mem_region(subdev->map.phys, subdev->map.size); 57} 58 59static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res) 60{ 61 unsigned long phys; 62 unsigned int size; 63 int ret; 64 65 phys = res->start; 66 size = res->end - phys + 1; 67 68 /* 69 * Retrieve the bankwidth from the MSC registers. 70 * We currently only implement CS0 and CS1 here. 71 */ 72 switch (phys) { 73 default: 74 printk(KERN_WARNING "SA1100 flash: unknown base address " 75 "0x%08lx, assuming CS0\n", phys); 76 77 case SA1100_CS0_PHYS: 78 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; 79 break; 80 81 case SA1100_CS1_PHYS: 82 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; 83 break; 84 } 85 86 if (!request_mem_region(phys, size, subdev->name)) { 87 ret = -EBUSY; 88 goto out; 89 } 90 91 if (subdev->plat->set_vpp) 92 subdev->map.set_vpp = sa1100_set_vpp; 93 94 subdev->map.phys = phys; 95 subdev->map.size = size; 96 subdev->map.virt = ioremap(phys, size); 97 if (!subdev->map.virt) { 98 ret = -ENOMEM; 99 goto err; 100 } 101 102 simple_map_init(&subdev->map); 103 104 /* 105 * Now let's probe for the actual flash. Do it here since 106 * specific machine settings might have been set above. 107 */ 108 subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map); 109 if (subdev->mtd == NULL) { 110 ret = -ENXIO; 111 goto err; 112 } 113 subdev->mtd->owner = THIS_MODULE; 114 115 printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " 116 "%d-bit\n", phys, subdev->mtd->size >> 20, 117 subdev->map.bankwidth * 8); 118 119 return 0; 120 121 err: 122 sa1100_destroy_subdev(subdev); 123 out: 124 return ret; 125} 126 127static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat) 128{ 129 int i; 130 131 if (info->mtd) { 132 if (info->nr_parts == 0) 133 del_mtd_device(info->mtd); 134#ifdef CONFIG_MTD_PARTITIONS 135 else 136 del_mtd_partitions(info->mtd); 137#endif 138#ifdef CONFIG_MTD_CONCAT 139 if (info->mtd != info->subdev[0].mtd) 140 mtd_concat_destroy(info->mtd); 141#endif 142 } 143 144 kfree(info->parts); 145 146 for (i = info->num_subdev - 1; i >= 0; i--) 147 sa1100_destroy_subdev(&info->subdev[i]); 148 kfree(info); 149 150 if (plat->exit) 151 plat->exit(); 152} 153 154static struct sa_info *__init 155sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) 156{ 157 struct sa_info *info; 158 int nr, size, i, ret = 0; 159 160 /* 161 * Count number of devices. 162 */ 163 for (nr = 0; ; nr++) 164 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr)) 165 break; 166 167 if (nr == 0) { 168 ret = -ENODEV; 169 goto out; 170 } 171 172 size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr; 173 174 /* 175 * Allocate the map_info structs in one go. 176 */ 177 info = kzalloc(size, GFP_KERNEL); 178 if (!info) { 179 ret = -ENOMEM; 180 goto out; 181 } 182 183 if (plat->init) { 184 ret = plat->init(); 185 if (ret) 186 goto err; 187 } 188 189 /* 190 * Claim and then map the memory regions. 191 */ 192 for (i = 0; i < nr; i++) { 193 struct sa_subdev_info *subdev = &info->subdev[i]; 194 struct resource *res; 195 196 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 197 if (!res) 198 break; 199 200 subdev->map.name = subdev->name; 201 sprintf(subdev->name, "%s-%d", plat->name, i); 202 subdev->plat = plat; 203 204 ret = sa1100_probe_subdev(subdev, res); 205 if (ret) 206 break; 207 } 208 209 info->num_subdev = i; 210 211 /* 212 * ENXIO is special. It means we didn't find a chip when we probed. 213 */ 214 if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0)) 215 goto err; 216 217 /* 218 * If we found one device, don't bother with concat support. If 219 * we found multiple devices, use concat if we have it available, 220 * otherwise fail. Either way, it'll be called "sa1100". 221 */ 222 if (info->num_subdev == 1) { 223 strcpy(info->subdev[0].name, plat->name); 224 info->mtd = info->subdev[0].mtd; 225 ret = 0; 226 } else if (info->num_subdev > 1) { 227#ifdef CONFIG_MTD_CONCAT 228 struct mtd_info *cdev[nr]; 229 /* 230 * We detected multiple devices. Concatenate them together. 231 */ 232 for (i = 0; i < info->num_subdev; i++) 233 cdev[i] = info->subdev[i].mtd; 234 235 info->mtd = mtd_concat_create(cdev, info->num_subdev, 236 plat->name); 237 if (info->mtd == NULL) 238 ret = -ENXIO; 239#else 240 printk(KERN_ERR "SA1100 flash: multiple devices " 241 "found but MTD concat support disabled.\n"); 242 ret = -ENXIO; 243#endif 244 } 245 246 if (ret == 0) 247 return info; 248 249 err: 250 sa1100_destroy(info, plat); 251 out: 252 return ERR_PTR(ret); 253} 254 255static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; 256 257static int __init sa1100_mtd_probe(struct platform_device *pdev) 258{ 259 struct flash_platform_data *plat = pdev->dev.platform_data; 260 struct mtd_partition *parts; 261 const char *part_type = NULL; 262 struct sa_info *info; 263 int err, nr_parts = 0; 264 265 if (!plat) 266 return -ENODEV; 267 268 info = sa1100_setup_mtd(pdev, plat); 269 if (IS_ERR(info)) { 270 err = PTR_ERR(info); 271 goto out; 272 } 273 274 /* 275 * Partition selection stuff. 276 */ 277#ifdef CONFIG_MTD_PARTITIONS 278 nr_parts = parse_mtd_partitions(info->mtd, part_probes, &parts, 0); 279 if (nr_parts > 0) { 280 info->parts = parts; 281 part_type = "dynamic"; 282 } else 283#endif 284 { 285 parts = plat->parts; 286 nr_parts = plat->nr_parts; 287 part_type = "static"; 288 } 289 290 if (nr_parts == 0) { 291 printk(KERN_NOTICE "SA1100 flash: no partition info " 292 "available, registering whole flash\n"); 293 add_mtd_device(info->mtd); 294 } else { 295 printk(KERN_NOTICE "SA1100 flash: using %s partition " 296 "definition\n", part_type); 297 add_mtd_partitions(info->mtd, parts, nr_parts); 298 } 299 300 info->nr_parts = nr_parts; 301 302 platform_set_drvdata(pdev, info); 303 err = 0; 304 305 out: 306 return err; 307} 308 309static int __exit sa1100_mtd_remove(struct platform_device *pdev) 310{ 311 struct sa_info *info = platform_get_drvdata(pdev); 312 struct flash_platform_data *plat = pdev->dev.platform_data; 313 314 platform_set_drvdata(pdev, NULL); 315 sa1100_destroy(info, plat); 316 317 return 0; 318} 319 320#ifdef CONFIG_PM 321static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state) 322{ 323 struct sa_info *info = platform_get_drvdata(dev); 324 int ret = 0; 325 326 if (info) 327 ret = info->mtd->suspend(info->mtd); 328 329 return ret; 330} 331 332static int sa1100_mtd_resume(struct platform_device *dev) 333{ 334 struct sa_info *info = platform_get_drvdata(dev); 335 if (info) 336 info->mtd->resume(info->mtd); 337 return 0; 338} 339 340static void sa1100_mtd_shutdown(struct platform_device *dev) 341{ 342 struct sa_info *info = platform_get_drvdata(dev); 343 if (info && info->mtd->suspend(info->mtd) == 0) 344 info->mtd->resume(info->mtd); 345} 346#else 347#define sa1100_mtd_suspend NULL 348#define sa1100_mtd_resume NULL 349#define sa1100_mtd_shutdown NULL 350#endif 351 352static struct platform_driver sa1100_mtd_driver = { 353 .probe = sa1100_mtd_probe, 354 .remove = __exit_p(sa1100_mtd_remove), 355 .suspend = sa1100_mtd_suspend, 356 .resume = sa1100_mtd_resume, 357 .shutdown = sa1100_mtd_shutdown, 358 .driver = { 359 .name = "flash", 360 }, 361}; 362 363static int __init sa1100_mtd_init(void) 364{ 365 return platform_driver_register(&sa1100_mtd_driver); 366} 367 368static void __exit sa1100_mtd_exit(void) 369{ 370 platform_driver_unregister(&sa1100_mtd_driver); 371} 372 373module_init(sa1100_mtd_init); 374module_exit(sa1100_mtd_exit); 375 376MODULE_AUTHOR("Nicolas Pitre"); 377MODULE_DESCRIPTION("SA1100 CFI map driver"); 378MODULE_LICENSE("GPL"); 379