0001
0002
0003
0004
0005
0006 #include <linux/init.h>
0007 #include <linux/ioport.h>
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/mtd/mtd.h>
0011 #include <linux/slab.h>
0012 #include <linux/types.h>
0013
0014 #include <asm/addrspace.h>
0015 #include <asm/bootinfo.h>
0016 #include <asm/dec/ioasic_addrs.h>
0017 #include <asm/dec/kn02.h>
0018 #include <asm/dec/kn03.h>
0019 #include <asm/io.h>
0020 #include <asm/paccess.h>
0021
0022 #include "ms02-nv.h"
0023
0024
0025 static char version[] __initdata =
0026 "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";
0027
0028 MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
0029 MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
0030 MODULE_LICENSE("GPL");
0031
0032
0033
0034
0035
0036
0037
0038
0039 static ulong ms02nv_addrs[] __initdata = {
0040 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
0041 0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000,
0042 0x02000000, 0x01800000, 0x01000000, 0x00800000
0043 };
0044
0045 static const char ms02nv_name[] = "DEC MS02-NV NVRAM";
0046 static const char ms02nv_res_diag_ram[] = "Diagnostic RAM";
0047 static const char ms02nv_res_user_ram[] = "General-purpose RAM";
0048 static const char ms02nv_res_csr[] = "Control and status register";
0049
0050 static struct mtd_info *root_ms02nv_mtd;
0051
0052
0053 static int ms02nv_read(struct mtd_info *mtd, loff_t from,
0054 size_t len, size_t *retlen, u_char *buf)
0055 {
0056 struct ms02nv_private *mp = mtd->priv;
0057
0058 memcpy(buf, mp->uaddr + from, len);
0059 *retlen = len;
0060 return 0;
0061 }
0062
0063 static int ms02nv_write(struct mtd_info *mtd, loff_t to,
0064 size_t len, size_t *retlen, const u_char *buf)
0065 {
0066 struct ms02nv_private *mp = mtd->priv;
0067
0068 memcpy(mp->uaddr + to, buf, len);
0069 *retlen = len;
0070 return 0;
0071 }
0072
0073
0074 static inline uint ms02nv_probe_one(ulong addr)
0075 {
0076 ms02nv_uint *ms02nv_diagp;
0077 ms02nv_uint *ms02nv_magicp;
0078 uint ms02nv_diag;
0079 uint ms02nv_magic;
0080 size_t size;
0081
0082 int err;
0083
0084
0085
0086
0087
0088 ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
0089 ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
0090 err = get_dbe(ms02nv_magic, ms02nv_magicp);
0091 if (err)
0092 return 0;
0093 if (ms02nv_magic != MS02NV_ID)
0094 return 0;
0095
0096 ms02nv_diag = *ms02nv_diagp;
0097 size = (ms02nv_diag & MS02NV_DIAG_SIZE_MASK) << MS02NV_DIAG_SIZE_SHIFT;
0098 if (size > MS02NV_CSR)
0099 size = MS02NV_CSR;
0100
0101 return size;
0102 }
0103
0104 static int __init ms02nv_init_one(ulong addr)
0105 {
0106 struct mtd_info *mtd;
0107 struct ms02nv_private *mp;
0108 struct resource *mod_res;
0109 struct resource *diag_res;
0110 struct resource *user_res;
0111 struct resource *csr_res;
0112 ulong fixaddr;
0113 size_t size, fixsize;
0114
0115 static int version_printed;
0116
0117 int ret = -ENODEV;
0118
0119
0120 mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL);
0121 if (!mod_res)
0122 return -ENOMEM;
0123
0124 mod_res->name = ms02nv_name;
0125 mod_res->start = addr;
0126 mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
0127 mod_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
0128 if (request_resource(&iomem_resource, mod_res) < 0)
0129 goto err_out_mod_res;
0130
0131 size = ms02nv_probe_one(addr);
0132 if (!size)
0133 goto err_out_mod_res_rel;
0134
0135 if (!version_printed) {
0136 printk(KERN_INFO "%s", version);
0137 version_printed = 1;
0138 }
0139
0140 ret = -ENOMEM;
0141 mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
0142 if (!mtd)
0143 goto err_out_mod_res_rel;
0144 mp = kzalloc(sizeof(*mp), GFP_KERNEL);
0145 if (!mp)
0146 goto err_out_mtd;
0147
0148 mtd->priv = mp;
0149 mp->resource.module = mod_res;
0150
0151
0152 diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL);
0153 if (!diag_res)
0154 goto err_out_mp;
0155
0156 diag_res->name = ms02nv_res_diag_ram;
0157 diag_res->start = addr;
0158 diag_res->end = addr + MS02NV_RAM - 1;
0159 diag_res->flags = IORESOURCE_BUSY;
0160 request_resource(mod_res, diag_res);
0161
0162 mp->resource.diag_ram = diag_res;
0163
0164
0165 user_res = kzalloc(sizeof(*user_res), GFP_KERNEL);
0166 if (!user_res)
0167 goto err_out_diag_res;
0168
0169 user_res->name = ms02nv_res_user_ram;
0170 user_res->start = addr + MS02NV_RAM;
0171 user_res->end = addr + size - 1;
0172 user_res->flags = IORESOURCE_BUSY;
0173 request_resource(mod_res, user_res);
0174
0175 mp->resource.user_ram = user_res;
0176
0177
0178 csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL);
0179 if (!csr_res)
0180 goto err_out_user_res;
0181
0182 csr_res->name = ms02nv_res_csr;
0183 csr_res->start = addr + MS02NV_CSR;
0184 csr_res->end = addr + MS02NV_CSR + 3;
0185 csr_res->flags = IORESOURCE_BUSY;
0186 request_resource(mod_res, csr_res);
0187
0188 mp->resource.csr = csr_res;
0189
0190 mp->addr = phys_to_virt(addr);
0191 mp->size = size;
0192
0193
0194
0195
0196
0197 fixaddr = (addr + MS02NV_RAM + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
0198 fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1);
0199 mp->uaddr = phys_to_virt(fixaddr);
0200
0201 mtd->type = MTD_RAM;
0202 mtd->flags = MTD_CAP_RAM;
0203 mtd->size = fixsize;
0204 mtd->name = ms02nv_name;
0205 mtd->owner = THIS_MODULE;
0206 mtd->_read = ms02nv_read;
0207 mtd->_write = ms02nv_write;
0208 mtd->writesize = 1;
0209
0210 ret = -EIO;
0211 if (mtd_device_register(mtd, NULL, 0)) {
0212 printk(KERN_ERR
0213 "ms02-nv: Unable to register MTD device, aborting!\n");
0214 goto err_out_csr_res;
0215 }
0216
0217 printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n",
0218 mtd->index, ms02nv_name, addr, size >> 20);
0219
0220 mp->next = root_ms02nv_mtd;
0221 root_ms02nv_mtd = mtd;
0222
0223 return 0;
0224
0225
0226 err_out_csr_res:
0227 release_resource(csr_res);
0228 kfree(csr_res);
0229 err_out_user_res:
0230 release_resource(user_res);
0231 kfree(user_res);
0232 err_out_diag_res:
0233 release_resource(diag_res);
0234 kfree(diag_res);
0235 err_out_mp:
0236 kfree(mp);
0237 err_out_mtd:
0238 kfree(mtd);
0239 err_out_mod_res_rel:
0240 release_resource(mod_res);
0241 err_out_mod_res:
0242 kfree(mod_res);
0243 return ret;
0244 }
0245
0246 static void __exit ms02nv_remove_one(void)
0247 {
0248 struct mtd_info *mtd = root_ms02nv_mtd;
0249 struct ms02nv_private *mp = mtd->priv;
0250
0251 root_ms02nv_mtd = mp->next;
0252
0253 mtd_device_unregister(mtd);
0254
0255 release_resource(mp->resource.csr);
0256 kfree(mp->resource.csr);
0257 release_resource(mp->resource.user_ram);
0258 kfree(mp->resource.user_ram);
0259 release_resource(mp->resource.diag_ram);
0260 kfree(mp->resource.diag_ram);
0261 release_resource(mp->resource.module);
0262 kfree(mp->resource.module);
0263 kfree(mp);
0264 kfree(mtd);
0265 }
0266
0267
0268 static int __init ms02nv_init(void)
0269 {
0270 volatile u32 *csr;
0271 uint stride = 0;
0272 int count = 0;
0273 int i;
0274
0275 switch (mips_machtype) {
0276 case MACH_DS5000_200:
0277 csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR);
0278 if (*csr & KN02_CSR_BNK32M)
0279 stride = 2;
0280 break;
0281 case MACH_DS5000_2X0:
0282 case MACH_DS5900:
0283 csr = (volatile u32 *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR);
0284 if (*csr & KN03_MCR_BNK32M)
0285 stride = 2;
0286 break;
0287 default:
0288 return -ENODEV;
0289 }
0290
0291 for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)
0292 if (!ms02nv_init_one(ms02nv_addrs[i] << stride))
0293 count++;
0294
0295 return (count > 0) ? 0 : -ENODEV;
0296 }
0297
0298 static void __exit ms02nv_cleanup(void)
0299 {
0300 while (root_ms02nv_mtd)
0301 ms02nv_remove_one();
0302 }
0303
0304
0305 module_init(ms02nv_init);
0306 module_exit(ms02nv_cleanup);