11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Copyright (c) 2001 Maciej W. Rozycki
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	This program is free software; you can redistribute it and/or
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	modify it under the terms of the GNU General Public License
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	as published by the Free Software Foundation; either version
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	2 of the License, or (at your option) any later version.
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/addrspace.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/bootinfo.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dec/ioasic_addrs.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dec/kn02.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dec/kn03.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/paccess.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "ms02-nv.h"
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char version[] __initdata =
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	"ms02-nv.c: v.1.0.0  13 Aug 2001  Maciej W. Rozycki.\n";
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Addresses we probe for an MS02-NV at.  Modules may be located
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * boundary within a 0MiB up to 448MiB range.  We don't support a module
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * at 0MiB, though.
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ulong ms02nv_addrs[] __initdata = {
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000,
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x02000000, 0x01800000, 0x01000000, 0x00800000
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char ms02nv_name[] = "DEC MS02-NV NVRAM";
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char ms02nv_res_diag_ram[] = "Diagnostic RAM";
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char ms02nv_res_user_ram[] = "General-purpose RAM";
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char ms02nv_res_csr[] = "Control and status register";
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *root_ms02nv_mtd;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ms02nv_read(struct mtd_info *mtd, loff_t from,
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t len, size_t *retlen, u_char *buf)
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp = mtd->priv;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(buf, mp->uaddr + from, len);
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*retlen = len;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ms02nv_write(struct mtd_info *mtd, loff_t to,
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t len, size_t *retlen, const u_char *buf)
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp = mtd->priv;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(mp->uaddr + to, buf, len);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*retlen = len;
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline uint ms02nv_probe_one(ulong addr)
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_uint *ms02nv_diagp;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_uint *ms02nv_magicp;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint ms02nv_diag;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint ms02nv_magic;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t size;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The firmware writes MS02NV_ID at MS02NV_MAGIC and also
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * a diagnostic status at MS02NV_DIAG.
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
92af2c80e926ad5335d00a8d507928aff4e8ff1877?	ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
93af2c80e926ad5335d00a8d507928aff4e8ff1877?	ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = get_dbe(ms02nv_magic, ms02nv_magicp);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ms02nv_magic != MS02NV_ID)
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_diag = *ms02nv_diagp;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = (ms02nv_diag & MS02NV_DIAG_SIZE_MASK) << MS02NV_DIAG_SIZE_SHIFT;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (size > MS02NV_CSR)
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size = MS02NV_CSR;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return size;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ms02nv_init_one(ulong addr)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_info *mtd;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *mod_res;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *diag_res;
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *user_res;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *csr_res;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong fixaddr;
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t size, fixsize;
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static int version_printed;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -ENODEV;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The module decodes 8MiB of address space. */
12495b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mod_res)
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->name = ms02nv_name;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->start = addr;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_resource(&iomem_resource, mod_res) < 0)
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = ms02nv_probe_one(addr);
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!size)
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res_rel;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!version_printed) {
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s", version);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		version_printed = 1;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = -ENOMEM;
14595b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mtd)
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res_rel;
14895b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mp = kzalloc(sizeof(*mp), GFP_KERNEL);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mp)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mtd;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->priv = mp;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.module = mod_res;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Firmware's diagnostic NVRAM area. */
15695b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!diag_res)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mp;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->name = ms02nv_res_diag_ram;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->start = addr;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->end = addr + MS02NV_RAM - 1;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->flags = IORESOURCE_BUSY;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, diag_res);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.diag_ram = diag_res;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* User-available general-purpose NVRAM area. */
16995b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	user_res = kzalloc(sizeof(*user_res), GFP_KERNEL);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!user_res)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_diag_res;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->name = ms02nv_res_user_ram;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->start = addr + MS02NV_RAM;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->end = addr + size - 1;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->flags = IORESOURCE_BUSY;
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, user_res);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.user_ram = user_res;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Control and status register. */
18295b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL);
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!csr_res)
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_user_res;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->name = ms02nv_res_csr;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->start = addr + MS02NV_CSR;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->end = addr + MS02NV_CSR + 3;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->flags = IORESOURCE_BUSY;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, csr_res);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.csr = csr_res;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->addr = phys_to_virt(addr);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->size = size;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Hide the firmware's diagnostic area.  It may get destroyed
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * upon a reboot.  Take paging into account for mapping support.
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	fixaddr = (addr + MS02NV_RAM + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->uaddr = phys_to_virt(fixaddr);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20521c8db9eff95260e543535dfc6f27164c4c0c0ffDavid Woodhouse	mtd->type = MTD_RAM;
206e1219724be193519f20743f5500bd1eff11890d0Joern Engel	mtd->flags = MTD_CAP_RAM;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->size = fixsize;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->name = (char *)ms02nv_name;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->owner = THIS_MODULE;
2103c3c10bba1e4ccb75b41442e45c1a072f6cded19Artem Bityutskiy	mtd->_read = ms02nv_read;
2113c3c10bba1e4ccb75b41442e45c1a072f6cded19Artem Bityutskiy	mtd->_write = ms02nv_write;
21217ffc7ba6d7ea68b8d5f55a5ca1b87163e69720dArtem B. Bityutskiy	mtd->writesize = 1;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = -EIO;
215ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	if (mtd_device_register(mtd, NULL, 0)) {
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"ms02-nv: Unable to register MTD device, aborting!\n");
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_csr_res;
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
221af2c80e926ad5335d00a8d507928aff4e8ff1877?	printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n",
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mtd->index, ms02nv_name, addr, size >> 20);
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->next = root_ms02nv_mtd;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	root_ms02nv_mtd = mtd;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_csr_res:
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(csr_res);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(csr_res);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_user_res:
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(user_res);
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(user_res);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_diag_res:
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(diag_res);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(diag_res);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mp:
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mtd:
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mtd);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mod_res_rel:
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mod_res);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mod_res:
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mod_res);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ms02nv_remove_one(void)
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_info *mtd = root_ms02nv_mtd;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp = mtd->priv;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	root_ms02nv_mtd = mp->next;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
257ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	mtd_device_unregister(mtd);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.csr);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.csr);
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.user_ram);
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.user_ram);
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.diag_ram);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.diag_ram);
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.module);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.module);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mtd);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ms02nv_init(void)
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	volatile u32 *csr;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint stride = 0;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int count = 0;
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (mips_machtype) {
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5000_200:
2813eb8ceac486ed9b6eceed098423f1ca6b180ec9dMaciej W. Rozycki		csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR);
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*csr & KN02_CSR_BNK32M)
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			stride = 2;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5000_2X0:
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5900:
2873eb8ceac486ed9b6eceed098423f1ca6b180ec9dMaciej W. Rozycki		csr = (volatile u32 *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR);
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*csr & KN03_MCR_BNK32M)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			stride = 2;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29687d10f3c7954d143e509a2af2bca2a27aeb3114dTobias Klauser	for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ms02nv_init_one(ms02nv_addrs[i] << stride))
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			count++;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (count > 0) ? 0 : -ENODEV;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ms02nv_cleanup(void)
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (root_ms02nv_mtd)
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ms02nv_remove_one();
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ms02nv_init);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ms02nv_cleanup);
312