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