162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip) 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * (C) 2000 Nicolas Pitre <nico@fluxnic.net> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This code is GPL 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1662306a36Sopenharmony_ci#include <linux/mtd/map.h> 1762306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci#include <asm/hardware/dec21285.h> 2162306a36Sopenharmony_ci#include <asm/mach-types.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic struct mtd_info *dc21285_mtd; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#ifdef CONFIG_ARCH_NETWINDER 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * This is really ugly, but it seams to be the only 2962306a36Sopenharmony_ci * realiable way to do it, as the cpld state machine 3062306a36Sopenharmony_ci * is unpredictible. So we have a 25us penalty per 3162306a36Sopenharmony_ci * write access. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic void nw_en_write(void) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned long flags; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* 3862306a36Sopenharmony_ci * we want to write a bit pattern XXX1 to Xilinx to enable 3962306a36Sopenharmony_ci * the write gate, which will be open for about the next 2ms. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci raw_spin_lock_irqsave(&nw_gpio_lock, flags); 4262306a36Sopenharmony_ci nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE); 4362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&nw_gpio_lock, flags); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * let the ISA bus to catch on... 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci udelay(25); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci#else 5162306a36Sopenharmony_ci#define nw_en_write() do { } while (0) 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic map_word dc21285_read8(struct map_info *map, unsigned long ofs) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci map_word val; 5762306a36Sopenharmony_ci val.x[0] = *(uint8_t*)(map->virt + ofs); 5862306a36Sopenharmony_ci return val; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic map_word dc21285_read16(struct map_info *map, unsigned long ofs) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci map_word val; 6462306a36Sopenharmony_ci val.x[0] = *(uint16_t*)(map->virt + ofs); 6562306a36Sopenharmony_ci return val; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic map_word dc21285_read32(struct map_info *map, unsigned long ofs) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci map_word val; 7162306a36Sopenharmony_ci val.x[0] = *(uint32_t*)(map->virt + ofs); 7262306a36Sopenharmony_ci return val; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci memcpy(to, (void*)(map->virt + from), len); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci if (machine_is_netwinder()) 8362306a36Sopenharmony_ci nw_en_write(); 8462306a36Sopenharmony_ci *CSR_ROMWRITEREG = adr & 3; 8562306a36Sopenharmony_ci adr &= ~3; 8662306a36Sopenharmony_ci *(uint8_t*)(map->virt + adr) = d.x[0]; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (machine_is_netwinder()) 9262306a36Sopenharmony_ci nw_en_write(); 9362306a36Sopenharmony_ci *CSR_ROMWRITEREG = adr & 3; 9462306a36Sopenharmony_ci adr &= ~3; 9562306a36Sopenharmony_ci *(uint16_t*)(map->virt + adr) = d.x[0]; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (machine_is_netwinder()) 10162306a36Sopenharmony_ci nw_en_write(); 10262306a36Sopenharmony_ci *(uint32_t*)(map->virt + adr) = d.x[0]; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci while (len > 0) { 10862306a36Sopenharmony_ci map_word d; 10962306a36Sopenharmony_ci d.x[0] = *((uint32_t*)from); 11062306a36Sopenharmony_ci dc21285_write32(map, d, to); 11162306a36Sopenharmony_ci from += 4; 11262306a36Sopenharmony_ci to += 4; 11362306a36Sopenharmony_ci len -= 4; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci while (len > 0) { 12062306a36Sopenharmony_ci map_word d; 12162306a36Sopenharmony_ci d.x[0] = *((uint16_t*)from); 12262306a36Sopenharmony_ci dc21285_write16(map, d, to); 12362306a36Sopenharmony_ci from += 2; 12462306a36Sopenharmony_ci to += 2; 12562306a36Sopenharmony_ci len -= 2; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci map_word d; 13262306a36Sopenharmony_ci d.x[0] = *((uint8_t*)from); 13362306a36Sopenharmony_ci dc21285_write8(map, d, to); 13462306a36Sopenharmony_ci from++; 13562306a36Sopenharmony_ci to++; 13662306a36Sopenharmony_ci len--; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct map_info dc21285_map = { 14062306a36Sopenharmony_ci .name = "DC21285 flash", 14162306a36Sopenharmony_ci .phys = NO_XIP, 14262306a36Sopenharmony_ci .size = 16*1024*1024, 14362306a36Sopenharmony_ci .copy_from = dc21285_copy_from, 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Partition stuff */ 14762306a36Sopenharmony_cistatic const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int __init init_dc21285(void) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci /* Determine bankwidth */ 15262306a36Sopenharmony_ci switch (*CSR_SA110_CNTL & (3<<14)) { 15362306a36Sopenharmony_ci case SA110_CNTL_ROMWIDTH_8: 15462306a36Sopenharmony_ci dc21285_map.bankwidth = 1; 15562306a36Sopenharmony_ci dc21285_map.read = dc21285_read8; 15662306a36Sopenharmony_ci dc21285_map.write = dc21285_write8; 15762306a36Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_8; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case SA110_CNTL_ROMWIDTH_16: 16062306a36Sopenharmony_ci dc21285_map.bankwidth = 2; 16162306a36Sopenharmony_ci dc21285_map.read = dc21285_read16; 16262306a36Sopenharmony_ci dc21285_map.write = dc21285_write16; 16362306a36Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_16; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case SA110_CNTL_ROMWIDTH_32: 16662306a36Sopenharmony_ci dc21285_map.bankwidth = 4; 16762306a36Sopenharmony_ci dc21285_map.read = dc21285_read32; 16862306a36Sopenharmony_ci dc21285_map.write = dc21285_write32; 16962306a36Sopenharmony_ci dc21285_map.copy_to = dc21285_copy_to_32; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci default: 17262306a36Sopenharmony_ci printk (KERN_ERR "DC21285 flash: undefined bankwidth\n"); 17362306a36Sopenharmony_ci return -ENXIO; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n", 17662306a36Sopenharmony_ci dc21285_map.bankwidth*8); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Let's map the flash area */ 17962306a36Sopenharmony_ci dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024); 18062306a36Sopenharmony_ci if (!dc21285_map.virt) { 18162306a36Sopenharmony_ci printk("Failed to ioremap\n"); 18262306a36Sopenharmony_ci return -EIO; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (machine_is_ebsa285()) { 18662306a36Sopenharmony_ci dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map); 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (!dc21285_mtd) { 19262306a36Sopenharmony_ci iounmap(dc21285_map.virt); 19362306a36Sopenharmony_ci return -ENXIO; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci dc21285_mtd->owner = THIS_MODULE; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if(machine_is_ebsa285()) { 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * Flash timing is determined with bits 19-16 of the 20362306a36Sopenharmony_ci * CSR_SA110_CNTL. The value is the number of wait cycles, or 20462306a36Sopenharmony_ci * 0 for 16 cycles (the default). Cycles are 20 ns. 20562306a36Sopenharmony_ci * Here we use 7 for 140 ns flash chips. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci /* access time */ 20862306a36Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); 20962306a36Sopenharmony_ci /* burst time */ 21062306a36Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); 21162306a36Sopenharmony_ci /* tristate time */ 21262306a36Sopenharmony_ci *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void __exit cleanup_dc21285(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci mtd_device_unregister(dc21285_mtd); 22162306a36Sopenharmony_ci map_destroy(dc21285_mtd); 22262306a36Sopenharmony_ci iounmap(dc21285_map.virt); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cimodule_init(init_dc21285); 22662306a36Sopenharmony_cimodule_exit(cleanup_dc21285); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 23062306a36Sopenharmony_ciMODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>"); 23162306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for DC21285 boards"); 232