at v2.6.25 321 lines 7.8 kB view raw
1/* 2 * $Id: physmap.c,v 1.39 2005/11/29 14:49:36 gleixner Exp $ 3 * 4 * Normal mappings of chips in physical memory 5 * 6 * Copyright (C) 2003 MontaVista Software Inc. 7 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net 8 * 9 * 031022 - [jsun] add run-time configure and partition setup 10 */ 11 12#include <linux/module.h> 13#include <linux/types.h> 14#include <linux/kernel.h> 15#include <linux/init.h> 16#include <linux/slab.h> 17#include <linux/device.h> 18#include <linux/platform_device.h> 19#include <linux/mtd/mtd.h> 20#include <linux/mtd/map.h> 21#include <linux/mtd/partitions.h> 22#include <linux/mtd/physmap.h> 23#include <linux/mtd/concat.h> 24#include <asm/io.h> 25 26#define MAX_RESOURCES 4 27 28struct physmap_flash_info { 29 struct mtd_info *mtd[MAX_RESOURCES]; 30 struct mtd_info *cmtd; 31 struct map_info map[MAX_RESOURCES]; 32 struct resource *res; 33#ifdef CONFIG_MTD_PARTITIONS 34 int nr_parts; 35 struct mtd_partition *parts; 36#endif 37}; 38 39static int physmap_flash_remove(struct platform_device *dev) 40{ 41 struct physmap_flash_info *info; 42 struct physmap_flash_data *physmap_data; 43 int i; 44 45 info = platform_get_drvdata(dev); 46 if (info == NULL) 47 return 0; 48 platform_set_drvdata(dev, NULL); 49 50 physmap_data = dev->dev.platform_data; 51 52#ifdef CONFIG_MTD_CONCAT 53 if (info->cmtd != info->mtd[0]) { 54 del_mtd_device(info->cmtd); 55 mtd_concat_destroy(info->cmtd); 56 } 57#endif 58 59 for (i = 0; i < MAX_RESOURCES; i++) { 60 if (info->mtd[i] != NULL) { 61#ifdef CONFIG_MTD_PARTITIONS 62 if (info->nr_parts) { 63 del_mtd_partitions(info->mtd[i]); 64 kfree(info->parts); 65 } else if (physmap_data->nr_parts) { 66 del_mtd_partitions(info->mtd[i]); 67 } else { 68 del_mtd_device(info->mtd[i]); 69 } 70#else 71 del_mtd_device(info->mtd[i]); 72#endif 73 map_destroy(info->mtd[i]); 74 } 75 76 if (info->map[i].virt != NULL) 77 iounmap(info->map[i].virt); 78 } 79 80 if (info->res != NULL) { 81 release_resource(info->res); 82 kfree(info->res); 83 } 84 85 return 0; 86} 87 88static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; 89#ifdef CONFIG_MTD_PARTITIONS 90static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL }; 91#endif 92 93static int physmap_flash_probe(struct platform_device *dev) 94{ 95 struct physmap_flash_data *physmap_data; 96 struct physmap_flash_info *info; 97 const char **probe_type; 98 int err = 0; 99 int i; 100 int devices_found = 0; 101 102 physmap_data = dev->dev.platform_data; 103 if (physmap_data == NULL) 104 return -ENODEV; 105 106 info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); 107 if (info == NULL) { 108 err = -ENOMEM; 109 goto err_out; 110 } 111 112 platform_set_drvdata(dev, info); 113 114 for (i = 0; i < dev->num_resources; i++) { 115 printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", 116 (unsigned long long)(dev->resource[i].end - dev->resource[i].start + 1), 117 (unsigned long long)dev->resource[i].start); 118 119 info->res = request_mem_region(dev->resource[i].start, 120 dev->resource[i].end - dev->resource[i].start + 1, 121 dev->dev.bus_id); 122 if (info->res == NULL) { 123 dev_err(&dev->dev, "Could not reserve memory region\n"); 124 err = -ENOMEM; 125 goto err_out; 126 } 127 128 info->map[i].name = dev->dev.bus_id; 129 info->map[i].phys = dev->resource[i].start; 130 info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; 131 info->map[i].bankwidth = physmap_data->width; 132 info->map[i].set_vpp = physmap_data->set_vpp; 133 134 info->map[i].virt = ioremap(info->map[i].phys, info->map[i].size); 135 if (info->map[i].virt == NULL) { 136 dev_err(&dev->dev, "Failed to ioremap flash region\n"); 137 err = EIO; 138 goto err_out; 139 } 140 141 simple_map_init(&info->map[i]); 142 143 probe_type = rom_probe_types; 144 for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) 145 info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); 146 if (info->mtd[i] == NULL) { 147 dev_err(&dev->dev, "map_probe failed\n"); 148 err = -ENXIO; 149 goto err_out; 150 } else { 151 devices_found++; 152 } 153 info->mtd[i]->owner = THIS_MODULE; 154 } 155 156 if (devices_found == 1) { 157 info->cmtd = info->mtd[0]; 158 } else if (devices_found > 1) { 159 /* 160 * We detected multiple devices. Concatenate them together. 161 */ 162#ifdef CONFIG_MTD_CONCAT 163 info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id); 164 if (info->cmtd == NULL) 165 err = -ENXIO; 166#else 167 printk(KERN_ERR "physmap-flash: multiple devices " 168 "found but MTD concat support disabled.\n"); 169 err = -ENXIO; 170#endif 171 } 172 if (err) 173 goto err_out; 174 175#ifdef CONFIG_MTD_PARTITIONS 176 err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); 177 if (err > 0) { 178 add_mtd_partitions(info->cmtd, info->parts, err); 179 return 0; 180 } 181 182 if (physmap_data->nr_parts) { 183 printk(KERN_NOTICE "Using physmap partition information\n"); 184 add_mtd_partitions(info->cmtd, physmap_data->parts, 185 physmap_data->nr_parts); 186 return 0; 187 } 188#endif 189 190 add_mtd_device(info->cmtd); 191 return 0; 192 193err_out: 194 physmap_flash_remove(dev); 195 return err; 196} 197 198#ifdef CONFIG_PM 199static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state) 200{ 201 struct physmap_flash_info *info = platform_get_drvdata(dev); 202 int ret = 0; 203 int i; 204 205 for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) 206 ret |= info->mtd[i]->suspend(info->mtd[i]); 207 208 return ret; 209} 210 211static int physmap_flash_resume(struct platform_device *dev) 212{ 213 struct physmap_flash_info *info = platform_get_drvdata(dev); 214 int i; 215 216 for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) 217 info->mtd[i]->resume(info->mtd[i]); 218 219 return 0; 220} 221 222static void physmap_flash_shutdown(struct platform_device *dev) 223{ 224 struct physmap_flash_info *info = platform_get_drvdata(dev); 225 int i; 226 227 for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) 228 if (info->mtd[i]->suspend(info->mtd[i]) == 0) 229 info->mtd[i]->resume(info->mtd[i]); 230} 231#else 232#define physmap_flash_suspend NULL 233#define physmap_flash_resume NULL 234#define physmap_flash_shutdown NULL 235#endif 236 237static struct platform_driver physmap_flash_driver = { 238 .probe = physmap_flash_probe, 239 .remove = physmap_flash_remove, 240 .suspend = physmap_flash_suspend, 241 .resume = physmap_flash_resume, 242 .shutdown = physmap_flash_shutdown, 243 .driver = { 244 .name = "physmap-flash", 245 }, 246}; 247 248 249#ifdef CONFIG_MTD_PHYSMAP_LEN 250#if CONFIG_MTD_PHYSMAP_LEN != 0 251#warning using PHYSMAP compat code 252#define PHYSMAP_COMPAT 253#endif 254#endif 255 256#ifdef PHYSMAP_COMPAT 257static struct physmap_flash_data physmap_flash_data = { 258 .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, 259}; 260 261static struct resource physmap_flash_resource = { 262 .start = CONFIG_MTD_PHYSMAP_START, 263 .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1, 264 .flags = IORESOURCE_MEM, 265}; 266 267static struct platform_device physmap_flash = { 268 .name = "physmap-flash", 269 .id = 0, 270 .dev = { 271 .platform_data = &physmap_flash_data, 272 }, 273 .num_resources = 1, 274 .resource = &physmap_flash_resource, 275}; 276 277void physmap_configure(unsigned long addr, unsigned long size, 278 int bankwidth, void (*set_vpp)(struct map_info *, int)) 279{ 280 physmap_flash_resource.start = addr; 281 physmap_flash_resource.end = addr + size - 1; 282 physmap_flash_data.width = bankwidth; 283 physmap_flash_data.set_vpp = set_vpp; 284} 285 286#ifdef CONFIG_MTD_PARTITIONS 287void physmap_set_partitions(struct mtd_partition *parts, int num_parts) 288{ 289 physmap_flash_data.nr_parts = num_parts; 290 physmap_flash_data.parts = parts; 291} 292#endif 293#endif 294 295static int __init physmap_init(void) 296{ 297 int err; 298 299 err = platform_driver_register(&physmap_flash_driver); 300#ifdef PHYSMAP_COMPAT 301 if (err == 0) 302 platform_device_register(&physmap_flash); 303#endif 304 305 return err; 306} 307 308static void __exit physmap_exit(void) 309{ 310#ifdef PHYSMAP_COMPAT 311 platform_device_unregister(&physmap_flash); 312#endif 313 platform_driver_unregister(&physmap_flash_driver); 314} 315 316module_init(physmap_init); 317module_exit(physmap_exit); 318 319MODULE_LICENSE("GPL"); 320MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 321MODULE_DESCRIPTION("Generic configurable MTD map driver");