xref: /linux/drivers/mtd/maps/sc520cdp.c (revision 3502fbcc25eb67db96fea35abe721c82aa1f4ff3)
11da177e4SLinus Torvalds /* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
61da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
71da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
81da177e4SLinus Torvalds  * (at your option) any later version.
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
111da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
121da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131da177e4SLinus Torvalds  * GNU General Public License for more details.
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
161da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
171da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  * The SC520CDP is an evaluation board for the Elan SC520 processor available
211da177e4SLinus Torvalds  * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
221da177e4SLinus Torvalds  * and up to 512 KiB of 8-bit DIL Flash ROM.
231da177e4SLinus Torvalds  * For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
241da177e4SLinus Torvalds  */
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #include <linux/module.h>
271da177e4SLinus Torvalds #include <linux/types.h>
281da177e4SLinus Torvalds #include <linux/kernel.h>
291da177e4SLinus Torvalds #include <linux/init.h>
301da177e4SLinus Torvalds #include <asm/io.h>
311da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
321da177e4SLinus Torvalds #include <linux/mtd/map.h>
331da177e4SLinus Torvalds #include <linux/mtd/concat.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds ** The Embedded Systems BIOS decodes the first FLASH starting at
371da177e4SLinus Torvalds ** 0x8400000. This is a *terrible* place for it because accessing
381da177e4SLinus Torvalds ** the flash at this location causes the A22 address line to be high
391da177e4SLinus Torvalds ** (that's what 0x8400000 binary's ought to be). But this is the highest
401da177e4SLinus Torvalds ** order address line on the raw flash devices themselves!!
411da177e4SLinus Torvalds ** This causes the top HALF of the flash to be accessed first. Beyond
421da177e4SLinus Torvalds ** the physical limits of the flash, the flash chip aliases over (to
431da177e4SLinus Torvalds ** 0x880000 which causes the bottom half to be accessed. This splits the
441da177e4SLinus Torvalds ** flash into two and inverts it! If you then try to access this from another
451da177e4SLinus Torvalds ** program that does NOT do this insanity, then you *will* access the
461da177e4SLinus Torvalds ** first half of the flash, but not find what you expect there. That
471da177e4SLinus Torvalds ** stuff is in the *second* half! Similarly, the address used by the
481da177e4SLinus Torvalds ** BIOS for the second FLASH bank is also quite a bad choice.
491da177e4SLinus Torvalds ** If REPROGRAM_PAR is defined below (the default), then this driver will
501da177e4SLinus Torvalds ** choose more useful addresses for the FLASH banks by reprogramming the
511da177e4SLinus Torvalds ** responsible PARxx registers in the SC520's MMCR region. This will
521da177e4SLinus Torvalds ** cause the settings to be incompatible with the BIOS's settings, which
531da177e4SLinus Torvalds ** shouldn't be a problem since you are running Linux, (i.e. the BIOS is
541da177e4SLinus Torvalds ** not much use anyway). However, if you need to be compatible with
551da177e4SLinus Torvalds ** the BIOS for some reason, just undefine REPROGRAM_PAR.
561da177e4SLinus Torvalds */
571da177e4SLinus Torvalds #define REPROGRAM_PAR
581da177e4SLinus Torvalds 
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* These are the addresses we want.. */
641da177e4SLinus Torvalds #define WINDOW_ADDR_0	0x08800000
651da177e4SLinus Torvalds #define WINDOW_ADDR_1	0x09000000
661da177e4SLinus Torvalds #define WINDOW_ADDR_2	0x09800000
671da177e4SLinus Torvalds 
681da177e4SLinus Torvalds /* .. and these are the addresses the BIOS gives us */
691da177e4SLinus Torvalds #define WINDOW_ADDR_0_BIOS	0x08400000
701da177e4SLinus Torvalds #define WINDOW_ADDR_1_BIOS	0x08c00000
711da177e4SLinus Torvalds #define WINDOW_ADDR_2_BIOS	0x09400000
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds #else
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds #define WINDOW_ADDR_0	0x08400000
761da177e4SLinus Torvalds #define WINDOW_ADDR_1	0x08C00000
771da177e4SLinus Torvalds #define WINDOW_ADDR_2	0x09400000
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds #endif
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds #define WINDOW_SIZE_0	0x00800000
821da177e4SLinus Torvalds #define WINDOW_SIZE_1	0x00800000
831da177e4SLinus Torvalds #define WINDOW_SIZE_2	0x00080000
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds static struct map_info sc520cdp_map[] = {
871da177e4SLinus Torvalds 	{
881da177e4SLinus Torvalds 		.name = "SC520CDP Flash Bank #0",
891da177e4SLinus Torvalds 		.size = WINDOW_SIZE_0,
901da177e4SLinus Torvalds 		.bankwidth = 4,
911da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_0
921da177e4SLinus Torvalds 	},
931da177e4SLinus Torvalds 	{
941da177e4SLinus Torvalds 		.name = "SC520CDP Flash Bank #1",
951da177e4SLinus Torvalds 		.size = WINDOW_SIZE_1,
961da177e4SLinus Torvalds 		.bankwidth = 4,
971da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_1
981da177e4SLinus Torvalds 	},
991da177e4SLinus Torvalds 	{
1001da177e4SLinus Torvalds 		.name = "SC520CDP DIL Flash",
1011da177e4SLinus Torvalds 		.size = WINDOW_SIZE_2,
1021da177e4SLinus Torvalds 		.bankwidth = 1,
1031da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_2
1041da177e4SLinus Torvalds 	},
1051da177e4SLinus Torvalds };
1061da177e4SLinus Torvalds 
10787d10f3cSTobias Klauser #define NUM_FLASH_BANKS	ARRAY_SIZE(sc520cdp_map)
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds static struct mtd_info *mymtd[NUM_FLASH_BANKS];
1101da177e4SLinus Torvalds static struct mtd_info *merged_mtd;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /*
1151da177e4SLinus Torvalds ** The SC520 MMCR (memory mapped control register) region resides
1161da177e4SLinus Torvalds ** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers
1171da177e4SLinus Torvalds ** are at offset 0x88 in the MMCR:
1181da177e4SLinus Torvalds */
1191da177e4SLinus Torvalds #define SC520_MMCR_BASE		0xFFFEF000
1201da177e4SLinus Torvalds #define SC520_MMCR_EXTENT	0x1000
1211da177e4SLinus Torvalds #define SC520_PAR(x)		((0x88/sizeof(unsigned long)) + (x))
1221da177e4SLinus Torvalds #define NUM_SC520_PAR		16	/* total number of PAR registers */
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /*
1251da177e4SLinus Torvalds ** The highest three bits in a PAR register determine what target
1261da177e4SLinus Torvalds ** device is controlled by this PAR. Here, only ROMCS? and BOOTCS
1271da177e4SLinus Torvalds ** devices are of interest.
1281da177e4SLinus Torvalds */
1291da177e4SLinus Torvalds #define SC520_PAR_BOOTCS	(0x4<<29)
1301da177e4SLinus Torvalds #define SC520_PAR_ROMCS0	(0x5<<29)
1311da177e4SLinus Torvalds #define SC520_PAR_ROMCS1	(0x6<<29)
1321da177e4SLinus Torvalds #define SC520_PAR_TRGDEV	(0x7<<29)
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds /*
1351da177e4SLinus Torvalds ** Bits 28 thru 26 determine some attributes for the
1361da177e4SLinus Torvalds ** region controlled by the PAR. (We only use non-cacheable)
1371da177e4SLinus Torvalds */
1381da177e4SLinus Torvalds #define SC520_PAR_WRPROT	(1<<26)	/* write protected       */
1391da177e4SLinus Torvalds #define SC520_PAR_NOCACHE	(1<<27)	/* non-cacheable         */
1401da177e4SLinus Torvalds #define SC520_PAR_NOEXEC	(1<<28)	/* code execution denied */
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds /*
1441da177e4SLinus Torvalds ** Bit 25 determines the granularity: 4K or 64K
1451da177e4SLinus Torvalds */
1461da177e4SLinus Torvalds #define SC520_PAR_PG_SIZ4	(0<<25)
1471da177e4SLinus Torvalds #define SC520_PAR_PG_SIZ64	(1<<25)
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds /*
1501da177e4SLinus Torvalds ** Build a value to be written into a PAR register.
1511da177e4SLinus Torvalds ** We only need ROM entries, 64K page size:
1521da177e4SLinus Torvalds */
1531da177e4SLinus Torvalds #define SC520_PAR_ENTRY(trgdev, address, size) \
1541da177e4SLinus Torvalds 	((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \
1551da177e4SLinus Torvalds 	(address) >> 16 | (((size) >> 16) - 1) << 14)
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds struct sc520_par_table
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	unsigned long trgdev;
1601da177e4SLinus Torvalds 	unsigned long new_par;
1611da177e4SLinus Torvalds 	unsigned long default_address;
1621da177e4SLinus Torvalds };
1631da177e4SLinus Torvalds 
1647ac571f8SDavid Woodhouse static const struct sc520_par_table par_table[NUM_FLASH_BANKS] =
1651da177e4SLinus Torvalds {
1661da177e4SLinus Torvalds 	{	/* Flash Bank #0: selected by ROMCS0 */
1671da177e4SLinus Torvalds 		SC520_PAR_ROMCS0,
1681da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0),
1691da177e4SLinus Torvalds 		WINDOW_ADDR_0_BIOS
1701da177e4SLinus Torvalds 	},
1711da177e4SLinus Torvalds 	{	/* Flash Bank #1: selected by ROMCS1 */
1721da177e4SLinus Torvalds 		SC520_PAR_ROMCS1,
1731da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1),
1741da177e4SLinus Torvalds 		WINDOW_ADDR_1_BIOS
1751da177e4SLinus Torvalds 	},
1761da177e4SLinus Torvalds 	{	/* DIL (BIOS) Flash: selected by BOOTCS */
1771da177e4SLinus Torvalds 		SC520_PAR_BOOTCS,
1781da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2),
1791da177e4SLinus Torvalds 		WINDOW_ADDR_2_BIOS
1801da177e4SLinus Torvalds 	}
1811da177e4SLinus Torvalds };
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds static void sc520cdp_setup_par(void)
1851da177e4SLinus Torvalds {
1863a9f76bdSBrian Norris 	unsigned long __iomem *mmcr;
1871da177e4SLinus Torvalds 	unsigned long mmcr_val;
1881da177e4SLinus Torvalds 	int i, j;
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds 	/* map in SC520's MMCR area */
1911da177e4SLinus Torvalds 	mmcr = ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
1921da177e4SLinus Torvalds 	if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
1931da177e4SLinus Torvalds 		/* force physical address fields to BIOS defaults: */
1941da177e4SLinus Torvalds 		for(i = 0; i < NUM_FLASH_BANKS; i++)
1951da177e4SLinus Torvalds 			sc520cdp_map[i].phys = par_table[i].default_address;
1961da177e4SLinus Torvalds 		return;
1971da177e4SLinus Torvalds 	}
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds 	/*
20025985edcSLucas De Marchi 	** Find the PARxx registers that are responsible for activating
2011da177e4SLinus Torvalds 	** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
2021da177e4SLinus Torvalds 	** new value from the table.
2031da177e4SLinus Torvalds 	*/
2041da177e4SLinus Torvalds 	for(i = 0; i < NUM_FLASH_BANKS; i++) {		/* for each par_table entry  */
2051da177e4SLinus Torvalds 		for(j = 0; j < NUM_SC520_PAR; j++) {	/* for each PAR register     */
2063a9f76bdSBrian Norris 			mmcr_val = readl(&mmcr[SC520_PAR(j)]);
2071da177e4SLinus Torvalds 			/* if target device field matches, reprogram the PAR */
2081da177e4SLinus Torvalds 			if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
2091da177e4SLinus Torvalds 			{
2103a9f76bdSBrian Norris 				writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]);
2111da177e4SLinus Torvalds 				break;
2121da177e4SLinus Torvalds 			}
2131da177e4SLinus Torvalds 		}
2141da177e4SLinus Torvalds 		if(j == NUM_SC520_PAR)
2151da177e4SLinus Torvalds 		{	/* no matching PAR found: try default BIOS address */
2161da177e4SLinus Torvalds 			printk(KERN_NOTICE "Could not find PAR responsible for %s\n",
2171da177e4SLinus Torvalds 				sc520cdp_map[i].name);
2181da177e4SLinus Torvalds 			printk(KERN_NOTICE "Trying default address 0x%lx\n",
2191da177e4SLinus Torvalds 				par_table[i].default_address);
2201da177e4SLinus Torvalds 			sc520cdp_map[i].phys = par_table[i].default_address;
2211da177e4SLinus Torvalds 		}
2221da177e4SLinus Torvalds 	}
2231da177e4SLinus Torvalds 	iounmap(mmcr);
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds #endif
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static int __init init_sc520cdp(void)
2291da177e4SLinus Torvalds {
230*3502fbccSLuis Henriques 	int i, j, devices_found = 0;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
2331da177e4SLinus Torvalds 	/* reprogram PAR registers so flash appears at the desired addresses */
2341da177e4SLinus Torvalds 	sc520cdp_setup_par();
2351da177e4SLinus Torvalds #endif
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
238e389612dSAndrew Morton 		printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
239e389612dSAndrew Morton 			(unsigned long long)sc520cdp_map[i].size,
240e389612dSAndrew Morton 			(unsigned long long)sc520cdp_map[i].phys);
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 		sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 		if (!sc520cdp_map[i].virt) {
2451da177e4SLinus Torvalds 			printk("Failed to ioremap_nocache\n");
246*3502fbccSLuis Henriques 			for (j = 0; j < i; j++) {
247*3502fbccSLuis Henriques 				if (mymtd[j]) {
248*3502fbccSLuis Henriques 					map_destroy(mymtd[j]);
249*3502fbccSLuis Henriques 					iounmap(sc520cdp_map[j].virt);
250*3502fbccSLuis Henriques 				}
251*3502fbccSLuis Henriques 			}
2521da177e4SLinus Torvalds 			return -EIO;
2531da177e4SLinus Torvalds 		}
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds 		simple_map_init(&sc520cdp_map[i]);
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds 		mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
2581da177e4SLinus Torvalds 		if(!mymtd[i])
2591da177e4SLinus Torvalds 			mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
2601da177e4SLinus Torvalds 		if(!mymtd[i])
2611da177e4SLinus Torvalds 			mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
2621da177e4SLinus Torvalds 
2631da177e4SLinus Torvalds 		if (mymtd[i]) {
2641da177e4SLinus Torvalds 			mymtd[i]->owner = THIS_MODULE;
2651da177e4SLinus Torvalds 			++devices_found;
2661da177e4SLinus Torvalds 		}
2671da177e4SLinus Torvalds 		else {
2681da177e4SLinus Torvalds 			iounmap(sc520cdp_map[i].virt);
2691da177e4SLinus Torvalds 		}
2701da177e4SLinus Torvalds 	}
2711da177e4SLinus Torvalds 	if(devices_found >= 2) {
2721da177e4SLinus Torvalds 		/* Combine the two flash banks into a single MTD device & register it: */
2731da177e4SLinus Torvalds 		merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1");
2741da177e4SLinus Torvalds 		if(merged_mtd)
275ee0e87b1SJamie Iles 			mtd_device_register(merged_mtd, NULL, 0);
2761da177e4SLinus Torvalds 	}
2771da177e4SLinus Torvalds 	if(devices_found == 3) /* register the third (DIL-Flash) device */
278ee0e87b1SJamie Iles 		mtd_device_register(mymtd[2], NULL, 0);
2791da177e4SLinus Torvalds 	return(devices_found ? 0 : -ENXIO);
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds static void __exit cleanup_sc520cdp(void)
2831da177e4SLinus Torvalds {
2841da177e4SLinus Torvalds 	int i;
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	if (merged_mtd) {
287ee0e87b1SJamie Iles 		mtd_device_unregister(merged_mtd);
2881da177e4SLinus Torvalds 		mtd_concat_destroy(merged_mtd);
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 	if (mymtd[2])
291ee0e87b1SJamie Iles 		mtd_device_unregister(mymtd[2]);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
2941da177e4SLinus Torvalds 		if (mymtd[i])
2951da177e4SLinus Torvalds 			map_destroy(mymtd[i]);
2961da177e4SLinus Torvalds 		if (sc520cdp_map[i].virt) {
2971da177e4SLinus Torvalds 			iounmap(sc520cdp_map[i].virt);
2981da177e4SLinus Torvalds 			sc520cdp_map[i].virt = NULL;
2991da177e4SLinus Torvalds 		}
3001da177e4SLinus Torvalds 	}
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds module_init(init_sc520cdp);
3041da177e4SLinus Torvalds module_exit(cleanup_sc520cdp);
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds MODULE_LICENSE("GPL");
3071da177e4SLinus Torvalds MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
3081da177e4SLinus Torvalds MODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform");
309