307 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2001 Maciej W. Rozycki4 */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 25static char version[] __initdata =26 "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";27 28MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");29MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");30MODULE_LICENSE("GPL");31 32 33/*34 * Addresses we probe for an MS02-NV at. Modules may be located35 * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB36 * boundary within a 0MiB up to 448MiB range. We don't support a module37 * at 0MiB, though.38 */39static ulong ms02nv_addrs[] __initdata = {40 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,41 0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000,42 0x02000000, 0x01800000, 0x01000000, 0x0080000043};44 45static const char ms02nv_name[] = "DEC MS02-NV NVRAM";46static const char ms02nv_res_diag_ram[] = "Diagnostic RAM";47static const char ms02nv_res_user_ram[] = "General-purpose RAM";48static const char ms02nv_res_csr[] = "Control and status register";49 50static struct mtd_info *root_ms02nv_mtd;51 52 53static 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 63static 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 74static 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 also86 * 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 104static 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 destroyed195 * 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_ERR213 "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 226err_out_csr_res:227 release_resource(csr_res);228 kfree(csr_res);229err_out_user_res:230 release_resource(user_res);231 kfree(user_res);232err_out_diag_res:233 release_resource(diag_res);234 kfree(diag_res);235err_out_mp:236 kfree(mp);237err_out_mtd:238 kfree(mtd);239err_out_mod_res_rel:240 release_resource(mod_res);241err_out_mod_res:242 kfree(mod_res);243 return ret;244}245 246static 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 268static 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 }290 291 for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)292 if (!ms02nv_init_one(ms02nv_addrs[i] << stride))293 count++;294 295 return (count > 0) ? 0 : -ENODEV;296}297 298static void __exit ms02nv_cleanup(void)299{300 while (root_ms02nv_mtd)301 ms02nv_remove_one();302}303 304 305module_init(ms02nv_init);306module_exit(ms02nv_cleanup);307