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