blob: 1722a35c95cba29ca9a2bfcb9dc2941a39e523f0 [file] [log] [blame]
Heiko Schocherf5e0d032006-06-19 11:02:41 +02001/*
2 * flash.c
3 * -------
Wolfgang Denkb87dfd22006-07-19 13:50:38 +02004 *
Heiko Schocherf5e0d032006-06-19 11:02:41 +02005 * Flash programming routines for the Wind River PPMC 74xx/7xx
6 * based on flash.c from the TQM8260 board.
Wolfgang Denkb87dfd22006-07-19 13:50:38 +02007 *
Heiko Schocherf5e0d032006-06-19 11:02:41 +02008 * 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 */
19static int write_dword (flash_info_t* info, ulong dest, unsigned char *pdata);
20static void write_via_fpu (volatile DWORD* addr, DWORD* data);
21
22flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
23
24/*-----------------------------------------------------------------------
25 */
26void flash_reset (void)
27{
28 unsigned long msr;
29 DWORD cmd_reset = 0x00F000F000F000F0LL;
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020030
Heiko Schocherf5e0d032006-06-19 11:02:41 +020031 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 );
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020036
Heiko Schocherf5e0d032006-06-19 11:02:41 +020037 set_msr (msr);
38 }
39}
40
41/*-----------------------------------------------------------------------
42 */
43ulong 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 ();
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020053 set_msr (msr | MSR_FP);
54
Heiko Schocherf5e0d032006-06-19 11:02:41 +020055 /* 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] );
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020059
Heiko Schocherf5e0d032006-06-19 11:02:41 +020060 /* Restore FPU */
61 set_msr (msr);
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020062
Heiko Schocherf5e0d032006-06-19 11:02:41 +020063 /* 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:
Wolfgang Denkb87dfd22006-07-19 13:50:38 +020073 /* No, faulty or unknown flash */
Heiko Schocherf5e0d032006-06-19 11:02:41 +020074 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 */
174unsigned long flash_init (void)
175{
176 unsigned long size_b0 = 0;
177 int i;
178
179 /* Init: no FLASHes known */
180 for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) {
181 flash_info[i].flash_id = FLASH_UNKNOWN;
182 }
183
184 /* Static FLASH Bank configuration here (only one bank) */
185 size_b0 = flash_get_size (CFG_FLASH_BASE, &flash_info[0]);
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 */
194#if CFG_MONITOR_BASE >= CFG_FLASH_BASE
195 flash_protect (FLAG_PROTECT_SET,
196 CFG_MONITOR_BASE,
197 CFG_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]);
198#endif
199
Jean-Christophe PLAGNIOL-VILLARD0e8d1582008-09-10 22:48:06 +0200200#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
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200203# endif
204 flash_protect (FLAG_PROTECT_SET,
Jean-Christophe PLAGNIOL-VILLARD0e8d1582008-09-10 22:48:06 +0200205 CONFIG_ENV_ADDR,
206 CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200207#endif
208
209 return (size_b0);
210}
211
212/*-----------------------------------------------------------------------
213 */
214void 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 */
286int 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 };
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200294
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200295 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 );
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200322
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200323 /* 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);
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200347
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200348 /* 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 {
362 if ((now = get_timer (start)) > CFG_FLASH_ERASE_TOUT) {
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 ();
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200376
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200377 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
389int 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 */
441static 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 };
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200449
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200450 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 }
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200457
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200458 /* Enable FPU */
459 msr = get_msr();
460 set_msr( msr | MSR_FP );
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200461
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200462 /* 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);
Wolfgang Denkb87dfd22006-07-19 13:50:38 +0200476
Heiko Schocherf5e0d032006-06-19 11:02:41 +0200477 /* data polling for D7 */
478 start = get_timer (0);
479 while (*(volatile DWORD*)dest != data ) {
480 if (get_timer (start) > CFG_FLASH_WRITE_TOUT) {
481 return (1);
482 }
483 }
484 return (0);
485}
486
487/*-----------------------------------------------------------------------
488 */
489static 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}