at v3.2-rc2 266 lines 6.5 kB view raw
1/* 2 * Normal mappings of chips in physical memory 3 * 4 * Copyright (C) 2003 MontaVista Software Inc. 5 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net 6 * 7 * 031022 - [jsun] add run-time configure and partition setup 8 */ 9 10#include <linux/module.h> 11#include <linux/types.h> 12#include <linux/kernel.h> 13#include <linux/init.h> 14#include <linux/slab.h> 15#include <linux/device.h> 16#include <linux/platform_device.h> 17#include <linux/mtd/mtd.h> 18#include <linux/mtd/map.h> 19#include <linux/mtd/partitions.h> 20#include <linux/mtd/physmap.h> 21#include <linux/mtd/concat.h> 22#include <linux/io.h> 23 24#define MAX_RESOURCES 4 25 26struct physmap_flash_info { 27 struct mtd_info *mtd[MAX_RESOURCES]; 28 struct mtd_info *cmtd; 29 struct map_info map[MAX_RESOURCES]; 30}; 31 32static int physmap_flash_remove(struct platform_device *dev) 33{ 34 struct physmap_flash_info *info; 35 struct physmap_flash_data *physmap_data; 36 int i; 37 38 info = platform_get_drvdata(dev); 39 if (info == NULL) 40 return 0; 41 platform_set_drvdata(dev, NULL); 42 43 physmap_data = dev->dev.platform_data; 44 45 if (info->cmtd) { 46 mtd_device_unregister(info->cmtd); 47 if (info->cmtd != info->mtd[0]) 48 mtd_concat_destroy(info->cmtd); 49 } 50 51 for (i = 0; i < MAX_RESOURCES; i++) { 52 if (info->mtd[i] != NULL) 53 map_destroy(info->mtd[i]); 54 } 55 56 if (physmap_data->exit) 57 physmap_data->exit(dev); 58 59 return 0; 60} 61 62static void physmap_set_vpp(struct map_info *map, int state) 63{ 64 struct platform_device *pdev; 65 struct physmap_flash_data *physmap_data; 66 67 pdev = (struct platform_device *)map->map_priv_1; 68 physmap_data = pdev->dev.platform_data; 69 70 if (physmap_data->set_vpp) 71 physmap_data->set_vpp(pdev, state); 72} 73 74static const char *rom_probe_types[] = { 75 "cfi_probe", 76 "jedec_probe", 77 "qinfo_probe", 78 "map_rom", 79 NULL }; 80static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs", 81 NULL }; 82 83static int physmap_flash_probe(struct platform_device *dev) 84{ 85 struct physmap_flash_data *physmap_data; 86 struct physmap_flash_info *info; 87 const char **probe_type; 88 int err = 0; 89 int i; 90 int devices_found = 0; 91 92 physmap_data = dev->dev.platform_data; 93 if (physmap_data == NULL) 94 return -ENODEV; 95 96 info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info), 97 GFP_KERNEL); 98 if (info == NULL) { 99 err = -ENOMEM; 100 goto err_out; 101 } 102 103 if (physmap_data->init) { 104 err = physmap_data->init(dev); 105 if (err) 106 goto err_out; 107 } 108 109 platform_set_drvdata(dev, info); 110 111 for (i = 0; i < dev->num_resources; i++) { 112 printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", 113 (unsigned long long)resource_size(&dev->resource[i]), 114 (unsigned long long)dev->resource[i].start); 115 116 if (!devm_request_mem_region(&dev->dev, 117 dev->resource[i].start, 118 resource_size(&dev->resource[i]), 119 dev_name(&dev->dev))) { 120 dev_err(&dev->dev, "Could not reserve memory region\n"); 121 err = -ENOMEM; 122 goto err_out; 123 } 124 125 info->map[i].name = dev_name(&dev->dev); 126 info->map[i].phys = dev->resource[i].start; 127 info->map[i].size = resource_size(&dev->resource[i]); 128 info->map[i].bankwidth = physmap_data->width; 129 info->map[i].set_vpp = physmap_set_vpp; 130 info->map[i].pfow_base = physmap_data->pfow_base; 131 info->map[i].map_priv_1 = (unsigned long)dev; 132 133 info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys, 134 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 if (physmap_data->probe_type == NULL) { 145 for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) 146 info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); 147 } else 148 info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]); 149 150 if (info->mtd[i] == NULL) { 151 dev_err(&dev->dev, "map_probe failed\n"); 152 err = -ENXIO; 153 goto err_out; 154 } else { 155 devices_found++; 156 } 157 info->mtd[i]->owner = THIS_MODULE; 158 info->mtd[i]->dev.parent = &dev->dev; 159 } 160 161 if (devices_found == 1) { 162 info->cmtd = info->mtd[0]; 163 } else if (devices_found > 1) { 164 /* 165 * We detected multiple devices. Concatenate them together. 166 */ 167 info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev)); 168 if (info->cmtd == NULL) 169 err = -ENXIO; 170 } 171 if (err) 172 goto err_out; 173 174 mtd_device_parse_register(info->cmtd, part_probe_types, 0, 175 physmap_data->parts, physmap_data->nr_parts); 176 return 0; 177 178err_out: 179 physmap_flash_remove(dev); 180 return err; 181} 182 183#ifdef CONFIG_PM 184static void physmap_flash_shutdown(struct platform_device *dev) 185{ 186 struct physmap_flash_info *info = platform_get_drvdata(dev); 187 int i; 188 189 for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) 190 if (info->mtd[i]->suspend && info->mtd[i]->resume) 191 if (info->mtd[i]->suspend(info->mtd[i]) == 0) 192 info->mtd[i]->resume(info->mtd[i]); 193} 194#else 195#define physmap_flash_shutdown NULL 196#endif 197 198static struct platform_driver physmap_flash_driver = { 199 .probe = physmap_flash_probe, 200 .remove = physmap_flash_remove, 201 .shutdown = physmap_flash_shutdown, 202 .driver = { 203 .name = "physmap-flash", 204 .owner = THIS_MODULE, 205 }, 206}; 207 208 209#ifdef CONFIG_MTD_PHYSMAP_COMPAT 210static struct physmap_flash_data physmap_flash_data = { 211 .width = CONFIG_MTD_PHYSMAP_BANKWIDTH, 212}; 213 214static struct resource physmap_flash_resource = { 215 .start = CONFIG_MTD_PHYSMAP_START, 216 .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1, 217 .flags = IORESOURCE_MEM, 218}; 219 220static struct platform_device physmap_flash = { 221 .name = "physmap-flash", 222 .id = 0, 223 .dev = { 224 .platform_data = &physmap_flash_data, 225 }, 226 .num_resources = 1, 227 .resource = &physmap_flash_resource, 228}; 229#endif 230 231static int __init physmap_init(void) 232{ 233 int err; 234 235 err = platform_driver_register(&physmap_flash_driver); 236#ifdef CONFIG_MTD_PHYSMAP_COMPAT 237 if (err == 0) { 238 err = platform_device_register(&physmap_flash); 239 if (err) 240 platform_driver_unregister(&physmap_flash_driver); 241 } 242#endif 243 244 return err; 245} 246 247static void __exit physmap_exit(void) 248{ 249#ifdef CONFIG_MTD_PHYSMAP_COMPAT 250 platform_device_unregister(&physmap_flash); 251#endif 252 platform_driver_unregister(&physmap_flash_driver); 253} 254 255module_init(physmap_init); 256module_exit(physmap_exit); 257 258MODULE_LICENSE("GPL"); 259MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 260MODULE_DESCRIPTION("Generic configurable MTD map driver"); 261 262/* legacy platform drivers can't hotplug or coldplg */ 263#ifndef CONFIG_MTD_PHYSMAP_COMPAT 264/* work with hotplug and coldplug */ 265MODULE_ALIAS("platform:physmap-flash"); 266#endif