]>
Commit | Line | Data |
---|---|---|
2cbe571a | 1 | /* |
9d5028c2 WD |
2 | * (C) Copyright 2003-2004 |
3 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
2cbe571a | 4 | * |
9d5028c2 WD |
5 | * (C) Copyright 2004 |
6 | * Martin Krause, TQ-Systems GmbH, [email protected] | |
7 | * | |
8 | * Modified for the CMC PU2 by (C) Copyright 2004 Gary Jennejohn | |
9 | * [email protected] | |
2cbe571a WD |
10 | * |
11 | * See file CREDITS for list of people who contributed to this | |
12 | * project. | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or | |
15 | * modify it under the terms of the GNU General Public License as | |
16 | * published by the Free Software Foundation; either version 2 of | |
17 | * the License, or (at your option) any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, write to the Free Software | |
26 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
27 | * MA 02111-1307 USA | |
28 | */ | |
29 | ||
30 | #include <common.h> | |
31 | ||
45ea3fca WD |
32 | #ifndef CFG_ENV_ADDR |
33 | #define CFG_ENV_ADDR (CFG_FLASH_BASE + CFG_ENV_OFFSET) | |
34 | #endif | |
35 | ||
9d5028c2 | 36 | flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
2cbe571a | 37 | |
9d5028c2 | 38 | #define FLASH_CYCLE1 0x0555 |
25d6712a | 39 | #define FLASH_CYCLE2 0x02AA |
2cbe571a WD |
40 | |
41 | /*----------------------------------------------------------------------- | |
9d5028c2 | 42 | * Functions |
2cbe571a | 43 | */ |
08f27278 | 44 | static ulong flash_get_size(vu_short *addr, flash_info_t *info); |
9d5028c2 | 45 | static void flash_reset(flash_info_t *info); |
08f27278 | 46 | static int write_word_amd(flash_info_t *info, vu_short *dest, ushort data); |
9d5028c2 | 47 | static flash_info_t *flash_get_info(ulong base); |
2cbe571a | 48 | |
9d5028c2 WD |
49 | /*----------------------------------------------------------------------- |
50 | * flash_init() | |
51 | * | |
52 | * sets up flash_info and returns size of FLASH (bytes) | |
53 | */ | |
54 | unsigned long flash_init (void) | |
2cbe571a | 55 | { |
9d5028c2 WD |
56 | unsigned long size = 0; |
57 | ulong flashbase = CFG_FLASH_BASE; | |
58 | ||
59 | /* Init: no FLASHes known */ | |
60 | memset(&flash_info[0], 0, sizeof(flash_info_t)); | |
61 | ||
08f27278 | 62 | flash_info[0].size = flash_get_size((vu_short *)flashbase, &flash_info[0]); |
9d5028c2 WD |
63 | |
64 | size = flash_info[0].size; | |
65 | ||
66 | #if CFG_MONITOR_BASE >= CFG_FLASH_BASE | |
67 | /* monitor protection ON by default */ | |
68 | flash_protect(FLAG_PROTECT_SET, | |
69 | CFG_MONITOR_BASE, | |
70 | CFG_MONITOR_BASE+monitor_flash_len-1, | |
71 | flash_get_info(CFG_MONITOR_BASE)); | |
72 | #endif | |
73 | ||
74 | #ifdef CFG_ENV_IS_IN_FLASH | |
75 | /* ENV protection ON by default */ | |
76 | flash_protect(FLAG_PROTECT_SET, | |
77 | CFG_ENV_ADDR, | |
78 | CFG_ENV_ADDR+CFG_ENV_SIZE-1, | |
79 | flash_get_info(CFG_ENV_ADDR)); | |
80 | #endif | |
81 | ||
82 | return size ? size : 1; | |
2cbe571a WD |
83 | } |
84 | ||
9d5028c2 WD |
85 | /*----------------------------------------------------------------------- |
86 | */ | |
87 | static void flash_reset(flash_info_t *info) | |
2cbe571a | 88 | { |
08f27278 | 89 | vu_short *base = (vu_short *)(info->start[0]); |
2cbe571a | 90 | |
9d5028c2 WD |
91 | /* Put FLASH back in read mode */ |
92 | if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) | |
08f27278 | 93 | *base = 0x00FF; /* Intel Read Mode */ |
9d5028c2 | 94 | else if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_AMD) |
08f27278 | 95 | *base = 0x00F0; /* AMD Read Mode */ |
2cbe571a WD |
96 | } |
97 | ||
9d5028c2 WD |
98 | /*----------------------------------------------------------------------- |
99 | */ | |
2cbe571a | 100 | |
9d5028c2 | 101 | static flash_info_t *flash_get_info(ulong base) |
2cbe571a | 102 | { |
9d5028c2 WD |
103 | int i; |
104 | flash_info_t * info; | |
2cbe571a | 105 | |
9d5028c2 WD |
106 | info = NULL; |
107 | for (i = 0; i < CFG_MAX_FLASH_BANKS; i ++) { | |
108 | info = & flash_info[i]; | |
109 | if (info->size && info->start[0] <= base && | |
110 | base <= info->start[0] + info->size - 1) | |
111 | break; | |
2cbe571a WD |
112 | } |
113 | ||
9d5028c2 | 114 | return i == CFG_MAX_FLASH_BANKS ? 0 : info; |
2cbe571a WD |
115 | } |
116 | ||
117 | /*----------------------------------------------------------------------- | |
118 | */ | |
9d5028c2 WD |
119 | |
120 | void flash_print_info (flash_info_t *info) | |
2cbe571a WD |
121 | { |
122 | int i; | |
123 | ||
9d5028c2 WD |
124 | if (info->flash_id == FLASH_UNKNOWN) { |
125 | printf ("missing or unknown FLASH type\n"); | |
126 | return; | |
127 | } | |
128 | ||
2cbe571a | 129 | switch (info->flash_id & FLASH_VENDMASK) { |
9d5028c2 WD |
130 | case FLASH_MAN_AMD: printf ("AMD "); break; |
131 | case FLASH_MAN_BM: printf ("BRIGHT MICRO "); break; | |
132 | case FLASH_MAN_FUJ: printf ("FUJITSU "); break; | |
133 | case FLASH_MAN_SST: printf ("SST "); break; | |
134 | case FLASH_MAN_STM: printf ("STM "); break; | |
135 | case FLASH_MAN_INTEL: printf ("INTEL "); break; | |
136 | default: printf ("Unknown Vendor "); break; | |
2cbe571a WD |
137 | } |
138 | ||
139 | switch (info->flash_id & FLASH_TYPEMASK) { | |
9d5028c2 WD |
140 | case FLASH_S29GL064M: |
141 | printf ("S29GL064M-R6 (64Mbit, uniform sector size)\n"); | |
2cbe571a WD |
142 | break; |
143 | default: | |
144 | printf ("Unknown Chip Type\n"); | |
2cbe571a WD |
145 | break; |
146 | } | |
147 | ||
148 | printf (" Size: %ld MB in %d Sectors\n", | |
9d5028c2 WD |
149 | info->size >> 20, |
150 | info->sector_count); | |
2cbe571a WD |
151 | |
152 | printf (" Sector Start Addresses:"); | |
9d5028c2 WD |
153 | |
154 | for (i=0; i<info->sector_count; ++i) { | |
2cbe571a WD |
155 | if ((i % 5) == 0) { |
156 | printf ("\n "); | |
157 | } | |
9d5028c2 WD |
158 | printf (" %08lX%s", |
159 | info->start[i], | |
2cbe571a WD |
160 | info->protect[i] ? " (RO)" : " "); |
161 | } | |
162 | printf ("\n"); | |
9d5028c2 | 163 | return; |
2cbe571a WD |
164 | } |
165 | ||
166 | /*----------------------------------------------------------------------- | |
167 | */ | |
168 | ||
9d5028c2 WD |
169 | /* |
170 | * The following code cannot be run from FLASH! | |
171 | */ | |
2cbe571a | 172 | |
08f27278 | 173 | ulong flash_get_size (vu_short *addr, flash_info_t *info) |
9d5028c2 WD |
174 | { |
175 | int i; | |
08f27278 | 176 | ushort value; |
9d5028c2 | 177 | ulong base = (ulong)addr; |
2cbe571a | 178 | |
08f27278 WD |
179 | /* Write auto select command sequence */ |
180 | addr[FLASH_CYCLE1] = 0x00AA; /* for AMD, Intel ignores this */ | |
181 | addr[FLASH_CYCLE2] = 0x0055; /* for AMD, Intel ignores this */ | |
182 | addr[FLASH_CYCLE1] = 0x0090; /* selects Intel or AMD */ | |
2cbe571a | 183 | |
08f27278 | 184 | /* read Manufacturer ID */ |
9d5028c2 | 185 | udelay(100); |
08f27278 WD |
186 | value = addr[0]; |
187 | debug ("Manufacturer ID: %04X\n", value); | |
2cbe571a | 188 | |
08f27278 WD |
189 | switch (value) { |
190 | ||
191 | case (AMD_MANUFACT & 0xFFFF): | |
45ea3fca | 192 | debug ("Manufacturer: AMD (Spansion)\n"); |
9d5028c2 WD |
193 | info->flash_id = FLASH_MAN_AMD; |
194 | break; | |
2cbe571a | 195 | |
08f27278 | 196 | case (INTEL_MANUFACT & 0xFFFF): |
45ea3fca | 197 | debug ("Manufacturer: Intel (not supported yet)\n"); |
9d5028c2 WD |
198 | info->flash_id = FLASH_MAN_INTEL; |
199 | break; | |
2cbe571a | 200 | |
9d5028c2 | 201 | default: |
08f27278 | 202 | printf ("Unknown Manufacturer ID: %04X\n", value); |
9d5028c2 WD |
203 | info->flash_id = FLASH_UNKNOWN; |
204 | info->sector_count = 0; | |
205 | info->size = 0; | |
08f27278 | 206 | goto out; |
9d5028c2 | 207 | } |
2cbe571a | 208 | |
08f27278 WD |
209 | value = addr[1]; |
210 | debug ("Device ID: %04X\n", value); | |
9d5028c2 | 211 | |
08f27278 WD |
212 | switch (addr[1]) { |
213 | ||
214 | case (AMD_ID_MIRROR & 0xFFFF): | |
45ea3fca | 215 | debug ("Mirror Bit flash: addr[14] = %08X addr[15] = %08X\n", |
9d5028c2 WD |
216 | addr[14], addr[15]); |
217 | ||
08f27278 WD |
218 | switch(addr[14]) { |
219 | case (AMD_ID_GL064M_2 & 0xFFFF): | |
9d5028c2 WD |
220 | if (addr[15] != (AMD_ID_GL064M_3 & 0xffff)) { |
221 | printf ("Chip: S29GLxxxM -> unknown\n"); | |
222 | info->flash_id = FLASH_UNKNOWN; | |
223 | info->sector_count = 0; | |
224 | info->size = 0; | |
225 | } else { | |
45ea3fca | 226 | debug ("Chip: S29GL064M-R6\n"); |
9d5028c2 WD |
227 | info->flash_id += FLASH_S29GL064M; |
228 | info->sector_count = 128; | |
229 | info->size = 0x00800000; | |
230 | for (i = 0; i < info->sector_count; i++) { | |
231 | info->start[i] = base; | |
232 | base += 0x10000; | |
233 | } | |
234 | } | |
235 | break; /* => 16 MB */ | |
236 | default: | |
237 | printf ("Chip: *** unknown ***\n"); | |
238 | info->flash_id = FLASH_UNKNOWN; | |
239 | info->sector_count = 0; | |
240 | info->size = 0; | |
241 | break; | |
242 | } | |
243 | break; | |
2cbe571a | 244 | |
9d5028c2 | 245 | default: |
08f27278 | 246 | printf ("Unknown Device ID: %04X\n", value); |
9d5028c2 WD |
247 | info->flash_id = FLASH_UNKNOWN; |
248 | info->sector_count = 0; | |
249 | info->size = 0; | |
08f27278 | 250 | break; |
9d5028c2 | 251 | } |
2cbe571a | 252 | |
08f27278 | 253 | out: |
9d5028c2 WD |
254 | /* Put FLASH back in read mode */ |
255 | flash_reset(info); | |
2cbe571a | 256 | |
9d5028c2 WD |
257 | return (info->size); |
258 | } | |
2cbe571a | 259 | |
9d5028c2 WD |
260 | /*----------------------------------------------------------------------- |
261 | */ | |
2cbe571a | 262 | |
9d5028c2 WD |
263 | int flash_erase (flash_info_t *info, int s_first, int s_last) |
264 | { | |
08f27278 | 265 | vu_short *addr = (vu_short *)(info->start[0]); |
25d6712a | 266 | int flag, prot, sect, ssect, l_sect; |
e2ffd59b | 267 | ulong now, last; |
2cbe571a | 268 | |
45ea3fca | 269 | debug ("flash_erase: first: %d last: %d\n", s_first, s_last); |
2cbe571a | 270 | |
9d5028c2 WD |
271 | if ((s_first < 0) || (s_first > s_last)) { |
272 | if (info->flash_id == FLASH_UNKNOWN) { | |
273 | printf ("- missing\n"); | |
274 | } else { | |
275 | printf ("- no sectors to erase\n"); | |
45ea3fca | 276 | } |
9d5028c2 | 277 | return 1; |
45ea3fca | 278 | } |
2cbe571a | 279 | |
9d5028c2 WD |
280 | if ((info->flash_id == FLASH_UNKNOWN) || |
281 | (info->flash_id > FLASH_AMD_COMP)) { | |
282 | printf ("Can't erase unknown flash type %08lx - aborted\n", | |
283 | info->flash_id); | |
284 | return 1; | |
2cbe571a WD |
285 | } |
286 | ||
9d5028c2 WD |
287 | prot = 0; |
288 | for (sect=s_first; sect<=s_last; ++sect) { | |
289 | if (info->protect[sect]) { | |
290 | prot++; | |
291 | } | |
292 | } | |
2cbe571a | 293 | |
9d5028c2 WD |
294 | if (prot) { |
295 | printf ("- Warning: %d protected sectors will not be erased!\n", | |
296 | prot); | |
297 | } else { | |
298 | printf ("\n"); | |
299 | } | |
2cbe571a | 300 | |
9d5028c2 WD |
301 | /* Disable interrupts which might cause a timeout here */ |
302 | flag = disable_interrupts(); | |
2cbe571a | 303 | |
25d6712a WD |
304 | /* |
305 | * Start erase on unprotected sectors. | |
306 | * Since the flash can erase multiple sectors with one command | |
307 | * we take advantage of that by doing the erase in chunks of | |
308 | * 3 sectors. | |
309 | */ | |
310 | for (sect = s_first; sect <= s_last; ) { | |
311 | l_sect = -1; | |
312 | ||
313 | addr[FLASH_CYCLE1] = 0x00AA; | |
314 | addr[FLASH_CYCLE2] = 0x0055; | |
315 | addr[FLASH_CYCLE1] = 0x0080; | |
316 | addr[FLASH_CYCLE1] = 0x00AA; | |
317 | addr[FLASH_CYCLE2] = 0x0055; | |
318 | ||
319 | /* do the erase in chunks of at most 3 sectors */ | |
320 | for (ssect = 0; ssect < 3; ssect++) { | |
321 | if ((sect + ssect) > s_last) | |
322 | break; | |
323 | if (info->protect[sect + ssect] == 0) { /* not protected */ | |
08f27278 | 324 | addr = (vu_short *)(info->start[sect + ssect]); |
25d6712a WD |
325 | addr[0] = 0x0030; |
326 | l_sect = sect + ssect; | |
327 | } | |
9d5028c2 | 328 | } |
25d6712a WD |
329 | /* wait at least 80us - let's wait 1 ms */ |
330 | udelay (1000); | |
331 | ||
332 | /* | |
333 | * We wait for the last triggered sector | |
334 | */ | |
335 | if (l_sect < 0) | |
336 | goto DONE; | |
337 | ||
400ab719 | 338 | reset_timer_masked (); |
e2ffd59b | 339 | last = 0; |
08f27278 | 340 | addr = (vu_short *)(info->start[l_sect]); |
25d6712a | 341 | while ((addr[0] & 0x0080) != 0x0080) { |
400ab719 | 342 | if ((now = get_timer_masked ()) > CFG_FLASH_ERASE_TOUT) { |
25d6712a WD |
343 | printf ("Timeout\n"); |
344 | return 1; | |
345 | } | |
346 | /* show that we're waiting */ | |
347 | if ((now - last) > 1000) { /* every second */ | |
348 | putc ('.'); | |
349 | last = now; | |
350 | } | |
351 | } | |
08f27278 | 352 | addr = (vu_short *)info->start[0]; |
25d6712a WD |
353 | addr[0] = 0x00F0; /* reset bank */ |
354 | sect += ssect; | |
9d5028c2 | 355 | } |
2cbe571a | 356 | |
9d5028c2 WD |
357 | /* re-enable interrupts if necessary */ |
358 | if (flag) | |
359 | enable_interrupts(); | |
2cbe571a | 360 | |
9d5028c2 WD |
361 | DONE: |
362 | /* reset to read mode */ | |
08f27278 | 363 | addr = (vu_short *)info->start[0]; |
25d6712a | 364 | addr[0] = 0x00F0; /* reset bank */ |
2cbe571a | 365 | |
9d5028c2 WD |
366 | printf (" done\n"); |
367 | return 0; | |
2cbe571a WD |
368 | } |
369 | ||
370 | /*----------------------------------------------------------------------- | |
9d5028c2 WD |
371 | * Copy memory to flash, returns: |
372 | * 0 - OK | |
373 | * 1 - write timeout | |
374 | * 2 - Flash not erased | |
2cbe571a WD |
375 | */ |
376 | ||
9d5028c2 | 377 | int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) |
2cbe571a WD |
378 | { |
379 | ulong wp, data; | |
380 | int rc; | |
381 | ||
382 | if (addr & 1) { | |
383 | printf ("unaligned destination not supported\n"); | |
384 | return ERR_ALIGN; | |
385 | }; | |
386 | ||
387 | if ((int) src & 1) { | |
388 | printf ("unaligned source not supported\n"); | |
389 | return ERR_ALIGN; | |
390 | }; | |
391 | ||
392 | wp = addr; | |
393 | ||
394 | while (cnt >= 2) { | |
08f27278 WD |
395 | data = *((vu_short *)src); |
396 | if ((rc = write_word_amd(info, (vu_short *)wp, data)) != 0) { | |
400ab719 | 397 | printf ("write_buff 1: write_word_amd() rc=%d\n", rc); |
2cbe571a WD |
398 | return (rc); |
399 | } | |
400 | src += 2; | |
401 | wp += 2; | |
402 | cnt -= 2; | |
403 | } | |
404 | ||
9d5028c2 | 405 | if (cnt == 0) { |
400ab719 | 406 | return (ERR_OK); |
9d5028c2 WD |
407 | } |
408 | ||
2cbe571a | 409 | if (cnt == 1) { |
400ab719 | 410 | data = (*((volatile u8 *) src)) | (*((volatile u8 *) (wp + 1)) << 8); |
08f27278 | 411 | if ((rc = write_word_amd(info, (vu_short *)wp, data)) != 0) { |
400ab719 | 412 | printf ("write_buff 1: write_word_amd() rc=%d\n", rc); |
2cbe571a WD |
413 | return (rc); |
414 | } | |
415 | src += 1; | |
416 | wp += 1; | |
417 | cnt -= 1; | |
9d5028c2 | 418 | } |
2cbe571a WD |
419 | |
420 | return ERR_OK; | |
421 | } | |
9d5028c2 WD |
422 | |
423 | /*----------------------------------------------------------------------- | |
424 | * Write a word to Flash for AMD FLASH | |
425 | * A word is 16 or 32 bits, whichever the bus width of the flash bank | |
426 | * (not an individual chip) is. | |
427 | * | |
428 | * returns: | |
429 | * 0 - OK | |
430 | * 1 - write timeout | |
431 | * 2 - Flash not erased | |
432 | */ | |
08f27278 | 433 | static int write_word_amd (flash_info_t *info, vu_short *dest, ushort data) |
9d5028c2 | 434 | { |
9d5028c2 | 435 | int flag; |
08f27278 | 436 | vu_short *base; /* first address in flash bank */ |
9d5028c2 WD |
437 | |
438 | /* Check if Flash is (sufficiently) erased */ | |
439 | if ((*dest & data) != data) { | |
440 | return (2); | |
441 | } | |
442 | ||
08f27278 | 443 | base = (vu_short *)(info->start[0]); |
9d5028c2 WD |
444 | |
445 | /* Disable interrupts which might cause a timeout here */ | |
446 | flag = disable_interrupts(); | |
447 | ||
08f27278 WD |
448 | base[FLASH_CYCLE1] = 0x00AA; /* unlock */ |
449 | base[FLASH_CYCLE2] = 0x0055; /* unlock */ | |
450 | base[FLASH_CYCLE1] = 0x00A0; /* selects program mode */ | |
9d5028c2 WD |
451 | |
452 | *dest = data; /* start programming the data */ | |
453 | ||
454 | /* re-enable interrupts if necessary */ | |
455 | if (flag) | |
456 | enable_interrupts(); | |
457 | ||
400ab719 | 458 | reset_timer_masked (); |
9d5028c2 WD |
459 | |
460 | /* data polling for D7 */ | |
08f27278 | 461 | while ((*dest & 0x0080) != (data & 0x0080)) { |
400ab719 | 462 | if (get_timer_masked () > CFG_FLASH_WRITE_TOUT) { |
08f27278 | 463 | *dest = 0x00F0; /* reset bank */ |
9d5028c2 WD |
464 | return (1); |
465 | } | |
466 | } | |
467 | return (0); | |
468 | } |