18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip) 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * (C) 2000 Nicolas Pitre <nico@fluxnic.net> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This code is GPL 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/io.h> 208c2ecf20Sopenharmony_ci#include <asm/hardware/dec21285.h> 218c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic struct mtd_info *dc21285_mtd; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_NETWINDER 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * This is really ugly, but it seams to be the only 298c2ecf20Sopenharmony_ci * realiable way to do it, as the cpld state machine 308c2ecf20Sopenharmony_ci * is unpredictible. So we have a 25us penalty per 318c2ecf20Sopenharmony_ci * write access. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_cistatic void nw_en_write(void) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned long flags; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci /* 388c2ecf20Sopenharmony_ci * we want to write a bit pattern XXX1 to Xilinx to enable 398c2ecf20Sopenharmony_ci * the write gate, which will be open for about the next 2ms. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&nw_gpio_lock, flags); 428c2ecf20Sopenharmony_ci nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE); 438c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * let the ISA bus to catch on... 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci udelay(25); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci#else 518c2ecf20Sopenharmony_ci#define nw_en_write() do { } while (0) 528c2ecf20Sopenharmony_ci#endif 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic map_word dc21285_read8(struct map_info *map, unsigned long ofs) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci map_word val; 578c2ecf20Sopenharmony_ci val.x[0] = *(uint8_t*)(map->virt + ofs); 588c2ecf20Sopenharmony_ci return val; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic map_word dc21285_read16(struct map_info *map, unsigned long ofs) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci map_word val; 648c2ecf20Sopenharmony_ci val.x[0] = *(uint16_t*)(map->virt + ofs); 658c2ecf20Sopenharmony_ci return val; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic map_word dc21285_read32(struct map_info *map, unsigned long ofs) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci map_word val; 718c2ecf20Sopenharmony_ci val.x[0] = *(uint32_t*)(map->virt + ofs); 728c2ecf20Sopenharmony_ci return val; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci memcpy(to, (void*)(map->virt + from), len); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (machine_is_netwinder()) 838c2ecf20Sopenharmony_ci nw_en_write(); 848c2ecf20Sopenharmony_ci *CSR_ROMWRITEREG = adr & 3; 858c2ecf20Sopenharmony_ci adr &= ~3; 868c2ecf20Sopenharmony_ci *(uint8_t*)(map->virt + adr) = d.x[0]; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (machine_is_netwinder()) 928c2ecf20Sopenharmony_ci nw_en_write(); 938c2ecf20Sopenharmony_ci *CSR_ROMWRITEREG = adr & 3; 948c2ecf20Sopenharmony_ci adr &= ~3; 958c2ecf20Sopenharmony_ci *(uint16_t*)(map->virt + adr) = d.x[0]; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (machine_is_netwinder()) 1018c2ecf20Sopenharmony_ci nw_en_write(); 1028c2ecf20Sopenharmony_ci *(uint32_t*)(map->virt + adr) = d.x[0]; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci while (len > 0) { 1088c2ecf20Sopenharmony_ci map_word d; 1098c2ecf20Sopenharmony_ci d.x[0] = *((uint32_t*)from); 1108c2ecf20Sopenharmony_ci dc21285_write32(map, d, to); 1118c2ecf20Sopenharmony_ci from += 4; 1128c2ecf20Sopenharmony_ci to += 4; 1138c2ecf20Sopenharmony_ci len -= 4; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci while (len > 0) { 1208c2ecf20Sopenharmony_ci map_word d; 1218c2ecf20Sopenharmony_ci d.x[0] = *((uint16_t*)from); 1228c2ecf20Sopenharmony_ci dc21285_write16(map, d, to); 1238c2ecf20Sopenharmony_ci from += 2; 1248c2ecf20Sopenharmony_ci to += 2; 1258c2ecf20Sopenharmony_ci len -= 2; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci map_word d; 1328c2ecf20Sopenharmony_ci d.x[0] = *((uint8_t*)from); 1338c2ecf20Sopenharmony_ci dc21285_write8(map, d, to); 1348c2ecf20Sopenharmony_ci from++; 1358c2ecf20Sopenharmony_ci to++; 1368c2ecf20Sopenharmony_ci len--; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct map_info dc21285_map = { 1408c2ecf20Sopenharmony_ci .name = "DC21285 flash", 1418c2ecf20Sopenharmony_ci .phys = NO_XIP, 1428c2ecf20Sopenharmony_ci .size = 16*1024*1024, 1438c2ecf20Sopenharmony_ci .copy_from = dc21285_copy_from, 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/* Partition stuff */ 1478c2ecf20Sopenharmony_cistatic const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int __init init_dc21285(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci /* Determine bankwidth */ 1528c2ecf20Sopenharmony_ci switch (*CSR_SA110_CNTL & (3<<14)) { 1538c2ecf20Sopenharmony_ci case SA110_CNTL_ROMWIDTH_8: 1548c2ecf20Sopenharmony_ci dc21285_map.bankwidth = 1; 1558c2ecf20Sopenharmony_ci dc21285_map.read = dc21285_read8; 1568c2ecf20Sopenharmony_ci dc21285_map.write = dc21285_write8; 1578c2ecf20Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_8; 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci case SA110_CNTL_ROMWIDTH_16: 1608c2ecf20Sopenharmony_ci dc21285_map.bankwidth = 2; 1618c2ecf20Sopenharmony_ci dc21285_map.read = dc21285_read16; 1628c2ecf20Sopenharmony_ci dc21285_map.write = dc21285_write16; 1638c2ecf20Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_16; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case SA110_CNTL_ROMWIDTH_32: 1668c2ecf20Sopenharmony_ci dc21285_map.bankwidth = 4; 1678c2ecf20Sopenharmony_ci dc21285_map.read = dc21285_read32; 1688c2ecf20Sopenharmony_ci dc21285_map.write = dc21285_write32; 1698c2ecf20Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_32; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci default: 1728c2ecf20Sopenharmony_ci printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); 1738c2ecf20Sopenharmony_ci return -ENXIO; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n", 1768c2ecf20Sopenharmony_ci dc21285_map.bankwidth*8); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Let's map the flash area */ 1798c2ecf20Sopenharmony_ci dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024); 1808c2ecf20Sopenharmony_ci if (!dc21285_map.virt) { 1818c2ecf20Sopenharmony_ci printk("Failed to ioremap\n"); 1828c2ecf20Sopenharmony_ci return -EIO; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (machine_is_ebsa285()) { 1868c2ecf20Sopenharmony_ci dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (!dc21285_mtd) { 1928c2ecf20Sopenharmony_ci iounmap(dc21285_map.virt); 1938c2ecf20Sopenharmony_ci return -ENXIO; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci dc21285_mtd->owner = THIS_MODULE; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if(machine_is_ebsa285()) { 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Flash timing is determined with bits 19-16 of the 2038c2ecf20Sopenharmony_ci * CSR_SA110_CNTL. The value is the number of wait cycles, or 2048c2ecf20Sopenharmony_ci * 0 for 16 cycles (the default). Cycles are 20 ns. 2058c2ecf20Sopenharmony_ci * Here we use 7 for 140 ns flash chips. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci /* access time */ 2088c2ecf20Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); 2098c2ecf20Sopenharmony_ci /* burst time */ 2108c2ecf20Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); 2118c2ecf20Sopenharmony_ci /* tristate time */ 2128c2ecf20Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void __exit cleanup_dc21285(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci mtd_device_unregister(dc21285_mtd); 2218c2ecf20Sopenharmony_ci map_destroy(dc21285_mtd); 2228c2ecf20Sopenharmony_ci iounmap(dc21285_map.virt); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cimodule_init(init_dc21285); 2268c2ecf20Sopenharmony_cimodule_exit(cleanup_dc21285); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>"); 2318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for DC21285 boards"); 232