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