root/drivers/mtd/maps/sa1100-flash.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sa1100_set_vpp
  2. sa1100_destroy_subdev
  3. sa1100_probe_subdev
  4. sa1100_destroy
  5. sa1100_setup_mtd
  6. sa1100_mtd_probe
  7. sa1100_mtd_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Flash memory access on SA11x0 based devices
   4  *
   5  * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
   6  */
   7 #include <linux/module.h>
   8 #include <linux/types.h>
   9 #include <linux/ioport.h>
  10 #include <linux/kernel.h>
  11 #include <linux/init.h>
  12 #include <linux/errno.h>
  13 #include <linux/slab.h>
  14 #include <linux/platform_device.h>
  15 #include <linux/err.h>
  16 #include <linux/io.h>
  17 
  18 #include <linux/mtd/mtd.h>
  19 #include <linux/mtd/map.h>
  20 #include <linux/mtd/partitions.h>
  21 #include <linux/mtd/concat.h>
  22 
  23 #include <mach/hardware.h>
  24 #include <linux/sizes.h>
  25 #include <asm/mach/flash.h>
  26 
  27 struct sa_subdev_info {
  28         char name[16];
  29         struct map_info map;
  30         struct mtd_info *mtd;
  31         struct flash_platform_data *plat;
  32 };
  33 
  34 struct sa_info {
  35         struct mtd_info         *mtd;
  36         int                     num_subdev;
  37         struct sa_subdev_info   subdev[0];
  38 };
  39 
  40 static DEFINE_SPINLOCK(sa1100_vpp_lock);
  41 static int sa1100_vpp_refcnt;
  42 static void sa1100_set_vpp(struct map_info *map, int on)
  43 {
  44         struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
  45         unsigned long flags;
  46 
  47         spin_lock_irqsave(&sa1100_vpp_lock, flags);
  48         if (on) {
  49                 if (++sa1100_vpp_refcnt == 1)   /* first nested 'on' */
  50                         subdev->plat->set_vpp(1);
  51         } else {
  52                 if (--sa1100_vpp_refcnt == 0)   /* last nested 'off' */
  53                         subdev->plat->set_vpp(0);
  54         }
  55         spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
  56 }
  57 
  58 static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
  59 {
  60         if (subdev->mtd)
  61                 map_destroy(subdev->mtd);
  62         if (subdev->map.virt)
  63                 iounmap(subdev->map.virt);
  64         release_mem_region(subdev->map.phys, subdev->map.size);
  65 }
  66 
  67 static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
  68 {
  69         unsigned long phys;
  70         unsigned int size;
  71         int ret;
  72 
  73         phys = res->start;
  74         size = res->end - phys + 1;
  75 
  76         /*
  77          * Retrieve the bankwidth from the MSC registers.
  78          * We currently only implement CS0 and CS1 here.
  79          */
  80         switch (phys) {
  81         default:
  82                 printk(KERN_WARNING "SA1100 flash: unknown base address "
  83                        "0x%08lx, assuming CS0\n", phys);
  84                 /* Fall through */
  85 
  86         case SA1100_CS0_PHYS:
  87                 subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
  88                 break;
  89 
  90         case SA1100_CS1_PHYS:
  91                 subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
  92                 break;
  93         }
  94 
  95         if (!request_mem_region(phys, size, subdev->name)) {
  96                 ret = -EBUSY;
  97                 goto out;
  98         }
  99 
 100         if (subdev->plat->set_vpp)
 101                 subdev->map.set_vpp = sa1100_set_vpp;
 102 
 103         subdev->map.phys = phys;
 104         subdev->map.size = size;
 105         subdev->map.virt = ioremap(phys, size);
 106         if (!subdev->map.virt) {
 107                 ret = -ENOMEM;
 108                 goto err;
 109         }
 110 
 111         simple_map_init(&subdev->map);
 112 
 113         /*
 114          * Now let's probe for the actual flash.  Do it here since
 115          * specific machine settings might have been set above.
 116          */
 117         subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
 118         if (subdev->mtd == NULL) {
 119                 ret = -ENXIO;
 120                 goto err;
 121         }
 122 
 123         printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
 124                 phys, (unsigned)(subdev->mtd->size >> 20),
 125                 subdev->map.bankwidth * 8);
 126 
 127         return 0;
 128 
 129  err:
 130         sa1100_destroy_subdev(subdev);
 131  out:
 132         return ret;
 133 }
 134 
 135 static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
 136 {
 137         int i;
 138 
 139         if (info->mtd) {
 140                 mtd_device_unregister(info->mtd);
 141                 if (info->mtd != info->subdev[0].mtd)
 142                         mtd_concat_destroy(info->mtd);
 143         }
 144 
 145         for (i = info->num_subdev - 1; i >= 0; i--)
 146                 sa1100_destroy_subdev(&info->subdev[i]);
 147         kfree(info);
 148 
 149         if (plat->exit)
 150                 plat->exit();
 151 }
 152 
 153 static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
 154                                         struct flash_platform_data *plat)
 155 {
 156         struct sa_info *info;
 157         int nr, size, i, ret = 0;
 158 
 159         /*
 160          * Count number of devices.
 161          */
 162         for (nr = 0; ; nr++)
 163                 if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
 164                         break;
 165 
 166         if (nr == 0) {
 167                 ret = -ENODEV;
 168                 goto out;
 169         }
 170 
 171         size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
 172 
 173         /*
 174          * Allocate the map_info structs in one go.
 175          */
 176         info = kzalloc(size, GFP_KERNEL);
 177         if (!info) {
 178                 ret = -ENOMEM;
 179                 goto out;
 180         }
 181 
 182         if (plat->init) {
 183                 ret = plat->init();
 184                 if (ret)
 185                         goto err;
 186         }
 187 
 188         /*
 189          * Claim and then map the memory regions.
 190          */
 191         for (i = 0; i < nr; i++) {
 192                 struct sa_subdev_info *subdev = &info->subdev[i];
 193                 struct resource *res;
 194 
 195                 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
 196                 if (!res)
 197                         break;
 198 
 199                 subdev->map.name = subdev->name;
 200                 sprintf(subdev->name, "%s-%d", plat->name, i);
 201                 subdev->plat = plat;
 202 
 203                 ret = sa1100_probe_subdev(subdev, res);
 204                 if (ret)
 205                         break;
 206         }
 207 
 208         info->num_subdev = i;
 209 
 210         /*
 211          * ENXIO is special.  It means we didn't find a chip when we probed.
 212          */
 213         if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
 214                 goto err;
 215 
 216         /*
 217          * If we found one device, don't bother with concat support.  If
 218          * we found multiple devices, use concat if we have it available,
 219          * otherwise fail.  Either way, it'll be called "sa1100".
 220          */
 221         if (info->num_subdev == 1) {
 222                 strcpy(info->subdev[0].name, plat->name);
 223                 info->mtd = info->subdev[0].mtd;
 224                 ret = 0;
 225         } else if (info->num_subdev > 1) {
 226                 struct mtd_info **cdev;
 227 
 228                 cdev = kmalloc_array(nr, sizeof(*cdev), GFP_KERNEL);
 229                 if (!cdev) {
 230                         ret = -ENOMEM;
 231                         goto err;
 232                 }
 233 
 234                 /*
 235                  * We detected multiple devices.  Concatenate them together.
 236                  */
 237                 for (i = 0; i < info->num_subdev; i++)
 238                         cdev[i] = info->subdev[i].mtd;
 239 
 240                 info->mtd = mtd_concat_create(cdev, info->num_subdev,
 241                                               plat->name);
 242                 kfree(cdev);
 243                 if (info->mtd == NULL) {
 244                         ret = -ENXIO;
 245                         goto err;
 246                 }
 247         }
 248         info->mtd->dev.parent = &pdev->dev;
 249 
 250         if (ret == 0)
 251                 return info;
 252 
 253  err:
 254         sa1100_destroy(info, plat);
 255  out:
 256         return ERR_PTR(ret);
 257 }
 258 
 259 static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
 260 
 261 static int sa1100_mtd_probe(struct platform_device *pdev)
 262 {
 263         struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
 264         struct sa_info *info;
 265         int err;
 266 
 267         if (!plat)
 268                 return -ENODEV;
 269 
 270         info = sa1100_setup_mtd(pdev, plat);
 271         if (IS_ERR(info)) {
 272                 err = PTR_ERR(info);
 273                 goto out;
 274         }
 275 
 276         /*
 277          * Partition selection stuff.
 278          */
 279         mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
 280                                   plat->nr_parts);
 281 
 282         platform_set_drvdata(pdev, info);
 283         err = 0;
 284 
 285  out:
 286         return err;
 287 }
 288 
 289 static int sa1100_mtd_remove(struct platform_device *pdev)
 290 {
 291         struct sa_info *info = platform_get_drvdata(pdev);
 292         struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
 293 
 294         sa1100_destroy(info, plat);
 295 
 296         return 0;
 297 }
 298 
 299 static struct platform_driver sa1100_mtd_driver = {
 300         .probe          = sa1100_mtd_probe,
 301         .remove         = sa1100_mtd_remove,
 302         .driver         = {
 303                 .name   = "sa1100-mtd",
 304         },
 305 };
 306 
 307 module_platform_driver(sa1100_mtd_driver);
 308 
 309 MODULE_AUTHOR("Nicolas Pitre");
 310 MODULE_DESCRIPTION("SA1100 CFI map driver");
 311 MODULE_LICENSE("GPL");
 312 MODULE_ALIAS("platform:sa1100-mtd");

/* [<][>][^][v][top][bottom][index][help] */