root/drivers/mtd/devices/ms02-nv.c

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

DEFINITIONS

This source file includes following definitions.
  1. ms02nv_read
  2. ms02nv_write
  3. ms02nv_probe_one
  4. ms02nv_init_one
  5. ms02nv_remove_one
  6. ms02nv_init
  7. ms02nv_cleanup

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *      Copyright (c) 2001 Maciej W. Rozycki
   4  */
   5 
   6 #include <linux/init.h>
   7 #include <linux/ioport.h>
   8 #include <linux/kernel.h>
   9 #include <linux/module.h>
  10 #include <linux/mtd/mtd.h>
  11 #include <linux/slab.h>
  12 #include <linux/types.h>
  13 
  14 #include <asm/addrspace.h>
  15 #include <asm/bootinfo.h>
  16 #include <asm/dec/ioasic_addrs.h>
  17 #include <asm/dec/kn02.h>
  18 #include <asm/dec/kn03.h>
  19 #include <asm/io.h>
  20 #include <asm/paccess.h>
  21 
  22 #include "ms02-nv.h"
  23 
  24 
  25 static char version[] __initdata =
  26         "ms02-nv.c: v.1.0.0  13 Aug 2001  Maciej W. Rozycki.\n";
  27 
  28 MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
  29 MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
  30 MODULE_LICENSE("GPL");
  31 
  32 
  33 /*
  34  * Addresses we probe for an MS02-NV at.  Modules may be located
  35  * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
  36  * boundary within a 0MiB up to 448MiB range.  We don't support a module
  37  * at 0MiB, though.
  38  */
  39 static ulong ms02nv_addrs[] __initdata = {
  40         0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
  41         0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000,
  42         0x02000000, 0x01800000, 0x01000000, 0x00800000
  43 };
  44 
  45 static const char ms02nv_name[] = "DEC MS02-NV NVRAM";
  46 static const char ms02nv_res_diag_ram[] = "Diagnostic RAM";
  47 static const char ms02nv_res_user_ram[] = "General-purpose RAM";
  48 static const char ms02nv_res_csr[] = "Control and status register";
  49 
  50 static struct mtd_info *root_ms02nv_mtd;
  51 
  52 
  53 static int ms02nv_read(struct mtd_info *mtd, loff_t from,
  54                         size_t len, size_t *retlen, u_char *buf)
  55 {
  56         struct ms02nv_private *mp = mtd->priv;
  57 
  58         memcpy(buf, mp->uaddr + from, len);
  59         *retlen = len;
  60         return 0;
  61 }
  62 
  63 static int ms02nv_write(struct mtd_info *mtd, loff_t to,
  64                         size_t len, size_t *retlen, const u_char *buf)
  65 {
  66         struct ms02nv_private *mp = mtd->priv;
  67 
  68         memcpy(mp->uaddr + to, buf, len);
  69         *retlen = len;
  70         return 0;
  71 }
  72 
  73 
  74 static inline uint ms02nv_probe_one(ulong addr)
  75 {
  76         ms02nv_uint *ms02nv_diagp;
  77         ms02nv_uint *ms02nv_magicp;
  78         uint ms02nv_diag;
  79         uint ms02nv_magic;
  80         size_t size;
  81 
  82         int err;
  83 
  84         /*
  85          * The firmware writes MS02NV_ID at MS02NV_MAGIC and also
  86          * a diagnostic status at MS02NV_DIAG.
  87          */
  88         ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
  89         ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
  90         err = get_dbe(ms02nv_magic, ms02nv_magicp);
  91         if (err)
  92                 return 0;
  93         if (ms02nv_magic != MS02NV_ID)
  94                 return 0;
  95 
  96         ms02nv_diag = *ms02nv_diagp;
  97         size = (ms02nv_diag & MS02NV_DIAG_SIZE_MASK) << MS02NV_DIAG_SIZE_SHIFT;
  98         if (size > MS02NV_CSR)
  99                 size = MS02NV_CSR;
 100 
 101         return size;
 102 }
 103 
 104 static int __init ms02nv_init_one(ulong addr)
 105 {
 106         struct mtd_info *mtd;
 107         struct ms02nv_private *mp;
 108         struct resource *mod_res;
 109         struct resource *diag_res;
 110         struct resource *user_res;
 111         struct resource *csr_res;
 112         ulong fixaddr;
 113         size_t size, fixsize;
 114 
 115         static int version_printed;
 116 
 117         int ret = -ENODEV;
 118 
 119         /* The module decodes 8MiB of address space. */
 120         mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL);
 121         if (!mod_res)
 122                 return -ENOMEM;
 123 
 124         mod_res->name = ms02nv_name;
 125         mod_res->start = addr;
 126         mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
 127         mod_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
 128         if (request_resource(&iomem_resource, mod_res) < 0)
 129                 goto err_out_mod_res;
 130 
 131         size = ms02nv_probe_one(addr);
 132         if (!size)
 133                 goto err_out_mod_res_rel;
 134 
 135         if (!version_printed) {
 136                 printk(KERN_INFO "%s", version);
 137                 version_printed = 1;
 138         }
 139 
 140         ret = -ENOMEM;
 141         mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
 142         if (!mtd)
 143                 goto err_out_mod_res_rel;
 144         mp = kzalloc(sizeof(*mp), GFP_KERNEL);
 145         if (!mp)
 146                 goto err_out_mtd;
 147 
 148         mtd->priv = mp;
 149         mp->resource.module = mod_res;
 150 
 151         /* Firmware's diagnostic NVRAM area. */
 152         diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL);
 153         if (!diag_res)
 154                 goto err_out_mp;
 155 
 156         diag_res->name = ms02nv_res_diag_ram;
 157         diag_res->start = addr;
 158         diag_res->end = addr + MS02NV_RAM - 1;
 159         diag_res->flags = IORESOURCE_BUSY;
 160         request_resource(mod_res, diag_res);
 161 
 162         mp->resource.diag_ram = diag_res;
 163 
 164         /* User-available general-purpose NVRAM area. */
 165         user_res = kzalloc(sizeof(*user_res), GFP_KERNEL);
 166         if (!user_res)
 167                 goto err_out_diag_res;
 168 
 169         user_res->name = ms02nv_res_user_ram;
 170         user_res->start = addr + MS02NV_RAM;
 171         user_res->end = addr + size - 1;
 172         user_res->flags = IORESOURCE_BUSY;
 173         request_resource(mod_res, user_res);
 174 
 175         mp->resource.user_ram = user_res;
 176 
 177         /* Control and status register. */
 178         csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL);
 179         if (!csr_res)
 180                 goto err_out_user_res;
 181 
 182         csr_res->name = ms02nv_res_csr;
 183         csr_res->start = addr + MS02NV_CSR;
 184         csr_res->end = addr + MS02NV_CSR + 3;
 185         csr_res->flags = IORESOURCE_BUSY;
 186         request_resource(mod_res, csr_res);
 187 
 188         mp->resource.csr = csr_res;
 189 
 190         mp->addr = phys_to_virt(addr);
 191         mp->size = size;
 192 
 193         /*
 194          * Hide the firmware's diagnostic area.  It may get destroyed
 195          * upon a reboot.  Take paging into account for mapping support.
 196          */
 197         fixaddr = (addr + MS02NV_RAM + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
 198         fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1);
 199         mp->uaddr = phys_to_virt(fixaddr);
 200 
 201         mtd->type = MTD_RAM;
 202         mtd->flags = MTD_CAP_RAM;
 203         mtd->size = fixsize;
 204         mtd->name = ms02nv_name;
 205         mtd->owner = THIS_MODULE;
 206         mtd->_read = ms02nv_read;
 207         mtd->_write = ms02nv_write;
 208         mtd->writesize = 1;
 209 
 210         ret = -EIO;
 211         if (mtd_device_register(mtd, NULL, 0)) {
 212                 printk(KERN_ERR
 213                         "ms02-nv: Unable to register MTD device, aborting!\n");
 214                 goto err_out_csr_res;
 215         }
 216 
 217         printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n",
 218                 mtd->index, ms02nv_name, addr, size >> 20);
 219 
 220         mp->next = root_ms02nv_mtd;
 221         root_ms02nv_mtd = mtd;
 222 
 223         return 0;
 224 
 225 
 226 err_out_csr_res:
 227         release_resource(csr_res);
 228         kfree(csr_res);
 229 err_out_user_res:
 230         release_resource(user_res);
 231         kfree(user_res);
 232 err_out_diag_res:
 233         release_resource(diag_res);
 234         kfree(diag_res);
 235 err_out_mp:
 236         kfree(mp);
 237 err_out_mtd:
 238         kfree(mtd);
 239 err_out_mod_res_rel:
 240         release_resource(mod_res);
 241 err_out_mod_res:
 242         kfree(mod_res);
 243         return ret;
 244 }
 245 
 246 static void __exit ms02nv_remove_one(void)
 247 {
 248         struct mtd_info *mtd = root_ms02nv_mtd;
 249         struct ms02nv_private *mp = mtd->priv;
 250 
 251         root_ms02nv_mtd = mp->next;
 252 
 253         mtd_device_unregister(mtd);
 254 
 255         release_resource(mp->resource.csr);
 256         kfree(mp->resource.csr);
 257         release_resource(mp->resource.user_ram);
 258         kfree(mp->resource.user_ram);
 259         release_resource(mp->resource.diag_ram);
 260         kfree(mp->resource.diag_ram);
 261         release_resource(mp->resource.module);
 262         kfree(mp->resource.module);
 263         kfree(mp);
 264         kfree(mtd);
 265 }
 266 
 267 
 268 static int __init ms02nv_init(void)
 269 {
 270         volatile u32 *csr;
 271         uint stride = 0;
 272         int count = 0;
 273         int i;
 274 
 275         switch (mips_machtype) {
 276         case MACH_DS5000_200:
 277                 csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR);
 278                 if (*csr & KN02_CSR_BNK32M)
 279                         stride = 2;
 280                 break;
 281         case MACH_DS5000_2X0:
 282         case MACH_DS5900:
 283                 csr = (volatile u32 *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR);
 284                 if (*csr & KN03_MCR_BNK32M)
 285                         stride = 2;
 286                 break;
 287         default:
 288                 return -ENODEV;
 289                 break;
 290         }
 291 
 292         for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)
 293                 if (!ms02nv_init_one(ms02nv_addrs[i] << stride))
 294                         count++;
 295 
 296         return (count > 0) ? 0 : -ENODEV;
 297 }
 298 
 299 static void __exit ms02nv_cleanup(void)
 300 {
 301         while (root_ms02nv_mtd)
 302                 ms02nv_remove_one();
 303 }
 304 
 305 
 306 module_init(ms02nv_init);
 307 module_exit(ms02nv_cleanup);

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