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