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