11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds * MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip)
31da177e4SLinus Torvalds *
42f82af08SNicolas Pitre * (C) 2000 Nicolas Pitre <nico@fluxnic.net>
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * This code is GPL
71da177e4SLinus Torvalds */
81da177e4SLinus Torvalds #include <linux/module.h>
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/kernel.h>
111da177e4SLinus Torvalds #include <linux/init.h>
121da177e4SLinus Torvalds #include <linux/delay.h>
134e57b681STim Schmielau #include <linux/slab.h>
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
161da177e4SLinus Torvalds #include <linux/mtd/map.h>
171da177e4SLinus Torvalds #include <linux/mtd/partitions.h>
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds #include <asm/io.h>
201da177e4SLinus Torvalds #include <asm/hardware/dec21285.h>
211da177e4SLinus Torvalds #include <asm/mach-types.h>
221da177e4SLinus Torvalds
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds static struct mtd_info *dc21285_mtd;
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds #ifdef CONFIG_ARCH_NETWINDER
271da177e4SLinus Torvalds /*
281da177e4SLinus Torvalds * This is really ugly, but it seams to be the only
291da177e4SLinus Torvalds * realiable way to do it, as the cpld state machine
301da177e4SLinus Torvalds * is unpredictible. So we have a 25us penalty per
311da177e4SLinus Torvalds * write access.
321da177e4SLinus Torvalds */
nw_en_write(void)331da177e4SLinus Torvalds static void nw_en_write(void)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds unsigned long flags;
361da177e4SLinus Torvalds
371da177e4SLinus Torvalds /*
381da177e4SLinus Torvalds * we want to write a bit pattern XXX1 to Xilinx to enable
391da177e4SLinus Torvalds * the write gate, which will be open for about the next 2ms.
401da177e4SLinus Torvalds */
41e5babdf9SUwe Kleine-König raw_spin_lock_irqsave(&nw_gpio_lock, flags);
4270d13e08SRussell King nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
43e5babdf9SUwe Kleine-König raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
441da177e4SLinus Torvalds
451da177e4SLinus Torvalds /*
461da177e4SLinus Torvalds * let the ISA bus to catch on...
471da177e4SLinus Torvalds */
481da177e4SLinus Torvalds udelay(25);
491da177e4SLinus Torvalds }
501da177e4SLinus Torvalds #else
511da177e4SLinus Torvalds #define nw_en_write() do { } while (0)
521da177e4SLinus Torvalds #endif
531da177e4SLinus Torvalds
dc21285_read8(struct map_info * map,unsigned long ofs)541da177e4SLinus Torvalds static map_word dc21285_read8(struct map_info *map, unsigned long ofs)
551da177e4SLinus Torvalds {
561da177e4SLinus Torvalds map_word val;
571da177e4SLinus Torvalds val.x[0] = *(uint8_t*)(map->virt + ofs);
581da177e4SLinus Torvalds return val;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds
dc21285_read16(struct map_info * map,unsigned long ofs)611da177e4SLinus Torvalds static map_word dc21285_read16(struct map_info *map, unsigned long ofs)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds map_word val;
641da177e4SLinus Torvalds val.x[0] = *(uint16_t*)(map->virt + ofs);
651da177e4SLinus Torvalds return val;
661da177e4SLinus Torvalds }
671da177e4SLinus Torvalds
dc21285_read32(struct map_info * map,unsigned long ofs)681da177e4SLinus Torvalds static map_word dc21285_read32(struct map_info *map, unsigned long ofs)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds map_word val;
711da177e4SLinus Torvalds val.x[0] = *(uint32_t*)(map->virt + ofs);
721da177e4SLinus Torvalds return val;
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds
dc21285_copy_from(struct map_info * map,void * to,unsigned long from,ssize_t len)751da177e4SLinus Torvalds static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
761da177e4SLinus Torvalds {
771da177e4SLinus Torvalds memcpy(to, (void*)(map->virt + from), len);
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds
dc21285_write8(struct map_info * map,const map_word d,unsigned long adr)801da177e4SLinus Torvalds static void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr)
811da177e4SLinus Torvalds {
821da177e4SLinus Torvalds if (machine_is_netwinder())
831da177e4SLinus Torvalds nw_en_write();
841da177e4SLinus Torvalds *CSR_ROMWRITEREG = adr & 3;
851da177e4SLinus Torvalds adr &= ~3;
861da177e4SLinus Torvalds *(uint8_t*)(map->virt + adr) = d.x[0];
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds
dc21285_write16(struct map_info * map,const map_word d,unsigned long adr)891da177e4SLinus Torvalds static void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds if (machine_is_netwinder())
921da177e4SLinus Torvalds nw_en_write();
931da177e4SLinus Torvalds *CSR_ROMWRITEREG = adr & 3;
941da177e4SLinus Torvalds adr &= ~3;
951da177e4SLinus Torvalds *(uint16_t*)(map->virt + adr) = d.x[0];
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds
dc21285_write32(struct map_info * map,const map_word d,unsigned long adr)981da177e4SLinus Torvalds static void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds if (machine_is_netwinder())
1011da177e4SLinus Torvalds nw_en_write();
1021da177e4SLinus Torvalds *(uint32_t*)(map->virt + adr) = d.x[0];
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds
dc21285_copy_to_32(struct map_info * map,unsigned long to,const void * from,ssize_t len)1051da177e4SLinus Torvalds static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len)
1061da177e4SLinus Torvalds {
1071da177e4SLinus Torvalds while (len > 0) {
1081da177e4SLinus Torvalds map_word d;
10975b84e94SMartin Michlmayr d.x[0] = *((uint32_t*)from);
1101da177e4SLinus Torvalds dc21285_write32(map, d, to);
11175b84e94SMartin Michlmayr from += 4;
1121da177e4SLinus Torvalds to += 4;
1131da177e4SLinus Torvalds len -= 4;
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds
dc21285_copy_to_16(struct map_info * map,unsigned long to,const void * from,ssize_t len)1171da177e4SLinus Torvalds static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len)
1181da177e4SLinus Torvalds {
1191da177e4SLinus Torvalds while (len > 0) {
1201da177e4SLinus Torvalds map_word d;
12175b84e94SMartin Michlmayr d.x[0] = *((uint16_t*)from);
1221da177e4SLinus Torvalds dc21285_write16(map, d, to);
12375b84e94SMartin Michlmayr from += 2;
1241da177e4SLinus Torvalds to += 2;
1251da177e4SLinus Torvalds len -= 2;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds
dc21285_copy_to_8(struct map_info * map,unsigned long to,const void * from,ssize_t len)1291da177e4SLinus Torvalds static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds map_word d;
13275b84e94SMartin Michlmayr d.x[0] = *((uint8_t*)from);
1331da177e4SLinus Torvalds dc21285_write8(map, d, to);
13475b84e94SMartin Michlmayr from++;
1351da177e4SLinus Torvalds to++;
1361da177e4SLinus Torvalds len--;
1371da177e4SLinus Torvalds }
1381da177e4SLinus Torvalds
1391da177e4SLinus Torvalds static struct map_info dc21285_map = {
1401da177e4SLinus Torvalds .name = "DC21285 flash",
1411da177e4SLinus Torvalds .phys = NO_XIP,
1421da177e4SLinus Torvalds .size = 16*1024*1024,
1431da177e4SLinus Torvalds .copy_from = dc21285_copy_from,
1441da177e4SLinus Torvalds };
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds /* Partition stuff */
1470984c891SArtem Bityutskiy static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
1481da177e4SLinus Torvalds
init_dc21285(void)1491da177e4SLinus Torvalds static int __init init_dc21285(void)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds /* Determine bankwidth */
1521da177e4SLinus Torvalds switch (*CSR_SA110_CNTL & (3<<14)) {
1531da177e4SLinus Torvalds case SA110_CNTL_ROMWIDTH_8:
1541da177e4SLinus Torvalds dc21285_map.bankwidth = 1;
1551da177e4SLinus Torvalds dc21285_map.read = dc21285_read8;
1561da177e4SLinus Torvalds dc21285_map.write = dc21285_write8;
1571da177e4SLinus Torvalds dc21285_map.copy_to = dc21285_copy_to_8;
1581da177e4SLinus Torvalds break;
1591da177e4SLinus Torvalds case SA110_CNTL_ROMWIDTH_16:
1601da177e4SLinus Torvalds dc21285_map.bankwidth = 2;
1611da177e4SLinus Torvalds dc21285_map.read = dc21285_read16;
1621da177e4SLinus Torvalds dc21285_map.write = dc21285_write16;
1631da177e4SLinus Torvalds dc21285_map.copy_to = dc21285_copy_to_16;
1641da177e4SLinus Torvalds break;
1651da177e4SLinus Torvalds case SA110_CNTL_ROMWIDTH_32:
1661da177e4SLinus Torvalds dc21285_map.bankwidth = 4;
1671da177e4SLinus Torvalds dc21285_map.read = dc21285_read32;
1681da177e4SLinus Torvalds dc21285_map.write = dc21285_write32;
1691da177e4SLinus Torvalds dc21285_map.copy_to = dc21285_copy_to_32;
1701da177e4SLinus Torvalds break;
1711da177e4SLinus Torvalds default:
1721da177e4SLinus Torvalds printk (KERN_ERR "DC21285 flash: undefined bankwidth\n");
1731da177e4SLinus Torvalds return -ENXIO;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n",
1761da177e4SLinus Torvalds dc21285_map.bankwidth*8);
1771da177e4SLinus Torvalds
1781da177e4SLinus Torvalds /* Let's map the flash area */
1791da177e4SLinus Torvalds dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024);
1801da177e4SLinus Torvalds if (!dc21285_map.virt) {
1811da177e4SLinus Torvalds printk("Failed to ioremap\n");
1821da177e4SLinus Torvalds return -EIO;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds if (machine_is_ebsa285()) {
1861da177e4SLinus Torvalds dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map);
1871da177e4SLinus Torvalds } else {
1881da177e4SLinus Torvalds dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map);
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds
1911da177e4SLinus Torvalds if (!dc21285_mtd) {
1921da177e4SLinus Torvalds iounmap(dc21285_map.virt);
1931da177e4SLinus Torvalds return -ENXIO;
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds
1961da177e4SLinus Torvalds dc21285_mtd->owner = THIS_MODULE;
1971da177e4SLinus Torvalds
19842d7fbe2SArtem Bityutskiy mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0);
1991da177e4SLinus Torvalds
2001da177e4SLinus Torvalds if(machine_is_ebsa285()) {
2011da177e4SLinus Torvalds /*
2021da177e4SLinus Torvalds * Flash timing is determined with bits 19-16 of the
2031da177e4SLinus Torvalds * CSR_SA110_CNTL. The value is the number of wait cycles, or
2041da177e4SLinus Torvalds * 0 for 16 cycles (the default). Cycles are 20 ns.
2051da177e4SLinus Torvalds * Here we use 7 for 140 ns flash chips.
2061da177e4SLinus Torvalds */
2071da177e4SLinus Torvalds /* access time */
2081da177e4SLinus Torvalds *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
2091da177e4SLinus Torvalds /* burst time */
2101da177e4SLinus Torvalds *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
2111da177e4SLinus Torvalds /* tristate time */
2121da177e4SLinus Torvalds *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds
2151da177e4SLinus Torvalds return 0;
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
cleanup_dc21285(void)2181da177e4SLinus Torvalds static void __exit cleanup_dc21285(void)
2191da177e4SLinus Torvalds {
220bc2ffddcSJamie Iles mtd_device_unregister(dc21285_mtd);
2211da177e4SLinus Torvalds map_destroy(dc21285_mtd);
2221da177e4SLinus Torvalds iounmap(dc21285_map.virt);
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds module_init(init_dc21285);
2261da177e4SLinus Torvalds module_exit(cleanup_dc21285);
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds
2291da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2302f82af08SNicolas Pitre MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>");
2311da177e4SLinus Torvalds MODULE_DESCRIPTION("MTD map driver for DC21285 boards");
232