blob: 24bcd7c0a3f21e377e6e3d1b8913fb67e5251d16 [file] [log] [blame]
wdenkaffae2b2002-08-17 09:36:01 +00001/*
2 * (C) Copyright 2000
3 * Marius Groeger <mgroeger@sysgo.de>
4 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
5 *
6 * (C) Copyright 2000
7 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
8 *
9 * Flash Routines for AMD 29F080B devices
10 * Added support for 64bit and AMD 29DL323B
11 *
12 *--------------------------------------------------------------------
Wolfgang Denk1a459662013-07-08 09:37:19 +020013 * SPDX-License-Identifier: GPL-2.0+
wdenkaffae2b2002-08-17 09:36:01 +000014 */
15
16#include <common.h>
17#include <mpc8xx.h>
18#include <asm/io.h>
19
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020020flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
wdenkaffae2b2002-08-17 09:36:01 +000021
22#define RD_SWP32(x) in_le32((volatile u32*)x)
23
24/*-----------------------------------------------------------------------
25 * Functions
26 */
27
28static ulong flash_get_size (vu_long *addr, flash_info_t *info);
29static int write_word (flash_info_t *info, ulong dest, ulong data);
30
31/*-----------------------------------------------------------------------
32 */
33
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000034unsigned long flash_init(void)
wdenkaffae2b2002-08-17 09:36:01 +000035{
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000036 int i;
wdenkaffae2b2002-08-17 09:36:01 +000037
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000038 /* Init: no FLASHes known */
39 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i)
40 flash_info[i].flash_id = FLASH_UNKNOWN;
wdenkaffae2b2002-08-17 09:36:01 +000041
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000042 /* for now, only support the 4 MB Flash SIMM */
Wolfgang Denk7b490cf2011-11-05 05:13:13 +000043 (void)flash_get_size((vu_long *) CONFIG_SYS_FLASH0_BASE,
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000044 &flash_info[0]);
wdenkaffae2b2002-08-17 09:36:01 +000045
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000046 /*
47 * protect monitor and environment sectors
48 */
wdenkaffae2b2002-08-17 09:36:01 +000049
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020050#if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH0_BASE
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000051 flash_protect(FLAG_PROTECT_SET,
52 CONFIG_SYS_MONITOR_BASE,
53 CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
54 &flash_info[0]);
wdenkaffae2b2002-08-17 09:36:01 +000055#endif
56
Jean-Christophe PLAGNIOL-VILLARD0e8d1582008-09-10 22:48:06 +020057#if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR)
Wolfgang Denk2bf22a52011-11-05 05:13:12 +000058#ifndef CONFIG_ENV_SIZE
59#define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE
60#endif
61 flash_protect(FLAG_PROTECT_SET,
62 CONFIG_ENV_ADDR,
63 CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
wdenkaffae2b2002-08-17 09:36:01 +000064#endif
65
Wolfgang Denk7b490cf2011-11-05 05:13:13 +000066 return CONFIG_SYS_FLASH0_SIZE * 1024 * 1024;
wdenkaffae2b2002-08-17 09:36:01 +000067}
68
69/*-----------------------------------------------------------------------
70 */
71void flash_print_info (flash_info_t *info)
72{
73 int i;
74
75 if (info->flash_id == FLASH_UNKNOWN) {
76 printf ("missing or unknown FLASH type\n");
77 return;
78 }
79
80 switch (info->flash_id & FLASH_VENDMASK) {
81 case (AMD_MANUFACT & FLASH_VENDMASK):
82 printf ("AMD ");
83 break;
84 case (FUJ_MANUFACT & FLASH_VENDMASK):
85 printf ("FUJITSU ");
86 break;
87 case (SST_MANUFACT & FLASH_VENDMASK):
88 printf ("SST ");
89 break;
90 default:
91 printf ("Unknown Vendor ");
92 break;
93 }
94
95 switch (info->flash_id & FLASH_TYPEMASK) {
96 case (AMD_ID_DL323B & FLASH_TYPEMASK):
97 printf("AM29DL323B (32 MBit)\n");
98 break;
99 default:
100 printf ("Unknown Chip Type\n");
101 break;
102 }
103
104 printf (" Size: %ld MB in %d Sectors\n",
105 info->size >> 20, info->sector_count);
106
107 printf (" Sector Start Addresses:");
108 for (i = 0; i < info->sector_count; ++i) {
109 if ((i % 5) == 0) printf ("\n ");
110 printf (" %08lX%s",
111 info->start[i],
112 info->protect[i] ? " (RO)" : " "
113 );
114 }
115 printf ("\n");
116 return;
117}
118
119/*
120 * The following code cannot be run from FLASH!
121 */
122
123static ulong flash_get_size (vu_long *addr, flash_info_t *info)
124{
125 short i;
126 vu_long vendor[2], devid[2];
127 ulong base = (ulong)addr;
128
129 /* Reset and Write auto select command: read Manufacturer ID */
130 addr[0] = 0xf0f0f0f0;
131 addr[2 * 0x0555] = 0xAAAAAAAA;
132 addr[2 * 0x02AA] = 0x55555555;
133 addr[2 * 0x0555] = 0x90909090;
134 addr[1] = 0xf0f0f0f0;
135 addr[2 * 0x0555 + 1] = 0xAAAAAAAA;
136 addr[2 * 0x02AA + 1] = 0x55555555;
137 addr[2 * 0x0555 + 1] = 0x90909090;
138 udelay (1000);
139
140 vendor[0] = RD_SWP32(&addr[0]);
141 vendor[1] = RD_SWP32(&addr[1]);
142 if (vendor[0] != vendor[1] || vendor[0] != AMD_MANUFACT) {
143 info->size = 0;
144 goto out;
145 }
146
147 devid[0] = RD_SWP32(&addr[2]);
148 devid[1] = RD_SWP32(&addr[3]);
149
150 if (devid[0] == AMD_ID_DL323B) {
151 /*
152 * we have 2 Banks
153 * Bank 1 (23 Sectors): 0-7=8kbyte, 8-22=64kbyte
154 * Bank 2 (48 Sectors): 23-70=64kbyte
155 */
156 info->flash_id = (AMD_MANUFACT & FLASH_VENDMASK) |
wdenk8bde7f72003-06-27 21:31:46 +0000157 (AMD_ID_DL323B & FLASH_TYPEMASK);
wdenkaffae2b2002-08-17 09:36:01 +0000158 info->sector_count = 71;
159 info->size = 4 * (8 * 8 + 63 * 64) * 1024;
160 }
161 else {
162 info->size = 0;
163 goto out;
164 }
165
166 /* set up sector start address table */
167 for (i = 0; i < 8; i++) {
wdenk8bde7f72003-06-27 21:31:46 +0000168 info->start[i] = base + (i * 0x8000);
wdenkaffae2b2002-08-17 09:36:01 +0000169 }
170 for (i = 8; i < info->sector_count; i++) {
wdenk8bde7f72003-06-27 21:31:46 +0000171 info->start[i] = base + (i * 0x40000) + 8 * 0x8000 - 8 * 0x40000;
wdenkaffae2b2002-08-17 09:36:01 +0000172 }
173
174 /* check for protected sectors */
175 for (i = 0; i < info->sector_count; i++) {
wdenk8bde7f72003-06-27 21:31:46 +0000176 /* read sector protection at sector address */
wdenkaffae2b2002-08-17 09:36:01 +0000177 addr = (volatile unsigned long *)(info->start[i]);
wdenk8bde7f72003-06-27 21:31:46 +0000178 addr[2 * 0x0555] = 0xAAAAAAAA;
wdenkaffae2b2002-08-17 09:36:01 +0000179 addr[2 * 0x02AA] = 0x55555555;
180 addr[2 * 0x0555] = 0x90909090;
181 addr[2 * 0x0555 + 1] = 0xAAAAAAAA;
182 addr[2 * 0x02AA + 1] = 0x55555555;
183 addr[2 * 0x0555 + 1] = 0x90909090;
184 udelay (1000);
wdenk8bde7f72003-06-27 21:31:46 +0000185 base = RD_SWP32(&addr[4]);
wdenkaffae2b2002-08-17 09:36:01 +0000186 base |= RD_SWP32(&addr[5]);
187 info->protect[i] = base & 0x00010001 ? 1 : 0;
188 }
189 addr = (vu_long*)info->start[0];
190
191out:
192 /* reset command */
193 addr[0] = 0xf0f0f0f0;
194 addr[1] = 0xf0f0f0f0;
195
196 return info->size;
197}
198
199
200/*-----------------------------------------------------------------------
201 */
202
203int flash_erase (flash_info_t *info, int s_first, int s_last)
204{
205 vu_long *addr = (vu_long*)(info->start[0]);
206 int flag, prot, sect, l_sect;
207 ulong start, now, last;
208
209 if ((s_first < 0) || (s_first > s_last)) {
210 if (info->flash_id == FLASH_UNKNOWN) {
211 printf ("- missing\n");
212 } else {
213 printf ("- no sectors to erase\n");
214 }
215 return 1;
216 }
217
218 prot = 0;
219 for (sect = s_first; sect <= s_last; sect++) {
220 if (info->protect[sect]) {
221 prot++;
222 }
223 }
224
225 if (prot) {
226 printf ("- Warning: %d protected sectors will not be erased!\n",
227 prot);
228 } else {
229 printf ("\n");
230 }
231
232 l_sect = -1;
233
234 /* Disable interrupts which might cause a timeout here */
235 flag = disable_interrupts();
236
237 addr[2 * 0x0555] = 0xAAAAAAAA;
238 addr[2 * 0x02AA] = 0x55555555;
239 addr[2 * 0x0555] = 0x80808080;
240 addr[2 * 0x0555] = 0xAAAAAAAA;
241 addr[2 * 0x02AA] = 0x55555555;
242 addr[2 * 0x0555 + 1] = 0xAAAAAAAA;
243 addr[2 * 0x02AA + 1] = 0x55555555;
244 addr[2 * 0x0555 + 1] = 0x80808080;
245 addr[2 * 0x0555 + 1] = 0xAAAAAAAA;
246 addr[2 * 0x02AA + 1] = 0x55555555;
247 udelay (100);
248
249 /* Start erase on unprotected sectors */
250 for (sect = s_first; sect<=s_last; sect++) {
251 if (info->protect[sect] == 0) { /* not protected */
252 addr = (vu_long*)(info->start[sect]);
253 addr[0] = 0x30303030;
254 addr[1] = 0x30303030;
255 l_sect = sect;
256 }
257 }
258
259 /* re-enable interrupts if necessary */
260 if (flag)
261 enable_interrupts();
262
263 /* wait at least 80us - let's wait 1 ms */
264 udelay (1000);
265
266 /*
267 * We wait for the last triggered sector
268 */
269 if (l_sect < 0)
270 goto DONE;
271
272 start = get_timer (0);
273 last = start;
274 addr = (vu_long*)(info->start[l_sect]);
275 while ( (addr[0] & 0x80808080) != 0x80808080 ||
276 (addr[1] & 0x80808080) != 0x80808080) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200277 if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
wdenkaffae2b2002-08-17 09:36:01 +0000278 printf ("Timeout\n");
279 return 1;
280 }
281 /* show that we're waiting */
282 if ((now - last) > 1000) { /* every second */
283 serial_putc ('.');
284 last = now;
285 }
286 }
287
288 DONE:
289 /* reset to read mode */
290 addr = (volatile unsigned long *)info->start[0];
291 addr[0] = 0xF0F0F0F0; /* reset bank */
292 addr[1] = 0xF0F0F0F0; /* reset bank */
293
294 printf (" done\n");
295 return 0;
296}
297
298/*-----------------------------------------------------------------------
299 * Copy memory to flash, returns:
300 * 0 - OK
301 * 1 - write timeout
302 * 2 - Flash not erased
303 */
304
305int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
306{
307 ulong cp, wp, data;
308 int i, l, rc;
309
310 wp = (addr & ~3); /* get lower word aligned address */
311
312 /*
313 * handle unaligned start bytes
314 */
315 if ((l = addr - wp) != 0) {
316 data = 0;
317 for (i=0, cp=wp; i<l; ++i, ++cp) {
318 data = (data << 8) | (*(uchar *)cp);
319 }
320 for (; i<4 && cnt>0; ++i) {
321 data = (data << 8) | *src++;
322 --cnt;
323 ++cp;
324 }
325 for (; cnt==0 && i<4; ++i, ++cp) {
326 data = (data << 8) | (*(uchar *)cp);
327 }
328
329 if ((rc = write_word(info, wp, data)) != 0) {
330 return (rc);
331 }
332 wp += 4;
333 }
334
335 /*
336 * handle word aligned part
337 */
338 while (cnt >= 4) {
339 data = 0;
340 for (i=0; i<4; ++i) {
341 data = (data << 8) | *src++;
342 }
343 if ((rc = write_word(info, wp, data)) != 0) {
344 return (rc);
345 }
346 wp += 4;
347 cnt -= 4;
348 }
349
350 if (cnt == 0) {
351 return (0);
352 }
353
354 /*
355 * handle unaligned tail bytes
356 */
357 data = 0;
358 for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) {
359 data = (data << 8) | *src++;
360 --cnt;
361 }
362 for (; i<4; ++i, ++cp) {
363 data = (data << 8) | (*(uchar *)cp);
364 }
365
366 return (write_word(info, wp, data));
367}
368
369/*-----------------------------------------------------------------------
370 * Write a word to Flash, returns:
371 * 0 - OK
372 * 1 - write timeout
373 * 2 - Flash not erased
374 */
375static int write_word (flash_info_t *info, ulong dest, ulong data)
376{
377 vu_long *addr = (vu_long*)(info->start[0]);
378 ulong start;
379 int flag;
380
381 /* Check if Flash is (sufficiently) erased */
382 if ((*((vu_long *)dest) & data) != data) {
383 return (2);
384 }
385 /* Disable interrupts which might cause a timeout here */
386 flag = disable_interrupts();
387
388 if ((dest & 0x00000004) == 0) {
wdenk8bde7f72003-06-27 21:31:46 +0000389 addr[2 * 0x0555] = 0xAAAAAAAA;
wdenkaffae2b2002-08-17 09:36:01 +0000390 addr[2 * 0x02AA] = 0x55555555;
wdenk8bde7f72003-06-27 21:31:46 +0000391 addr[2 * 0x0555] = 0xA0A0A0A0;
wdenkaffae2b2002-08-17 09:36:01 +0000392 }
393 else {
wdenk8bde7f72003-06-27 21:31:46 +0000394 addr[2 * 0x0555 + 1] = 0xAAAAAAAA;
wdenkaffae2b2002-08-17 09:36:01 +0000395 addr[2 * 0x02AA + 1] = 0x55555555;
wdenk8bde7f72003-06-27 21:31:46 +0000396 addr[2 * 0x0555 + 1] = 0xA0A0A0A0;
wdenkaffae2b2002-08-17 09:36:01 +0000397 }
398
399 *((vu_long *)dest) = data;
400
401 /* re-enable interrupts if necessary */
402 if (flag)
403 enable_interrupts();
404
405 /* data polling for D7 */
406 start = get_timer (0);
407 while ((*((vu_long *)dest) & 0x80808080) != (data & 0x80808080)) {
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200408 if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
wdenkaffae2b2002-08-17 09:36:01 +0000409 return (1);
410 }
411 }
412 return (0);
413}
414
415/*-----------------------------------------------------------------------
416 */