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