linux/drivers/mtd/maps/physmap.c
<<
>>
Prefs
   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        spinlock_t              vpp_lock;
  31        int                     vpp_refcnt;
  32};
  33
  34static int physmap_flash_remove(struct platform_device *dev)
  35{
  36        struct physmap_flash_info *info;
  37        struct physmap_flash_data *physmap_data;
  38        int i;
  39
  40        info = platform_get_drvdata(dev);
  41        if (info == NULL)
  42                return 0;
  43        platform_set_drvdata(dev, NULL);
  44
  45        physmap_data = dev->dev.platform_data;
  46
  47        if (info->cmtd) {
  48                mtd_device_unregister(info->cmtd);
  49                if (info->cmtd != info->mtd[0])
  50                        mtd_concat_destroy(info->cmtd);
  51        }
  52
  53        for (i = 0; i < MAX_RESOURCES; i++) {
  54                if (info->mtd[i] != NULL)
  55                        map_destroy(info->mtd[i]);
  56        }
  57
  58        if (physmap_data->exit)
  59                physmap_data->exit(dev);
  60
  61        return 0;
  62}
  63
  64static void physmap_set_vpp(struct map_info *map, int state)
  65{
  66        struct platform_device *pdev;
  67        struct physmap_flash_data *physmap_data;
  68        struct physmap_flash_info *info;
  69        unsigned long flags;
  70
  71        pdev = (struct platform_device *)map->map_priv_1;
  72        physmap_data = pdev->dev.platform_data;
  73
  74        if (!physmap_data->set_vpp)
  75                return;
  76
  77        info = platform_get_drvdata(pdev);
  78
  79        spin_lock_irqsave(&info->vpp_lock, flags);
  80        if (state) {
  81                if (++info->vpp_refcnt == 1)    /* first nested 'on' */
  82                        physmap_data->set_vpp(pdev, 1);
  83        } else {
  84                if (--info->vpp_refcnt == 0)    /* last nested 'off' */
  85                        physmap_data->set_vpp(pdev, 0);
  86        }
  87        spin_unlock_irqrestore(&info->vpp_lock, flags);
  88}
  89
  90static const char *rom_probe_types[] = {
  91                                        "cfi_probe",
  92                                        "jedec_probe",
  93                                        "qinfo_probe",
  94                                        "map_rom",
  95                                        NULL };
  96static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs",
  97                                          NULL };
  98
  99static int physmap_flash_probe(struct platform_device *dev)
 100{
 101        struct physmap_flash_data *physmap_data;
 102        struct physmap_flash_info *info;
 103        const char **probe_type;
 104        const char **part_types;
 105        int err = 0;
 106        int i;
 107        int devices_found = 0;
 108
 109        physmap_data = dev->dev.platform_data;
 110        if (physmap_data == NULL)
 111                return -ENODEV;
 112
 113        info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
 114                            GFP_KERNEL);
 115        if (info == NULL) {
 116                err = -ENOMEM;
 117                goto err_out;
 118        }
 119
 120        if (physmap_data->init) {
 121                err = physmap_data->init(dev);
 122                if (err)
 123                        goto err_out;
 124        }
 125
 126        platform_set_drvdata(dev, info);
 127
 128        for (i = 0; i < dev->num_resources; i++) {
 129                printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
 130                       (unsigned long long)resource_size(&dev->resource[i]),
 131                       (unsigned long long)dev->resource[i].start);
 132
 133                if (!devm_request_mem_region(&dev->dev,
 134                        dev->resource[i].start,
 135                        resource_size(&dev->resource[i]),
 136                        dev_name(&dev->dev))) {
 137                        dev_err(&dev->dev, "Could not reserve memory region\n");
 138                        err = -ENOMEM;
 139                        goto err_out;
 140                }
 141
 142                info->map[i].name = dev_name(&dev->dev);
 143                info->map[i].phys = dev->resource[i].start;
 144                info->map[i].size = resource_size(&dev->resource[i]);
 145                info->map[i].bankwidth = physmap_data->width;
 146                info->map[i].set_vpp = physmap_set_vpp;
 147                info->map[i].pfow_base = physmap_data->pfow_base;
 148                info->map[i].map_priv_1 = (unsigned long)dev;
 149
 150                info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
 151                                                 info->map[i].size);
 152                if (info->map[i].virt == NULL) {
 153                        dev_err(&dev->dev, "Failed to ioremap flash region\n");
 154                        err = -EIO;
 155                        goto err_out;
 156                }
 157
 158                simple_map_init(&info->map[i]);
 159
 160                probe_type = rom_probe_types;
 161                if (physmap_data->probe_type == NULL) {
 162                        for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
 163                                info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
 164                } else
 165                        info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
 166
 167                if (info->mtd[i] == NULL) {
 168                        dev_err(&dev->dev, "map_probe failed\n");
 169                        err = -ENXIO;
 170                        goto err_out;
 171                } else {
 172                        devices_found++;
 173                }
 174                info->mtd[i]->owner = THIS_MODULE;
 175                info->mtd[i]->dev.parent = &dev->dev;
 176        }
 177
 178        if (devices_found == 1) {
 179                info->cmtd = info->mtd[0];
 180        } else if (devices_found > 1) {
 181                /*
 182                 * We detected multiple devices. Concatenate them together.
 183                 */
 184                info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
 185                if (info->cmtd == NULL)
 186                        err = -ENXIO;
 187        }
 188        if (err)
 189                goto err_out;
 190
 191        spin_lock_init(&info->vpp_lock);
 192
 193        part_types = physmap_data->part_probe_types ? : part_probe_types;
 194
 195        mtd_device_parse_register(info->cmtd, part_types, NULL,
 196                                  physmap_data->parts, physmap_data->nr_parts);
 197        return 0;
 198
 199err_out:
 200        physmap_flash_remove(dev);
 201        return err;
 202}
 203
 204#ifdef CONFIG_PM
 205static void physmap_flash_shutdown(struct platform_device *dev)
 206{
 207        struct physmap_flash_info *info = platform_get_drvdata(dev);
 208        int i;
 209
 210        for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
 211                if (mtd_suspend(info->mtd[i]) == 0)
 212                        mtd_resume(info->mtd[i]);
 213}
 214#else
 215#define physmap_flash_shutdown NULL
 216#endif
 217
 218static struct platform_driver physmap_flash_driver = {
 219        .probe          = physmap_flash_probe,
 220        .remove         = physmap_flash_remove,
 221        .shutdown       = physmap_flash_shutdown,
 222        .driver         = {
 223                .name   = "physmap-flash",
 224                .owner  = THIS_MODULE,
 225        },
 226};
 227
 228
 229#ifdef CONFIG_MTD_PHYSMAP_COMPAT
 230static struct physmap_flash_data physmap_flash_data = {
 231        .width          = CONFIG_MTD_PHYSMAP_BANKWIDTH,
 232};
 233
 234static struct resource physmap_flash_resource = {
 235        .start          = CONFIG_MTD_PHYSMAP_START,
 236        .end            = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
 237        .flags          = IORESOURCE_MEM,
 238};
 239
 240static struct platform_device physmap_flash = {
 241        .name           = "physmap-flash",
 242        .id             = 0,
 243        .dev            = {
 244                .platform_data  = &physmap_flash_data,
 245        },
 246        .num_resources  = 1,
 247        .resource       = &physmap_flash_resource,
 248};
 249#endif
 250
 251static int __init physmap_init(void)
 252{
 253        int err;
 254
 255        err = platform_driver_register(&physmap_flash_driver);
 256#ifdef CONFIG_MTD_PHYSMAP_COMPAT
 257        if (err == 0) {
 258                err = platform_device_register(&physmap_flash);
 259                if (err)
 260                        platform_driver_unregister(&physmap_flash_driver);
 261        }
 262#endif
 263
 264        return err;
 265}
 266
 267static void __exit physmap_exit(void)
 268{
 269#ifdef CONFIG_MTD_PHYSMAP_COMPAT
 270        platform_device_unregister(&physmap_flash);
 271#endif
 272        platform_driver_unregister(&physmap_flash_driver);
 273}
 274
 275module_init(physmap_init);
 276module_exit(physmap_exit);
 277
 278MODULE_LICENSE("GPL");
 279MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 280MODULE_DESCRIPTION("Generic configurable MTD map driver");
 281
 282/* legacy platform drivers can't hotplug or coldplg */
 283#ifndef CONFIG_MTD_PHYSMAP_COMPAT
 284/* work with hotplug and coldplug */
 285MODULE_ALIAS("platform:physmap-flash");
 286#endif
 287