]>
Commit | Line | Data |
---|---|---|
f5e0d039 HS |
1 | /* |
2 | * flash.c | |
3 | * ------- | |
b87dfd28 | 4 | * |
f5e0d039 HS |
5 | * Flash programming routines for the Wind River PPMC 74xx/7xx |
6 | * based on flash.c from the TQM8260 board. | |
b87dfd28 | 7 | * |
f5e0d039 HS |
8 | * By Richard Danter (richard.danter@windriver.com) |
9 | * Copyright (C) 2005 Wind River Systems | |
10 | */ | |
11 | ||
12 | #include <common.h> | |
13 | #include <asm/processor.h> | |
14 | #include <74xx_7xx.h> | |
15 | ||
16 | #define DWORD unsigned long long | |
17 | ||
18 | /* Local function prototypes */ | |
19 | static int write_dword (flash_info_t* info, ulong dest, unsigned char *pdata); | |
20 | static void write_via_fpu (volatile DWORD* addr, DWORD* data); | |
21 | ||
6d0f6bcf | 22 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; |
f5e0d039 HS |
23 | |
24 | /*----------------------------------------------------------------------- | |
25 | */ | |
26 | void flash_reset (void) | |
27 | { | |
28 | unsigned long msr; | |
29 | DWORD cmd_reset = 0x00F000F000F000F0LL; | |
b87dfd28 | 30 | |
f5e0d039 HS |
31 | if (flash_info[0].flash_id != FLASH_UNKNOWN) { |
32 | msr = get_msr (); | |
33 | set_msr (msr | MSR_FP); | |
34 | ||
35 | write_via_fpu ((DWORD*)flash_info[0].start[0], &cmd_reset ); | |
b87dfd28 | 36 | |
f5e0d039 HS |
37 | set_msr (msr); |
38 | } | |
39 | } | |
40 | ||
41 | /*----------------------------------------------------------------------- | |
42 | */ | |
43 | ulong flash_get_size (ulong baseaddr, flash_info_t * info) | |
44 | { | |
45 | int i; | |
46 | unsigned long msr; | |
47 | DWORD flashtest; | |
48 | DWORD cmd_select[3] = { 0x00AA00AA00AA00AALL, 0x0055005500550055LL, | |
49 | 0x0090009000900090LL }; | |
50 | ||
51 | /* Enable FPU */ | |
52 | msr = get_msr (); | |
b87dfd28 WD |
53 | set_msr (msr | MSR_FP); |
54 | ||
f5e0d039 HS |
55 | /* Write auto-select command sequence */ |
56 | write_via_fpu ((DWORD*)(baseaddr + (0x0555 << 3)), &cmd_select[0] ); | |
57 | write_via_fpu ((DWORD*)(baseaddr + (0x02AA << 3)), &cmd_select[1] ); | |
58 | write_via_fpu ((DWORD*)(baseaddr + (0x0555 << 3)), &cmd_select[2] ); | |
b87dfd28 | 59 | |
f5e0d039 HS |
60 | /* Restore FPU */ |
61 | set_msr (msr); | |
b87dfd28 | 62 | |
f5e0d039 HS |
63 | /* Read manufacturer ID */ |
64 | flashtest = *(volatile DWORD*)baseaddr; | |
65 | switch ((int)flashtest) { | |
66 | case AMD_MANUFACT: | |
67 | info->flash_id = FLASH_MAN_AMD; | |
68 | break; | |
69 | case FUJ_MANUFACT: | |
70 | info->flash_id = FLASH_MAN_FUJ; | |
71 | break; | |
72 | default: | |
b87dfd28 | 73 | /* No, faulty or unknown flash */ |
f5e0d039 HS |
74 | info->flash_id = FLASH_UNKNOWN; |
75 | info->sector_count = 0; | |
76 | info->size = 0; | |
77 | return (0); | |
78 | } | |
79 | ||
80 | /* Read device ID */ | |
81 | flashtest = *(volatile DWORD*)(baseaddr + 8); | |
82 | switch ((long)flashtest) { | |
83 | case AMD_ID_LV800T: | |
84 | info->flash_id += FLASH_AM800T; | |
85 | info->sector_count = 19; | |
86 | info->size = 0x00400000; | |
87 | break; | |
88 | case AMD_ID_LV800B: | |
89 | info->flash_id += FLASH_AM800B; | |
90 | info->sector_count = 19; | |
91 | info->size = 0x00400000; | |
92 | break; | |
93 | case AMD_ID_LV160T: | |
94 | info->flash_id += FLASH_AM160T; | |
95 | info->sector_count = 35; | |
96 | info->size = 0x00800000; | |
97 | break; | |
98 | case AMD_ID_LV160B: | |
99 | info->flash_id += FLASH_AM160B; | |
100 | info->sector_count = 35; | |
101 | info->size = 0x00800000; | |
102 | break; | |
103 | case AMD_ID_DL322T: | |
104 | info->flash_id += FLASH_AMDL322T; | |
105 | info->sector_count = 71; | |
106 | info->size = 0x01000000; | |
107 | break; | |
108 | case AMD_ID_DL322B: | |
109 | info->flash_id += FLASH_AMDL322B; | |
110 | info->sector_count = 71; | |
111 | info->size = 0x01000000; | |
112 | break; | |
113 | case AMD_ID_DL323T: | |
114 | info->flash_id += FLASH_AMDL323T; | |
115 | info->sector_count = 71; | |
116 | info->size = 0x01000000; | |
117 | break; | |
118 | case AMD_ID_DL323B: | |
119 | info->flash_id += FLASH_AMDL323B; | |
120 | info->sector_count = 71; | |
121 | info->size = 0x01000000; | |
122 | break; | |
123 | case AMD_ID_LV640U: | |
124 | info->flash_id += FLASH_AM640U; | |
125 | info->sector_count = 128; | |
126 | info->size = 0x02000000; | |
127 | break; | |
128 | default: | |
129 | /* Unknown flash type */ | |
130 | info->flash_id = FLASH_UNKNOWN; | |
131 | return (0); | |
132 | } | |
133 | ||
134 | if ((long)flashtest == AMD_ID_LV640U) { | |
135 | /* set up sector start adress table (uniform sector type) */ | |
136 | for (i = 0; i < info->sector_count; i++) | |
137 | info->start[i] = baseaddr + (i * 0x00040000); | |
138 | } else if (info->flash_id & FLASH_BTYPE) { | |
139 | /* set up sector start adress table (bottom sector type) */ | |
140 | info->start[0] = baseaddr + 0x00000000; | |
141 | info->start[1] = baseaddr + 0x00010000; | |
142 | info->start[2] = baseaddr + 0x00018000; | |
143 | info->start[3] = baseaddr + 0x00020000; | |
144 | for (i = 4; i < info->sector_count; i++) { | |
145 | info->start[i] = baseaddr + (i * 0x00040000) - 0x000C0000; | |
146 | } | |
147 | } else { | |
148 | /* set up sector start adress table (top sector type) */ | |
149 | i = info->sector_count - 1; | |
150 | info->start[i--] = baseaddr + info->size - 0x00010000; | |
151 | info->start[i--] = baseaddr + info->size - 0x00018000; | |
152 | info->start[i--] = baseaddr + info->size - 0x00020000; | |
153 | for (; i >= 0; i--) { | |
154 | info->start[i] = baseaddr + i * 0x00040000; | |
155 | } | |
156 | } | |
157 | ||
158 | /* check for protected sectors */ | |
159 | for (i = 0; i < info->sector_count; i++) { | |
160 | /* read sector protection at sector address, (A7 .. A0) = 0x02 */ | |
161 | if (*(volatile DWORD*)(info->start[i] + 16) & 0x0001000100010001LL) { | |
162 | info->protect[i] = 1; /* D0 = 1 if protected */ | |
163 | } else { | |
164 | info->protect[i] = 0; | |
165 | } | |
166 | } | |
167 | ||
168 | flash_reset (); | |
169 | return (info->size); | |
170 | } | |
171 | ||
172 | /*----------------------------------------------------------------------- | |
173 | */ | |
174 | unsigned long flash_init (void) | |
175 | { | |
176 | unsigned long size_b0 = 0; | |
177 | int i; | |
178 | ||
179 | /* Init: no FLASHes known */ | |
6d0f6bcf | 180 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { |
f5e0d039 HS |
181 | flash_info[i].flash_id = FLASH_UNKNOWN; |
182 | } | |
183 | ||
184 | /* Static FLASH Bank configuration here (only one bank) */ | |
6d0f6bcf | 185 | size_b0 = flash_get_size (CONFIG_SYS_FLASH_BASE, &flash_info[0]); |
f5e0d039 HS |
186 | if (flash_info[0].flash_id == FLASH_UNKNOWN || size_b0 == 0) { |
187 | printf ("## Unknown FLASH on Bank 0 - Size = 0x%08lx = %ld MB\n", | |
188 | size_b0, size_b0 >> 20); | |
189 | } | |
190 | ||
191 | /* | |
192 | * protect monitor and environment sectors | |
193 | */ | |
6d0f6bcf | 194 | #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE |
f5e0d039 | 195 | flash_protect (FLAG_PROTECT_SET, |
6d0f6bcf JCPV |
196 | CONFIG_SYS_MONITOR_BASE, |
197 | CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]); | |
f5e0d039 HS |
198 | #endif |
199 | ||
0e8d1586 JCPV |
200 | #if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR) |
201 | # ifndef CONFIG_ENV_SIZE | |
202 | # define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE | |
f5e0d039 HS |
203 | # endif |
204 | flash_protect (FLAG_PROTECT_SET, | |
0e8d1586 JCPV |
205 | CONFIG_ENV_ADDR, |
206 | CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]); | |
f5e0d039 HS |
207 | #endif |
208 | ||
209 | return (size_b0); | |
210 | } | |
211 | ||
212 | /*----------------------------------------------------------------------- | |
213 | */ | |
214 | void flash_print_info (flash_info_t * info) | |
215 | { | |
216 | int i; | |
217 | ||
218 | if (info->flash_id == FLASH_UNKNOWN) { | |
219 | printf ("missing or unknown FLASH type\n"); | |
220 | return; | |
221 | } | |
222 | ||
223 | switch (info->flash_id & FLASH_VENDMASK) { | |
224 | case FLASH_MAN_AMD: | |
225 | printf ("AMD "); | |
226 | break; | |
227 | case FLASH_MAN_FUJ: | |
228 | printf ("FUJITSU "); | |
229 | break; | |
230 | default: | |
231 | printf ("Unknown Vendor "); | |
232 | break; | |
233 | } | |
234 | ||
235 | switch (info->flash_id & FLASH_TYPEMASK) { | |
236 | case FLASH_AM800T: | |
237 | printf ("29LV800T (8 M, top sector)\n"); | |
238 | break; | |
239 | case FLASH_AM800B: | |
240 | printf ("29LV800T (8 M, bottom sector)\n"); | |
241 | break; | |
242 | case FLASH_AM160T: | |
243 | printf ("29LV160T (16 M, top sector)\n"); | |
244 | break; | |
245 | case FLASH_AM160B: | |
246 | printf ("29LV160B (16 M, bottom sector)\n"); | |
247 | break; | |
248 | case FLASH_AMDL322T: | |
249 | printf ("29DL322T (32 M, top sector)\n"); | |
250 | break; | |
251 | case FLASH_AMDL322B: | |
252 | printf ("29DL322B (32 M, bottom sector)\n"); | |
253 | break; | |
254 | case FLASH_AMDL323T: | |
255 | printf ("29DL323T (32 M, top sector)\n"); | |
256 | break; | |
257 | case FLASH_AMDL323B: | |
258 | printf ("29DL323B (32 M, bottom sector)\n"); | |
259 | break; | |
260 | case FLASH_AM640U: | |
261 | printf ("29LV640D (64 M, uniform sector)\n"); | |
262 | break; | |
263 | default: | |
264 | printf ("Unknown Chip Type\n"); | |
265 | break; | |
266 | } | |
267 | ||
268 | printf (" Size: %ld MB in %d Sectors\n", | |
269 | info->size >> 20, info->sector_count); | |
270 | ||
271 | printf (" Sector Start Addresses:"); | |
272 | for (i = 0; i < info->sector_count; ++i) { | |
273 | if ((i % 5) == 0) | |
274 | printf ("\n "); | |
275 | printf (" %08lX%s", | |
276 | info->start[i], | |
277 | info->protect[i] ? " (RO)" : " " | |
278 | ); | |
279 | } | |
280 | printf ("\n"); | |
281 | return; | |
282 | } | |
283 | ||
284 | /*----------------------------------------------------------------------- | |
285 | */ | |
286 | int flash_erase (flash_info_t * info, int s_first, int s_last) | |
287 | { | |
288 | int flag, prot, sect, l_sect; | |
289 | ulong start, now, last; | |
290 | unsigned long msr; | |
291 | DWORD cmd_erase[6] = { 0x00AA00AA00AA00AALL, 0x0055005500550055LL, | |
292 | 0x0080008000800080LL, 0x00AA00AA00AA00AALL, | |
293 | 0x0055005500550055LL, 0x0030003000300030LL }; | |
b87dfd28 | 294 | |
f5e0d039 HS |
295 | if ((s_first < 0) || (s_first > s_last)) { |
296 | if (info->flash_id == FLASH_UNKNOWN) { | |
297 | printf ("- missing\n"); | |
298 | } else { | |
299 | printf ("- no sectors to erase\n"); | |
300 | } | |
301 | return 1; | |
302 | } | |
303 | ||
304 | prot = 0; | |
305 | for (sect = s_first; sect <= s_last; sect++) { | |
306 | if (info->protect[sect]) | |
307 | prot++; | |
308 | } | |
309 | ||
310 | if (prot) { | |
311 | printf ("- Warning: %d protected sectors will not be erased!\n", | |
312 | prot); | |
313 | } else { | |
314 | printf ("\n"); | |
315 | } | |
316 | ||
317 | l_sect = -1; | |
318 | ||
319 | /* Enable FPU */ | |
320 | msr = get_msr(); | |
321 | set_msr ( msr | MSR_FP ); | |
b87dfd28 | 322 | |
f5e0d039 HS |
323 | /* Disable interrupts which might cause a timeout here */ |
324 | flag = disable_interrupts (); | |
325 | ||
326 | write_via_fpu ((DWORD*)(info->start[0] + (0x0555 << 3)), &cmd_erase[0] ); | |
327 | write_via_fpu ((DWORD*)(info->start[0] + (0x02AA << 3)), &cmd_erase[1] ); | |
328 | write_via_fpu ((DWORD*)(info->start[0] + (0x0555 << 3)), &cmd_erase[2] ); | |
329 | write_via_fpu ((DWORD*)(info->start[0] + (0x0555 << 3)), &cmd_erase[3] ); | |
330 | write_via_fpu ((DWORD*)(info->start[0] + (0x02AA << 3)), &cmd_erase[4] ); | |
331 | udelay (1000); | |
332 | ||
333 | /* Start erase on unprotected sectors */ | |
334 | for (sect = s_first; sect <= s_last; sect++) { | |
335 | if (info->protect[sect] == 0) { /* not protected */ | |
336 | write_via_fpu ((DWORD*)info->start[sect], &cmd_erase[5] ); | |
337 | l_sect = sect; | |
338 | } | |
339 | } | |
340 | ||
341 | /* re-enable interrupts if necessary */ | |
342 | if (flag) | |
343 | enable_interrupts (); | |
344 | ||
345 | /* Restore FPU */ | |
346 | set_msr (msr); | |
b87dfd28 | 347 | |
f5e0d039 HS |
348 | /* wait at least 80us - let's wait 1 ms */ |
349 | udelay (1000); | |
350 | ||
351 | /* | |
352 | * We wait for the last triggered sector | |
353 | */ | |
354 | if (l_sect < 0) | |
355 | goto DONE; | |
356 | ||
357 | start = get_timer (0); | |
358 | last = start; | |
359 | while ((*(volatile DWORD*)info->start[l_sect] & 0x0080008000800080LL ) | |
360 | != 0x0080008000800080LL ) | |
361 | { | |
6d0f6bcf | 362 | if ((now = get_timer (start)) > CONFIG_SYS_FLASH_ERASE_TOUT) { |
f5e0d039 HS |
363 | printf ("Timeout\n"); |
364 | return 1; | |
365 | } | |
366 | /* show that we're waiting */ | |
367 | if ((now - last) > 1000) { /* every second */ | |
368 | serial_putc ('.'); | |
369 | last = now; | |
370 | } | |
371 | } | |
372 | ||
373 | DONE: | |
374 | /* reset to read mode */ | |
375 | flash_reset (); | |
b87dfd28 | 376 | |
f5e0d039 HS |
377 | printf (" done\n"); |
378 | return 0; | |
379 | } | |
380 | ||
381 | ||
382 | /*----------------------------------------------------------------------- | |
383 | * Copy memory to flash, returns: | |
384 | * 0 - OK | |
385 | * 1 - write timeout | |
386 | * 2 - Flash not erased | |
387 | */ | |
388 | ||
389 | int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) | |
390 | { | |
391 | ulong dp; | |
392 | static unsigned char bb[8]; | |
393 | int i, l, rc, cc = cnt; | |
394 | ||
395 | dp = (addr & ~7); /* get lower dword aligned address */ | |
396 | ||
397 | /* | |
398 | * handle unaligned start bytes | |
399 | */ | |
400 | if ((l = addr - dp) != 0) { | |
401 | for (i = 0; i < 8; i++) | |
402 | bb[i] = (i < l || (i - l) >= cc) ? *(char*)(dp + i) : *src++; | |
403 | if ((rc = write_dword (info, dp, bb)) != 0) { | |
404 | return (rc); | |
405 | } | |
406 | dp += 8; | |
407 | cc -= 8 - l; | |
408 | } | |
409 | ||
410 | /* | |
411 | * handle word aligned part | |
412 | */ | |
413 | while (cc >= 8) { | |
414 | if ((rc = write_dword (info, dp, src)) != 0) { | |
415 | return (rc); | |
416 | } | |
417 | dp += 8; | |
418 | src += 8; | |
419 | cc -= 8; | |
420 | } | |
421 | ||
422 | if (cc <= 0) { | |
423 | return (0); | |
424 | } | |
425 | ||
426 | /* | |
427 | * handle unaligned tail bytes | |
428 | */ | |
429 | for (i = 0; i < 8; i++) { | |
430 | bb[i] = (i < cc) ? *src++ : *(char*)(dp + i); | |
431 | } | |
432 | return (write_dword (info, dp, bb)); | |
433 | } | |
434 | ||
435 | /*----------------------------------------------------------------------- | |
436 | * Write a dword to Flash, returns: | |
437 | * 0 - OK | |
438 | * 1 - write timeout | |
439 | * 2 - Flash not erased | |
440 | */ | |
441 | static int write_dword (flash_info_t * info, ulong dest, unsigned char *pdata) | |
442 | { | |
443 | ulong start; | |
444 | unsigned long msr; | |
445 | int flag, i; | |
446 | DWORD data; | |
447 | DWORD cmd_write[3] = { 0x00AA00AA00AA00AALL, 0x0055005500550055LL, | |
448 | 0x00A000A000A000A0LL }; | |
b87dfd28 | 449 | |
f5e0d039 HS |
450 | for (data = 0, i = 0; i < 8; i++) |
451 | data = (data << 8) + *pdata++; | |
452 | ||
453 | /* Check if Flash is (sufficiently) erased */ | |
454 | if ((*(DWORD*)dest & data) != data) { | |
455 | return (2); | |
456 | } | |
b87dfd28 | 457 | |
f5e0d039 HS |
458 | /* Enable FPU */ |
459 | msr = get_msr(); | |
460 | set_msr( msr | MSR_FP ); | |
b87dfd28 | 461 | |
f5e0d039 HS |
462 | /* Disable interrupts which might cause a timeout here */ |
463 | flag = disable_interrupts (); | |
464 | ||
465 | write_via_fpu ((DWORD*)(info->start[0] + (0x0555 << 3)), &cmd_write[0] ); | |
466 | write_via_fpu ((DWORD*)(info->start[0] + (0x02AA << 3)), &cmd_write[1] ); | |
467 | write_via_fpu ((DWORD*)(info->start[0] + (0x0555 << 3)), &cmd_write[2] ); | |
468 | write_via_fpu ((DWORD*)dest, &data ); | |
469 | ||
470 | /* re-enable interrupts if necessary */ | |
471 | if (flag) | |
472 | enable_interrupts (); | |
473 | ||
474 | /* Restore FPU */ | |
475 | set_msr(msr); | |
b87dfd28 | 476 | |
f5e0d039 HS |
477 | /* data polling for D7 */ |
478 | start = get_timer (0); | |
479 | while (*(volatile DWORD*)dest != data ) { | |
6d0f6bcf | 480 | if (get_timer (start) > CONFIG_SYS_FLASH_WRITE_TOUT) { |
f5e0d039 HS |
481 | return (1); |
482 | } | |
483 | } | |
484 | return (0); | |
485 | } | |
486 | ||
487 | /*----------------------------------------------------------------------- | |
488 | */ | |
489 | static void write_via_fpu (volatile DWORD* addr, DWORD* data) | |
490 | { | |
491 | __asm__ __volatile__ ("lfd 1, 0(%0)"::"r" (data)); | |
492 | __asm__ __volatile__ ("stfd 1, 0(%0)"::"r" (addr)); | |
493 | __asm__ __volatile__ ("eieio"); | |
494 | } |