xref: /linux/drivers/mtd/maps/sc520cdp.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform
2*1da177e4SLinus Torvalds  *
3*1da177e4SLinus Torvalds  * Copyright (C) 2001 Sysgo Real-Time Solutions GmbH
4*1da177e4SLinus Torvalds  *
5*1da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
6*1da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
7*1da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
8*1da177e4SLinus Torvalds  * (at your option) any later version.
9*1da177e4SLinus Torvalds  *
10*1da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
11*1da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*1da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*1da177e4SLinus Torvalds  * GNU General Public License for more details.
14*1da177e4SLinus Torvalds  *
15*1da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
16*1da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
17*1da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18*1da177e4SLinus Torvalds  *
19*1da177e4SLinus Torvalds  * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $
20*1da177e4SLinus Torvalds  *
21*1da177e4SLinus Torvalds  *
22*1da177e4SLinus Torvalds  * The SC520CDP is an evaluation board for the Elan SC520 processor available
23*1da177e4SLinus Torvalds  * from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
24*1da177e4SLinus Torvalds  * and up to 512 KiB of 8-bit DIL Flash ROM.
25*1da177e4SLinus Torvalds  * For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
26*1da177e4SLinus Torvalds  */
27*1da177e4SLinus Torvalds 
28*1da177e4SLinus Torvalds #include <linux/config.h>
29*1da177e4SLinus Torvalds #include <linux/module.h>
30*1da177e4SLinus Torvalds #include <linux/types.h>
31*1da177e4SLinus Torvalds #include <linux/kernel.h>
32*1da177e4SLinus Torvalds #include <linux/init.h>
33*1da177e4SLinus Torvalds #include <asm/io.h>
34*1da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
35*1da177e4SLinus Torvalds #include <linux/mtd/map.h>
36*1da177e4SLinus Torvalds #include <linux/mtd/concat.h>
37*1da177e4SLinus Torvalds 
38*1da177e4SLinus Torvalds /*
39*1da177e4SLinus Torvalds ** The Embedded Systems BIOS decodes the first FLASH starting at
40*1da177e4SLinus Torvalds ** 0x8400000. This is a *terrible* place for it because accessing
41*1da177e4SLinus Torvalds ** the flash at this location causes the A22 address line to be high
42*1da177e4SLinus Torvalds ** (that's what 0x8400000 binary's ought to be). But this is the highest
43*1da177e4SLinus Torvalds ** order address line on the raw flash devices themselves!!
44*1da177e4SLinus Torvalds ** This causes the top HALF of the flash to be accessed first. Beyond
45*1da177e4SLinus Torvalds ** the physical limits of the flash, the flash chip aliases over (to
46*1da177e4SLinus Torvalds ** 0x880000 which causes the bottom half to be accessed. This splits the
47*1da177e4SLinus Torvalds ** flash into two and inverts it! If you then try to access this from another
48*1da177e4SLinus Torvalds ** program that does NOT do this insanity, then you *will* access the
49*1da177e4SLinus Torvalds ** first half of the flash, but not find what you expect there. That
50*1da177e4SLinus Torvalds ** stuff is in the *second* half! Similarly, the address used by the
51*1da177e4SLinus Torvalds ** BIOS for the second FLASH bank is also quite a bad choice.
52*1da177e4SLinus Torvalds ** If REPROGRAM_PAR is defined below (the default), then this driver will
53*1da177e4SLinus Torvalds ** choose more useful addresses for the FLASH banks by reprogramming the
54*1da177e4SLinus Torvalds ** responsible PARxx registers in the SC520's MMCR region. This will
55*1da177e4SLinus Torvalds ** cause the settings to be incompatible with the BIOS's settings, which
56*1da177e4SLinus Torvalds ** shouldn't be a problem since you are running Linux, (i.e. the BIOS is
57*1da177e4SLinus Torvalds ** not much use anyway). However, if you need to be compatible with
58*1da177e4SLinus Torvalds ** the BIOS for some reason, just undefine REPROGRAM_PAR.
59*1da177e4SLinus Torvalds */
60*1da177e4SLinus Torvalds #define REPROGRAM_PAR
61*1da177e4SLinus Torvalds 
62*1da177e4SLinus Torvalds 
63*1da177e4SLinus Torvalds 
64*1da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
65*1da177e4SLinus Torvalds 
66*1da177e4SLinus Torvalds /* These are the addresses we want.. */
67*1da177e4SLinus Torvalds #define WINDOW_ADDR_0	0x08800000
68*1da177e4SLinus Torvalds #define WINDOW_ADDR_1	0x09000000
69*1da177e4SLinus Torvalds #define WINDOW_ADDR_2	0x09800000
70*1da177e4SLinus Torvalds 
71*1da177e4SLinus Torvalds /* .. and these are the addresses the BIOS gives us */
72*1da177e4SLinus Torvalds #define WINDOW_ADDR_0_BIOS	0x08400000
73*1da177e4SLinus Torvalds #define WINDOW_ADDR_1_BIOS	0x08c00000
74*1da177e4SLinus Torvalds #define WINDOW_ADDR_2_BIOS	0x09400000
75*1da177e4SLinus Torvalds 
76*1da177e4SLinus Torvalds #else
77*1da177e4SLinus Torvalds 
78*1da177e4SLinus Torvalds #define WINDOW_ADDR_0	0x08400000
79*1da177e4SLinus Torvalds #define WINDOW_ADDR_1	0x08C00000
80*1da177e4SLinus Torvalds #define WINDOW_ADDR_2	0x09400000
81*1da177e4SLinus Torvalds 
82*1da177e4SLinus Torvalds #endif
83*1da177e4SLinus Torvalds 
84*1da177e4SLinus Torvalds #define WINDOW_SIZE_0	0x00800000
85*1da177e4SLinus Torvalds #define WINDOW_SIZE_1	0x00800000
86*1da177e4SLinus Torvalds #define WINDOW_SIZE_2	0x00080000
87*1da177e4SLinus Torvalds 
88*1da177e4SLinus Torvalds 
89*1da177e4SLinus Torvalds static struct map_info sc520cdp_map[] = {
90*1da177e4SLinus Torvalds 	{
91*1da177e4SLinus Torvalds 		.name = "SC520CDP Flash Bank #0",
92*1da177e4SLinus Torvalds 		.size = WINDOW_SIZE_0,
93*1da177e4SLinus Torvalds 		.bankwidth = 4,
94*1da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_0
95*1da177e4SLinus Torvalds 	},
96*1da177e4SLinus Torvalds 	{
97*1da177e4SLinus Torvalds 		.name = "SC520CDP Flash Bank #1",
98*1da177e4SLinus Torvalds 		.size = WINDOW_SIZE_1,
99*1da177e4SLinus Torvalds 		.bankwidth = 4,
100*1da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_1
101*1da177e4SLinus Torvalds 	},
102*1da177e4SLinus Torvalds 	{
103*1da177e4SLinus Torvalds 		.name = "SC520CDP DIL Flash",
104*1da177e4SLinus Torvalds 		.size = WINDOW_SIZE_2,
105*1da177e4SLinus Torvalds 		.bankwidth = 1,
106*1da177e4SLinus Torvalds 		.phys = WINDOW_ADDR_2
107*1da177e4SLinus Torvalds 	},
108*1da177e4SLinus Torvalds };
109*1da177e4SLinus Torvalds 
110*1da177e4SLinus Torvalds #define NUM_FLASH_BANKS	(sizeof(sc520cdp_map)/sizeof(struct map_info))
111*1da177e4SLinus Torvalds 
112*1da177e4SLinus Torvalds static struct mtd_info *mymtd[NUM_FLASH_BANKS];
113*1da177e4SLinus Torvalds static struct mtd_info *merged_mtd;
114*1da177e4SLinus Torvalds 
115*1da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
116*1da177e4SLinus Torvalds 
117*1da177e4SLinus Torvalds /*
118*1da177e4SLinus Torvalds ** The SC520 MMCR (memory mapped control register) region resides
119*1da177e4SLinus Torvalds ** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers
120*1da177e4SLinus Torvalds ** are at offset 0x88 in the MMCR:
121*1da177e4SLinus Torvalds */
122*1da177e4SLinus Torvalds #define SC520_MMCR_BASE		0xFFFEF000
123*1da177e4SLinus Torvalds #define SC520_MMCR_EXTENT	0x1000
124*1da177e4SLinus Torvalds #define SC520_PAR(x)		((0x88/sizeof(unsigned long)) + (x))
125*1da177e4SLinus Torvalds #define NUM_SC520_PAR		16	/* total number of PAR registers */
126*1da177e4SLinus Torvalds 
127*1da177e4SLinus Torvalds /*
128*1da177e4SLinus Torvalds ** The highest three bits in a PAR register determine what target
129*1da177e4SLinus Torvalds ** device is controlled by this PAR. Here, only ROMCS? and BOOTCS
130*1da177e4SLinus Torvalds ** devices are of interest.
131*1da177e4SLinus Torvalds */
132*1da177e4SLinus Torvalds #define SC520_PAR_BOOTCS	(0x4<<29)
133*1da177e4SLinus Torvalds #define SC520_PAR_ROMCS0	(0x5<<29)
134*1da177e4SLinus Torvalds #define SC520_PAR_ROMCS1	(0x6<<29)
135*1da177e4SLinus Torvalds #define SC520_PAR_TRGDEV	(0x7<<29)
136*1da177e4SLinus Torvalds 
137*1da177e4SLinus Torvalds /*
138*1da177e4SLinus Torvalds ** Bits 28 thru 26 determine some attributes for the
139*1da177e4SLinus Torvalds ** region controlled by the PAR. (We only use non-cacheable)
140*1da177e4SLinus Torvalds */
141*1da177e4SLinus Torvalds #define SC520_PAR_WRPROT	(1<<26)	/* write protected       */
142*1da177e4SLinus Torvalds #define SC520_PAR_NOCACHE	(1<<27)	/* non-cacheable         */
143*1da177e4SLinus Torvalds #define SC520_PAR_NOEXEC	(1<<28)	/* code execution denied */
144*1da177e4SLinus Torvalds 
145*1da177e4SLinus Torvalds 
146*1da177e4SLinus Torvalds /*
147*1da177e4SLinus Torvalds ** Bit 25 determines the granularity: 4K or 64K
148*1da177e4SLinus Torvalds */
149*1da177e4SLinus Torvalds #define SC520_PAR_PG_SIZ4	(0<<25)
150*1da177e4SLinus Torvalds #define SC520_PAR_PG_SIZ64	(1<<25)
151*1da177e4SLinus Torvalds 
152*1da177e4SLinus Torvalds /*
153*1da177e4SLinus Torvalds ** Build a value to be written into a PAR register.
154*1da177e4SLinus Torvalds ** We only need ROM entries, 64K page size:
155*1da177e4SLinus Torvalds */
156*1da177e4SLinus Torvalds #define SC520_PAR_ENTRY(trgdev, address, size) \
157*1da177e4SLinus Torvalds 	((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \
158*1da177e4SLinus Torvalds 	(address) >> 16 | (((size) >> 16) - 1) << 14)
159*1da177e4SLinus Torvalds 
160*1da177e4SLinus Torvalds struct sc520_par_table
161*1da177e4SLinus Torvalds {
162*1da177e4SLinus Torvalds 	unsigned long trgdev;
163*1da177e4SLinus Torvalds 	unsigned long new_par;
164*1da177e4SLinus Torvalds 	unsigned long default_address;
165*1da177e4SLinus Torvalds };
166*1da177e4SLinus Torvalds 
167*1da177e4SLinus Torvalds static struct sc520_par_table par_table[NUM_FLASH_BANKS] =
168*1da177e4SLinus Torvalds {
169*1da177e4SLinus Torvalds 	{	/* Flash Bank #0: selected by ROMCS0 */
170*1da177e4SLinus Torvalds 		SC520_PAR_ROMCS0,
171*1da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0),
172*1da177e4SLinus Torvalds 		WINDOW_ADDR_0_BIOS
173*1da177e4SLinus Torvalds 	},
174*1da177e4SLinus Torvalds 	{	/* Flash Bank #1: selected by ROMCS1 */
175*1da177e4SLinus Torvalds 		SC520_PAR_ROMCS1,
176*1da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1),
177*1da177e4SLinus Torvalds 		WINDOW_ADDR_1_BIOS
178*1da177e4SLinus Torvalds 	},
179*1da177e4SLinus Torvalds 	{	/* DIL (BIOS) Flash: selected by BOOTCS */
180*1da177e4SLinus Torvalds 		SC520_PAR_BOOTCS,
181*1da177e4SLinus Torvalds 		SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2),
182*1da177e4SLinus Torvalds 		WINDOW_ADDR_2_BIOS
183*1da177e4SLinus Torvalds 	}
184*1da177e4SLinus Torvalds };
185*1da177e4SLinus Torvalds 
186*1da177e4SLinus Torvalds 
187*1da177e4SLinus Torvalds static void sc520cdp_setup_par(void)
188*1da177e4SLinus Torvalds {
189*1da177e4SLinus Torvalds 	volatile unsigned long __iomem *mmcr;
190*1da177e4SLinus Torvalds 	unsigned long mmcr_val;
191*1da177e4SLinus Torvalds 	int i, j;
192*1da177e4SLinus Torvalds 
193*1da177e4SLinus Torvalds 	/* map in SC520's MMCR area */
194*1da177e4SLinus Torvalds 	mmcr = ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
195*1da177e4SLinus Torvalds 	if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
196*1da177e4SLinus Torvalds 		/* force physical address fields to BIOS defaults: */
197*1da177e4SLinus Torvalds 		for(i = 0; i < NUM_FLASH_BANKS; i++)
198*1da177e4SLinus Torvalds 			sc520cdp_map[i].phys = par_table[i].default_address;
199*1da177e4SLinus Torvalds 		return;
200*1da177e4SLinus Torvalds 	}
201*1da177e4SLinus Torvalds 
202*1da177e4SLinus Torvalds 	/*
203*1da177e4SLinus Torvalds 	** Find the PARxx registers that are reponsible for activating
204*1da177e4SLinus Torvalds 	** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
205*1da177e4SLinus Torvalds 	** new value from the table.
206*1da177e4SLinus Torvalds 	*/
207*1da177e4SLinus Torvalds 	for(i = 0; i < NUM_FLASH_BANKS; i++) {		/* for each par_table entry  */
208*1da177e4SLinus Torvalds 		for(j = 0; j < NUM_SC520_PAR; j++) {	/* for each PAR register     */
209*1da177e4SLinus Torvalds 			mmcr_val = mmcr[SC520_PAR(j)];
210*1da177e4SLinus Torvalds 			/* if target device field matches, reprogram the PAR */
211*1da177e4SLinus Torvalds 			if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
212*1da177e4SLinus Torvalds 			{
213*1da177e4SLinus Torvalds 				mmcr[SC520_PAR(j)] = par_table[i].new_par;
214*1da177e4SLinus Torvalds 				break;
215*1da177e4SLinus Torvalds 			}
216*1da177e4SLinus Torvalds 		}
217*1da177e4SLinus Torvalds 		if(j == NUM_SC520_PAR)
218*1da177e4SLinus Torvalds 		{	/* no matching PAR found: try default BIOS address */
219*1da177e4SLinus Torvalds 			printk(KERN_NOTICE "Could not find PAR responsible for %s\n",
220*1da177e4SLinus Torvalds 				sc520cdp_map[i].name);
221*1da177e4SLinus Torvalds 			printk(KERN_NOTICE "Trying default address 0x%lx\n",
222*1da177e4SLinus Torvalds 				par_table[i].default_address);
223*1da177e4SLinus Torvalds 			sc520cdp_map[i].phys = par_table[i].default_address;
224*1da177e4SLinus Torvalds 		}
225*1da177e4SLinus Torvalds 	}
226*1da177e4SLinus Torvalds 	iounmap(mmcr);
227*1da177e4SLinus Torvalds }
228*1da177e4SLinus Torvalds #endif
229*1da177e4SLinus Torvalds 
230*1da177e4SLinus Torvalds 
231*1da177e4SLinus Torvalds static int __init init_sc520cdp(void)
232*1da177e4SLinus Torvalds {
233*1da177e4SLinus Torvalds 	int i, devices_found = 0;
234*1da177e4SLinus Torvalds 
235*1da177e4SLinus Torvalds #ifdef REPROGRAM_PAR
236*1da177e4SLinus Torvalds 	/* reprogram PAR registers so flash appears at the desired addresses */
237*1da177e4SLinus Torvalds 	sc520cdp_setup_par();
238*1da177e4SLinus Torvalds #endif
239*1da177e4SLinus Torvalds 
240*1da177e4SLinus Torvalds 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
241*1da177e4SLinus Torvalds 		printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
242*1da177e4SLinus Torvalds 		       sc520cdp_map[i].size, sc520cdp_map[i].phys);
243*1da177e4SLinus Torvalds 
244*1da177e4SLinus Torvalds 		sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
245*1da177e4SLinus Torvalds 
246*1da177e4SLinus Torvalds 		if (!sc520cdp_map[i].virt) {
247*1da177e4SLinus Torvalds 			printk("Failed to ioremap_nocache\n");
248*1da177e4SLinus Torvalds 			return -EIO;
249*1da177e4SLinus Torvalds 		}
250*1da177e4SLinus Torvalds 
251*1da177e4SLinus Torvalds 		simple_map_init(&sc520cdp_map[i]);
252*1da177e4SLinus Torvalds 
253*1da177e4SLinus Torvalds 		mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
254*1da177e4SLinus Torvalds 		if(!mymtd[i])
255*1da177e4SLinus Torvalds 			mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
256*1da177e4SLinus Torvalds 		if(!mymtd[i])
257*1da177e4SLinus Torvalds 			mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
258*1da177e4SLinus Torvalds 
259*1da177e4SLinus Torvalds 		if (mymtd[i]) {
260*1da177e4SLinus Torvalds 			mymtd[i]->owner = THIS_MODULE;
261*1da177e4SLinus Torvalds 			++devices_found;
262*1da177e4SLinus Torvalds 		}
263*1da177e4SLinus Torvalds 		else {
264*1da177e4SLinus Torvalds 			iounmap(sc520cdp_map[i].virt);
265*1da177e4SLinus Torvalds 		}
266*1da177e4SLinus Torvalds 	}
267*1da177e4SLinus Torvalds 	if(devices_found >= 2) {
268*1da177e4SLinus Torvalds 		/* Combine the two flash banks into a single MTD device & register it: */
269*1da177e4SLinus Torvalds 		merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1");
270*1da177e4SLinus Torvalds 		if(merged_mtd)
271*1da177e4SLinus Torvalds 			add_mtd_device(merged_mtd);
272*1da177e4SLinus Torvalds 	}
273*1da177e4SLinus Torvalds 	if(devices_found == 3) /* register the third (DIL-Flash) device */
274*1da177e4SLinus Torvalds 		add_mtd_device(mymtd[2]);
275*1da177e4SLinus Torvalds 	return(devices_found ? 0 : -ENXIO);
276*1da177e4SLinus Torvalds }
277*1da177e4SLinus Torvalds 
278*1da177e4SLinus Torvalds static void __exit cleanup_sc520cdp(void)
279*1da177e4SLinus Torvalds {
280*1da177e4SLinus Torvalds 	int i;
281*1da177e4SLinus Torvalds 
282*1da177e4SLinus Torvalds 	if (merged_mtd) {
283*1da177e4SLinus Torvalds 		del_mtd_device(merged_mtd);
284*1da177e4SLinus Torvalds 		mtd_concat_destroy(merged_mtd);
285*1da177e4SLinus Torvalds 	}
286*1da177e4SLinus Torvalds 	if (mymtd[2])
287*1da177e4SLinus Torvalds 		del_mtd_device(mymtd[2]);
288*1da177e4SLinus Torvalds 
289*1da177e4SLinus Torvalds 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
290*1da177e4SLinus Torvalds 		if (mymtd[i])
291*1da177e4SLinus Torvalds 			map_destroy(mymtd[i]);
292*1da177e4SLinus Torvalds 		if (sc520cdp_map[i].virt) {
293*1da177e4SLinus Torvalds 			iounmap(sc520cdp_map[i].virt);
294*1da177e4SLinus Torvalds 			sc520cdp_map[i].virt = NULL;
295*1da177e4SLinus Torvalds 		}
296*1da177e4SLinus Torvalds 	}
297*1da177e4SLinus Torvalds }
298*1da177e4SLinus Torvalds 
299*1da177e4SLinus Torvalds module_init(init_sc520cdp);
300*1da177e4SLinus Torvalds module_exit(cleanup_sc520cdp);
301*1da177e4SLinus Torvalds 
302*1da177e4SLinus Torvalds MODULE_LICENSE("GPL");
303*1da177e4SLinus Torvalds MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
304*1da177e4SLinus Torvalds MODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform");
305