linux/drivers/mtd/maps/integrator-flash.c
<<
>>
Prefs
   1/*======================================================================
   2
   3    drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver
   4
   5    Copyright (C) 2000 ARM Limited
   6    Copyright (C) 2003 Deep Blue Solutions Ltd.
   7
   8   This program is free software; you can redistribute it and/or modify
   9   it under the terms of the GNU General Public License as published by
  10   the Free Software Foundation; either version 2 of the License, or
  11   (at your option) any later version.
  12
  13   This program is distributed in the hope that it will be useful,
  14   but WITHOUT ANY WARRANTY; without even the implied warranty of
  15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16   GNU General Public License for more details.
  17
  18   You should have received a copy of the GNU General Public License
  19   along with this program; if not, write to the Free Software
  20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21
  22   This is access code for flashes using ARM's flash partitioning
  23   standards.
  24
  25======================================================================*/
  26
  27#include <linux/module.h>
  28#include <linux/types.h>
  29#include <linux/kernel.h>
  30#include <linux/slab.h>
  31#include <linux/ioport.h>
  32#include <linux/platform_device.h>
  33#include <linux/init.h>
  34#include <linux/io.h>
  35
  36#include <linux/mtd/mtd.h>
  37#include <linux/mtd/map.h>
  38#include <linux/mtd/partitions.h>
  39#include <linux/mtd/concat.h>
  40
  41#include <asm/mach/flash.h>
  42#include <mach/hardware.h>
  43#include <asm/system.h>
  44
  45struct armflash_subdev_info {
  46        char                    *name;
  47        struct mtd_info         *mtd;
  48        struct map_info         map;
  49        struct flash_platform_data *plat;
  50};
  51
  52struct armflash_info {
  53        struct resource         *res;
  54        struct mtd_partition    *parts;
  55        struct mtd_info         *mtd;
  56        int                     nr_subdev;
  57        struct armflash_subdev_info subdev[0];
  58};
  59
  60static void armflash_set_vpp(struct map_info *map, int on)
  61{
  62        struct armflash_subdev_info *info =
  63                container_of(map, struct armflash_subdev_info, map);
  64
  65        if (info->plat && info->plat->set_vpp)
  66                info->plat->set_vpp(on);
  67}
  68
  69static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
  70
  71static int armflash_subdev_probe(struct armflash_subdev_info *subdev,
  72                                 struct resource *res)
  73{
  74        struct flash_platform_data *plat = subdev->plat;
  75        resource_size_t size = res->end - res->start + 1;
  76        void __iomem *base;
  77        int err = 0;
  78
  79        if (!request_mem_region(res->start, size, subdev->name)) {
  80                err = -EBUSY;
  81                goto out;
  82        }
  83
  84        base = ioremap(res->start, size);
  85        if (!base) {
  86                err = -ENOMEM;
  87                goto no_mem;
  88        }
  89
  90        /*
  91         * look for CFI based flash parts fitted to this board
  92         */
  93        subdev->map.size        = size;
  94        subdev->map.bankwidth   = plat->width;
  95        subdev->map.phys        = res->start;
  96        subdev->map.virt        = base;
  97        subdev->map.name        = subdev->name;
  98        subdev->map.set_vpp     = armflash_set_vpp;
  99
 100        simple_map_init(&subdev->map);
 101
 102        /*
 103         * Also, the CFI layer automatically works out what size
 104         * of chips we have, and does the necessary identification
 105         * for us automatically.
 106         */
 107        subdev->mtd = do_map_probe(plat->map_name, &subdev->map);
 108        if (!subdev->mtd) {
 109                err = -ENXIO;
 110                goto no_device;
 111        }
 112
 113        subdev->mtd->owner = THIS_MODULE;
 114
 115        /* Successful? */
 116        if (err == 0)
 117                return err;
 118
 119        if (subdev->mtd)
 120                map_destroy(subdev->mtd);
 121 no_device:
 122        iounmap(base);
 123 no_mem:
 124        release_mem_region(res->start, size);
 125 out:
 126        return err;
 127}
 128
 129static void armflash_subdev_remove(struct armflash_subdev_info *subdev)
 130{
 131        if (subdev->mtd)
 132                map_destroy(subdev->mtd);
 133        if (subdev->map.virt)
 134                iounmap(subdev->map.virt);
 135        kfree(subdev->name);
 136        subdev->name = NULL;
 137        release_mem_region(subdev->map.phys, subdev->map.size);
 138}
 139
 140static int armflash_probe(struct platform_device *dev)
 141{
 142        struct flash_platform_data *plat = dev->dev.platform_data;
 143        unsigned int size;
 144        struct armflash_info *info;
 145        int i, nr, err;
 146
 147        /* Count the number of devices */
 148        for (nr = 0; ; nr++)
 149                if (!platform_get_resource(dev, IORESOURCE_MEM, nr))
 150                        break;
 151        if (nr == 0) {
 152                err = -ENODEV;
 153                goto out;
 154        }
 155
 156        size = sizeof(struct armflash_info) +
 157                sizeof(struct armflash_subdev_info) * nr;
 158        info = kzalloc(size, GFP_KERNEL);
 159        if (!info) {
 160                err = -ENOMEM;
 161                goto out;
 162        }
 163
 164        if (plat && plat->init) {
 165                err = plat->init();
 166                if (err)
 167                        goto no_resource;
 168        }
 169
 170        for (i = 0; i < nr; i++) {
 171                struct armflash_subdev_info *subdev = &info->subdev[i];
 172                struct resource *res;
 173
 174                res = platform_get_resource(dev, IORESOURCE_MEM, i);
 175                if (!res)
 176                        break;
 177
 178                if (nr == 1)
 179                        /* No MTD concatenation, just use the default name */
 180                        subdev->name = kstrdup(dev_name(&dev->dev), GFP_KERNEL);
 181                else
 182                        subdev->name = kasprintf(GFP_KERNEL, "%s-%d",
 183                                                 dev_name(&dev->dev), i);
 184                if (!subdev->name) {
 185                        err = -ENOMEM;
 186                        break;
 187                }
 188                subdev->plat = plat;
 189
 190                err = armflash_subdev_probe(subdev, res);
 191                if (err) {
 192                        kfree(subdev->name);
 193                        subdev->name = NULL;
 194                        break;
 195                }
 196        }
 197        info->nr_subdev = i;
 198
 199        if (err)
 200                goto subdev_err;
 201
 202        if (info->nr_subdev == 1)
 203                info->mtd = info->subdev[0].mtd;
 204        else if (info->nr_subdev > 1) {
 205#ifdef CONFIG_MTD_CONCAT
 206                struct mtd_info *cdev[info->nr_subdev];
 207
 208                /*
 209                 * We detected multiple devices.  Concatenate them together.
 210                 */
 211                for (i = 0; i < info->nr_subdev; i++)
 212                        cdev[i] = info->subdev[i].mtd;
 213
 214                info->mtd = mtd_concat_create(cdev, info->nr_subdev,
 215                                              dev_name(&dev->dev));
 216                if (info->mtd == NULL)
 217                        err = -ENXIO;
 218#else
 219                printk(KERN_ERR "armflash: multiple devices found but "
 220                       "MTD concat support disabled.\n");
 221                err = -ENXIO;
 222#endif
 223        }
 224
 225        if (err < 0)
 226                goto cleanup;
 227
 228        err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
 229        if (err > 0) {
 230                err = add_mtd_partitions(info->mtd, info->parts, err);
 231                if (err)
 232                        printk(KERN_ERR
 233                               "mtd partition registration failed: %d\n", err);
 234        }
 235
 236        if (err == 0) {
 237                platform_set_drvdata(dev, info);
 238                return err;
 239        }
 240
 241        /*
 242         * We got an error, free all resources.
 243         */
 244 cleanup:
 245        if (info->mtd) {
 246                del_mtd_partitions(info->mtd);
 247#ifdef CONFIG_MTD_CONCAT
 248                if (info->mtd != info->subdev[0].mtd)
 249                        mtd_concat_destroy(info->mtd);
 250#endif
 251        }
 252        kfree(info->parts);
 253 subdev_err:
 254        for (i = info->nr_subdev - 1; i >= 0; i--)
 255                armflash_subdev_remove(&info->subdev[i]);
 256 no_resource:
 257        if (plat && plat->exit)
 258                plat->exit();
 259        kfree(info);
 260 out:
 261        return err;
 262}
 263
 264static int armflash_remove(struct platform_device *dev)
 265{
 266        struct armflash_info *info = platform_get_drvdata(dev);
 267        struct flash_platform_data *plat = dev->dev.platform_data;
 268        int i;
 269
 270        platform_set_drvdata(dev, NULL);
 271
 272        if (info) {
 273                if (info->mtd) {
 274                        del_mtd_partitions(info->mtd);
 275#ifdef CONFIG_MTD_CONCAT
 276                        if (info->mtd != info->subdev[0].mtd)
 277                                mtd_concat_destroy(info->mtd);
 278#endif
 279                }
 280                kfree(info->parts);
 281
 282                for (i = info->nr_subdev - 1; i >= 0; i--)
 283                        armflash_subdev_remove(&info->subdev[i]);
 284
 285                if (plat && plat->exit)
 286                        plat->exit();
 287
 288                kfree(info);
 289        }
 290
 291        return 0;
 292}
 293
 294static struct platform_driver armflash_driver = {
 295        .probe          = armflash_probe,
 296        .remove         = armflash_remove,
 297        .driver         = {
 298                .name   = "armflash",
 299                .owner  = THIS_MODULE,
 300        },
 301};
 302
 303static int __init armflash_init(void)
 304{
 305        return platform_driver_register(&armflash_driver);
 306}
 307
 308static void __exit armflash_exit(void)
 309{
 310        platform_driver_unregister(&armflash_driver);
 311}
 312
 313module_init(armflash_init);
 314module_exit(armflash_exit);
 315
 316MODULE_AUTHOR("ARM Ltd");
 317MODULE_DESCRIPTION("ARM Integrator CFI map driver");
 318MODULE_LICENSE("GPL");
 319MODULE_ALIAS("platform:armflash");
 320
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.