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