11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ######################################################################
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Tempustech VMAX SBC301 MTD Driver.
469f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   The VMAx 301 is a SBC based on . It
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   comes with three builtin AMD 29F016B flash chips and a socket for SRAM or
769f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner   more flash. Each unit has it's own 8k mapping into a settable region
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   (0xD8000). There are two 8k mappings for each MTD, the first is always set
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   to the lower 8k of the device the second is paged. Writing a 16 bit page
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   value to anywhere in the first 8k will cause the second 8k to page around.
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1269f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner   To boot the device a bios extension must be installed into the first 8k
1369f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner   of flash that is smart enough to copy itself down, page in the rest of
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   itself and begin executing.
1569f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   ##################################################################### */
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/spinlock.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/map.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WINDOW_START 0xd8000
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WINDOW_LENGTH 0x2000
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WINDOW_SHIFT 25
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define WINDOW_MASK 0x1FFF
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Actually we could use two spinlocks, but we'd have to have
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   more private space in the struct map_info. We lose a little
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   performance like this, but we'd probably lose more by having
3669f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner   the extra indirection from having one of the map->map_priv
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   fields pointing to yet another private struct.
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DEFINE_SPINLOCK(vmax301_spin);
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __vmax301_page(struct map_info *map, unsigned long page)
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writew(page, map->map_priv_2 - WINDOW_LENGTH);
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	map->map_priv_1 = page;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void vmax301_page(struct map_info *map,
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  unsigned long ofs)
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long page = (ofs >> WINDOW_SHIFT);
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (map->map_priv_1 != page)
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		__vmax301_page(map, page);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic map_word vmax301_read8(struct map_info *map, unsigned long ofs)
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	map_word ret;
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&vmax301_spin);
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmax301_page(map, ofs);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&vmax301_spin);
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while(len) {
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned long thislen = len;
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock(&vmax301_spin);
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmax301_page(map, from);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_fromio(to, map->map_priv_2 + from, thislen);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock(&vmax301_spin);
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		to += thislen;
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		from += thislen;
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= thislen;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock(&vmax301_spin);
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmax301_page(map, adr);
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock(&vmax301_spin);
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while(len) {
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned long thislen = len;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock(&vmax301_spin);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmax301_page(map, to);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy_toio(map->map_priv_2 + to, from, thislen);
9969f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner		spin_unlock(&vmax301_spin);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		to += thislen;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		from += thislen;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len -= thislen;
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct map_info vmax_map[2] = {
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name = "VMAX301 Internal Flash",
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.phys = NO_XIP,
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.size = 3*2*1024*1024,
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.bankwidth = 1,
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.read = vmax301_read8,
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.copy_from = vmax301_copy_from,
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.write = vmax301_write8,
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.copy_to = vmax301_copy_to,
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.map_priv_1 = WINDOW_START + WINDOW_LENGTH,
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.map_priv_2 = 0xFFFFFFFF
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	},
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.name = "VMAX301 Socket",
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.phys = NO_XIP,
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.size = 0,
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.bankwidth = 1,
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.read = vmax301_read8,
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.copy_from = vmax301_copy_from,
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.write = vmax301_write8,
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.copy_to = vmax301_copy_to,
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		.map_priv_2 = 0xFFFFFFFF
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *vmax_mtd[2] = {NULL, NULL};
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_vmax301(void)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
13869f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<2; i++) {
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (vmax_mtd[i]) {
141ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles			mtd_device_unregister(vmax_mtd[i]);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			map_destroy(vmax_mtd[i]);
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481aed165a602b80428bbdf17478c629169d96eda1Dmitri Vorobievstatic int __init init_vmax301(void)
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long iomapadr;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Print out our little header..
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START,
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       WINDOW_START+4*WINDOW_LENGTH);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!iomapadr) {
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("Failed to ioremap memory region\n");
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Put the address in the map's private data area.
16269f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner	   We store the actual MTD IO address rather than the
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   address of the first half, because it's used more
16469f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner	   often.
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*/
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
16869f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<2; i++) {
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!vmax_mtd[i])
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!vmax_mtd[i])
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!vmax_mtd[i])
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (vmax_mtd[i]) {
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			vmax_mtd[i]->owner = THIS_MODULE;
179ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles			mtd_device_register(vmax_mtd[i], NULL, 0);
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
183b5ac5d7edb64b3ca1a489b30d95d62492a1f0b80Petri T. Koistinen	if (!vmax_mtd[0] && !vmax_mtd[1]) {
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		iounmap((void *)iomapadr);
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENXIO;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_vmax301);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_vmax301);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board");
197