ms02-nv.c revision 3c3c10bba1e4ccb75b41442e45c1a072f6cded19
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	if (from + len > mtd->size)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(buf, mp->uaddr + from, len);
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*retlen = len;
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ms02nv_write(struct mtd_info *mtd, loff_t to,
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			size_t len, size_t *retlen, const u_char *buf)
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp = mtd->priv;
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (to + len > mtd->size)
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(mp->uaddr + to, buf, len);
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*retlen = len;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline uint ms02nv_probe_one(ulong addr)
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_uint *ms02nv_diagp;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_uint *ms02nv_magicp;
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint ms02nv_diag;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint ms02nv_magic;
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t size;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The firmware writes MS02NV_ID at MS02NV_MAGIC and also
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * a diagnostic status at MS02NV_DIAG.
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
100af2c80e926ad5335d00a8d507928aff4e8ff1877?	ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
101af2c80e926ad5335d00a8d507928aff4e8ff1877?	ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = get_dbe(ms02nv_magic, ms02nv_magicp);
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ms02nv_magic != MS02NV_ID)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ms02nv_diag = *ms02nv_diagp;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = (ms02nv_diag & MS02NV_DIAG_SIZE_MASK) << MS02NV_DIAG_SIZE_SHIFT;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (size > MS02NV_CSR)
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		size = MS02NV_CSR;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return size;
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ms02nv_init_one(ulong addr)
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_info *mtd;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *mod_res;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *diag_res;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *user_res;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct resource *csr_res;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ulong fixaddr;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t size, fixsize;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static int version_printed;
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = -ENODEV;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The module decodes 8MiB of address space. */
13295b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mod_res = kzalloc(sizeof(*mod_res), GFP_KERNEL);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mod_res)
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->name = ms02nv_name;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->start = addr;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->end = addr + MS02NV_SLOT_SIZE - 1;
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_resource(&iomem_resource, mod_res) < 0)
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size = ms02nv_probe_one(addr);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!size)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res_rel;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!version_printed) {
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s", version);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		version_printed = 1;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = -ENOMEM;
15395b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mtd)
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mod_res_rel;
15695b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	mp = kzalloc(sizeof(*mp), GFP_KERNEL);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!mp)
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mtd;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->priv = mp;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.module = mod_res;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Firmware's diagnostic NVRAM area. */
16495b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	diag_res = kzalloc(sizeof(*diag_res), GFP_KERNEL);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!diag_res)
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_mp;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->name = ms02nv_res_diag_ram;
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->start = addr;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->end = addr + MS02NV_RAM - 1;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	diag_res->flags = IORESOURCE_BUSY;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, diag_res);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.diag_ram = diag_res;
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* User-available general-purpose NVRAM area. */
17795b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	user_res = kzalloc(sizeof(*user_res), GFP_KERNEL);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!user_res)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_diag_res;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->name = ms02nv_res_user_ram;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->start = addr + MS02NV_RAM;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->end = addr + size - 1;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	user_res->flags = IORESOURCE_BUSY;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, user_res);
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.user_ram = user_res;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Control and status register. */
19095b93a0cd46682c6d9e8eea803fda510cb6b863aBurman Yan	csr_res = kzalloc(sizeof(*csr_res), GFP_KERNEL);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!csr_res)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_user_res;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->name = ms02nv_res_csr;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->start = addr + MS02NV_CSR;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->end = addr + MS02NV_CSR + 3;
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	csr_res->flags = IORESOURCE_BUSY;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	request_resource(mod_res, csr_res);
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->resource.csr = csr_res;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->addr = phys_to_virt(addr);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->size = size;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Hide the firmware's diagnostic area.  It may get destroyed
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * upon a reboot.  Take paging into account for mapping support.
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	fixaddr = (addr + MS02NV_RAM + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->uaddr = phys_to_virt(fixaddr);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21321c8db9eff95260e543535dfc6f27164c4c0c0ffDavid Woodhouse	mtd->type = MTD_RAM;
214e1219724be193519f20743f5500bd1eff11890d0Joern Engel	mtd->flags = MTD_CAP_RAM;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->size = fixsize;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->name = (char *)ms02nv_name;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mtd->owner = THIS_MODULE;
2183c3c10bba1e4ccb75b41442e45c1a072f6cded19Artem Bityutskiy	mtd->_read = ms02nv_read;
2193c3c10bba1e4ccb75b41442e45c1a072f6cded19Artem Bityutskiy	mtd->_write = ms02nv_write;
22017ffc7ba6d7ea68b8d5f55a5ca1b87163e69720dArtem B. Bityutskiy	mtd->writesize = 1;
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = -EIO;
223ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	if (mtd_device_register(mtd, NULL, 0)) {
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			"ms02-nv: Unable to register MTD device, aborting!\n");
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto err_out_csr_res;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
229af2c80e926ad5335d00a8d507928aff4e8ff1877?	printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n",
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mtd->index, ms02nv_name, addr, size >> 20);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mp->next = root_ms02nv_mtd;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	root_ms02nv_mtd = mtd;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_csr_res:
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(csr_res);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(csr_res);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_user_res:
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(user_res);
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(user_res);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_diag_res:
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(diag_res);
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(diag_res);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mp:
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mtd:
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mtd);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mod_res_rel:
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mod_res);
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserr_out_mod_res:
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mod_res);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ms02nv_remove_one(void)
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct mtd_info *mtd = root_ms02nv_mtd;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct ms02nv_private *mp = mtd->priv;
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	root_ms02nv_mtd = mp->next;
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
265ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles	mtd_device_unregister(mtd);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.csr);
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.csr);
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.user_ram);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.user_ram);
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.diag_ram);
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.diag_ram);
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_resource(mp->resource.module);
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp->resource.module);
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mp);
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(mtd);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ms02nv_init(void)
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	volatile u32 *csr;
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	uint stride = 0;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int count = 0;
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (mips_machtype) {
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5000_200:
2893eb8ceac486ed9b6eceed098423f1ca6b180ec9dMaciej W. Rozycki		csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR);
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*csr & KN02_CSR_BNK32M)
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			stride = 2;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5000_2X0:
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MACH_DS5900:
2953eb8ceac486ed9b6eceed098423f1ca6b180ec9dMaciej W. Rozycki		csr = (volatile u32 *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR);
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*csr & KN03_MCR_BNK32M)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			stride = 2;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30487d10f3c7954d143e509a2af2bca2a27aeb3114dTobias Klauser	for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!ms02nv_init_one(ms02nv_addrs[i] << stride))
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			count++;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return (count > 0) ? 0 : -ENODEV;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ms02nv_cleanup(void)
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (root_ms02nv_mtd)
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ms02nv_remove_one();
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ms02nv_init);
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ms02nv_cleanup);
320