]>
Commit | Line | Data |
---|---|---|
affae2bf | 1 | /* |
250b66e2 | 2 | * (C) Copyright 2000-2011 |
affae2bf WD |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
affae2bf WD |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <mpc8xx.h> | |
10 | ||
098a8a83 | 11 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; |
affae2bf WD |
12 | |
13 | /*----------------------------------------------------------------------- | |
14 | * Functions | |
15 | */ | |
098a8a83 WD |
16 | static ulong flash_get_size(vu_long *addr, flash_info_t *info); |
17 | static int write_word(flash_info_t *info, ulong dest, ulong data); | |
18 | static void flash_get_offsets(ulong base, flash_info_t *info); | |
affae2bf WD |
19 | |
20 | /*----------------------------------------------------------------------- | |
21 | */ | |
22 | ||
098a8a83 | 23 | unsigned long flash_init(void) |
affae2bf | 24 | { |
250b66e2 | 25 | unsigned long size_b0; |
affae2bf WD |
26 | int i; |
27 | ||
28 | /* Init: no FLASHes known */ | |
098a8a83 WD |
29 | for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) |
30 | flash_info[i].flash_id = FLASH_UNKNOWN; | |
affae2bf WD |
31 | |
32 | /* Detect size */ | |
098a8a83 WD |
33 | size_b0 = flash_get_size((vu_long *)CONFIG_SYS_FLASH_BASE, |
34 | &flash_info[0]); | |
affae2bf WD |
35 | |
36 | /* Setup offsets */ | |
098a8a83 | 37 | flash_get_offsets(CONFIG_SYS_FLASH_BASE, &flash_info[0]); |
affae2bf | 38 | |
6d0f6bcf | 39 | #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE |
affae2bf WD |
40 | /* Monitor protection ON by default */ |
41 | flash_protect(FLAG_PROTECT_SET, | |
6d0f6bcf JCPV |
42 | CONFIG_SYS_MONITOR_BASE, |
43 | CONFIG_SYS_MONITOR_BASE+monitor_flash_len-1, | |
affae2bf WD |
44 | &flash_info[0]); |
45 | #endif | |
46 | ||
affae2bf | 47 | flash_info[0].size = size_b0; |
affae2bf | 48 | |
250b66e2 | 49 | return size_b0; |
affae2bf WD |
50 | } |
51 | ||
52 | /*----------------------------------------------------------------------- | |
53 | * Fix this to support variable sector sizes | |
54 | */ | |
098a8a83 | 55 | static void flash_get_offsets(ulong base, flash_info_t *info) |
affae2bf WD |
56 | { |
57 | int i; | |
58 | ||
59 | /* set up sector start address table */ | |
60 | if ((info->flash_id & FLASH_TYPEMASK) == FLASH_AM040) { | |
61 | /* set sector offsets for bottom boot block type */ | |
62 | for (i = 0; i < info->sector_count; i++) | |
63 | info->start[i] = base + (i * 0x00010000); | |
64 | } | |
65 | } | |
66 | ||
67 | /*----------------------------------------------------------------------- | |
68 | */ | |
098a8a83 | 69 | void flash_print_info(flash_info_t *info) |
affae2bf WD |
70 | { |
71 | int i; | |
72 | ||
098a8a83 WD |
73 | if (info->flash_id == FLASH_UNKNOWN) { |
74 | puts("missing or unknown FLASH type\n"); | |
affae2bf WD |
75 | return; |
76 | } | |
77 | ||
098a8a83 WD |
78 | switch (info->flash_id & FLASH_VENDMASK) { |
79 | case FLASH_MAN_AMD: | |
80 | printf("AMD "); | |
81 | break; | |
82 | case FLASH_MAN_FUJ: | |
83 | printf("FUJITSU "); | |
84 | break; | |
85 | case FLASH_MAN_BM: | |
86 | printf("BRIGHT MICRO "); | |
87 | break; | |
88 | default: | |
89 | printf("Unknown Vendor "); | |
90 | break; | |
affae2bf WD |
91 | } |
92 | ||
098a8a83 WD |
93 | switch (info->flash_id & FLASH_TYPEMASK) { |
94 | case FLASH_AM040: | |
95 | printf("29F040 or 29LV040 (4 Mbit, uniform sectors)\n"); | |
96 | break; | |
97 | case FLASH_AM400B: | |
98 | printf("AM29LV400B (4 Mbit, bottom boot sect)\n"); | |
99 | break; | |
100 | case FLASH_AM400T: | |
101 | printf("AM29LV400T (4 Mbit, top boot sector)\n"); | |
102 | break; | |
103 | case FLASH_AM800B: | |
104 | printf("AM29LV800B (8 Mbit, bottom boot sect)\n"); | |
105 | break; | |
106 | case FLASH_AM800T: | |
107 | printf("AM29LV800T (8 Mbit, top boot sector)\n"); | |
108 | break; | |
109 | case FLASH_AM160B: | |
110 | printf("AM29LV160B (16 Mbit, bottom boot sect)\n"); | |
111 | break; | |
112 | case FLASH_AM160T: | |
113 | printf("AM29LV160T (16 Mbit, top boot sector)\n"); | |
114 | break; | |
115 | case FLASH_AM320B: | |
116 | printf("AM29LV320B (32 Mbit, bottom boot sect)\n"); | |
117 | break; | |
118 | case FLASH_AM320T: | |
119 | printf("AM29LV320T (32 Mbit, top boot sector)\n"); | |
120 | break; | |
121 | default: | |
122 | printf("Unknown Chip Type\n"); | |
123 | break; | |
affae2bf WD |
124 | } |
125 | ||
126 | if (info->size >> 20) { | |
098a8a83 WD |
127 | printf(" Size: %ld MB in %d Sectors\n", |
128 | info->size >> 20, | |
129 | info->sector_count); | |
affae2bf | 130 | } else { |
098a8a83 WD |
131 | printf(" Size: %ld KB in %d Sectors\n", |
132 | info->size >> 10, | |
133 | info->sector_count); | |
affae2bf WD |
134 | } |
135 | ||
098a8a83 | 136 | puts(" Sector Start Addresses:"); |
affae2bf | 137 | |
098a8a83 | 138 | for (i = 0; i < info->sector_count; ++i) { |
affae2bf | 139 | if ((i % 5) == 0) |
098a8a83 | 140 | puts("\n "); |
affae2bf | 141 | |
098a8a83 | 142 | printf(" %08lX%s", |
affae2bf WD |
143 | info->start[i], |
144 | info->protect[i] ? " (RO)" : " "); | |
145 | } | |
146 | ||
098a8a83 | 147 | putc('\n'); |
affae2bf WD |
148 | return; |
149 | } | |
150 | /*----------------------------------------------------------------------- | |
151 | */ | |
152 | ||
153 | /* | |
154 | * The following code cannot be run from FLASH! | |
155 | */ | |
156 | ||
098a8a83 | 157 | static ulong flash_get_size(vu_long *addr, flash_info_t *info) |
affae2bf WD |
158 | { |
159 | short i; | |
160 | volatile unsigned char *caddr; | |
161 | char value; | |
162 | ||
163 | caddr = (volatile unsigned char *)addr ; | |
164 | ||
165 | /* Write auto select command: read Manufacturer ID */ | |
166 | ||
250b66e2 | 167 | debug("Base address is: %8p\n", caddr); |
affae2bf WD |
168 | |
169 | caddr[0x0555] = 0xAA; | |
170 | caddr[0x02AA] = 0x55; | |
171 | caddr[0x0555] = 0x90; | |
172 | ||
173 | value = caddr[0]; | |
174 | ||
098a8a83 | 175 | debug("Manufact ID: %02x\n", value); |
affae2bf | 176 | |
098a8a83 WD |
177 | switch (value) { |
178 | case 0x1: /* AMD_MANUFACT */ | |
179 | info->flash_id = FLASH_MAN_AMD; | |
180 | break; | |
181 | case 0x4: /* FUJ_MANUFACT */ | |
182 | info->flash_id = FLASH_MAN_FUJ; | |
183 | break; | |
184 | default: | |
185 | info->flash_id = FLASH_UNKNOWN; | |
186 | info->sector_count = 0; | |
187 | info->size = 0; | |
affae2bf | 188 | break; |
affae2bf WD |
189 | } |
190 | ||
191 | value = caddr[1]; /* device ID */ | |
affae2bf | 192 | |
098a8a83 WD |
193 | debug("Device ID: %02x\n", value); |
194 | ||
195 | switch (value) { | |
196 | case AMD_ID_LV040B: | |
197 | info->flash_id += FLASH_AM040; | |
198 | info->sector_count = 8; | |
199 | info->size = 0x00080000; | |
200 | break; /* => 512Kb */ | |
201 | ||
202 | default: | |
203 | info->flash_id = FLASH_UNKNOWN; | |
204 | return 0; /* => no or unknown flash */ | |
affae2bf WD |
205 | } |
206 | ||
098a8a83 | 207 | flash_get_offsets((ulong)addr, &flash_info[0]); |
affae2bf WD |
208 | |
209 | /* check for protected sectors */ | |
098a8a83 WD |
210 | for (i = 0; i < info->sector_count; i++) { |
211 | /* | |
212 | * read sector protection at sector address, | |
213 | * (A7 .. A0) = 0x02 | |
214 | * D0 = 1 if protected | |
215 | */ | |
affae2bf WD |
216 | caddr = (volatile unsigned char *)(info->start[i]); |
217 | info->protect[i] = caddr[2] & 1; | |
218 | } | |
219 | ||
220 | /* | |
221 | * Prevent writes to uninitialized FLASH. | |
222 | */ | |
098a8a83 | 223 | if (info->flash_id != FLASH_UNKNOWN) { |
affae2bf WD |
224 | caddr = (volatile unsigned char *)info->start[0]; |
225 | *caddr = 0xF0; /* reset bank */ | |
226 | } | |
227 | ||
098a8a83 | 228 | return info->size; |
affae2bf WD |
229 | } |
230 | ||
098a8a83 | 231 | int flash_erase(flash_info_t *info, int s_first, int s_last) |
affae2bf | 232 | { |
098a8a83 WD |
233 | volatile unsigned char *addr = |
234 | (volatile unsigned char *)(info->start[0]); | |
affae2bf WD |
235 | int flag, prot, sect, l_sect; |
236 | ulong start, now, last; | |
237 | ||
238 | if ((s_first < 0) || (s_first > s_last)) { | |
098a8a83 WD |
239 | if (info->flash_id == FLASH_UNKNOWN) |
240 | printf("- missing\n"); | |
241 | else | |
242 | printf("- no sectors to erase\n"); | |
243 | ||
affae2bf WD |
244 | return 1; |
245 | } | |
246 | ||
247 | if ((info->flash_id == FLASH_UNKNOWN) || | |
248 | (info->flash_id > FLASH_AMD_COMP)) { | |
098a8a83 | 249 | printf("Can't erase unknown flash type - aborted\n"); |
affae2bf WD |
250 | return 1; |
251 | } | |
252 | ||
253 | prot = 0; | |
098a8a83 WD |
254 | for (sect = s_first; sect <= s_last; ++sect) { |
255 | if (info->protect[sect]) | |
affae2bf | 256 | prot++; |
affae2bf WD |
257 | } |
258 | ||
259 | if (prot) { | |
098a8a83 | 260 | printf("- Warning: %d protected sectors will not be erased!\n", |
affae2bf WD |
261 | prot); |
262 | } else { | |
098a8a83 | 263 | printf("\n"); |
affae2bf WD |
264 | } |
265 | ||
266 | l_sect = -1; | |
267 | ||
268 | /* Disable interrupts which might cause a timeout here */ | |
269 | flag = disable_interrupts(); | |
270 | ||
271 | addr[0x0555] = 0xAA; | |
272 | addr[0x02AA] = 0x55; | |
273 | addr[0x0555] = 0x80; | |
274 | addr[0x0555] = 0xAA; | |
275 | addr[0x02AA] = 0x55; | |
276 | ||
277 | /* Start erase on unprotected sectors */ | |
098a8a83 | 278 | for (sect = s_first; sect <= s_last; sect++) { |
affae2bf WD |
279 | if (info->protect[sect] == 0) { /* not protected */ |
280 | addr = (volatile unsigned char *)(info->start[sect]); | |
281 | addr[0] = 0x30; | |
282 | l_sect = sect; | |
283 | } | |
284 | } | |
285 | ||
286 | /* re-enable interrupts if necessary */ | |
287 | if (flag) | |
288 | enable_interrupts(); | |
289 | ||
290 | /* wait at least 80us - let's wait 1 ms */ | |
098a8a83 | 291 | udelay(1000); |
affae2bf WD |
292 | |
293 | /* | |
294 | * We wait for the last triggered sector | |
295 | */ | |
296 | if (l_sect < 0) | |
297 | goto DONE; | |
298 | ||
098a8a83 | 299 | start = get_timer(0); |
affae2bf WD |
300 | last = start; |
301 | addr = (volatile unsigned char *)(info->start[l_sect]); | |
302 | ||
098a8a83 WD |
303 | while ((addr[0] & 0xFF) != 0xFF) { |
304 | ||
305 | now = get_timer(start); | |
306 | ||
307 | if (now > CONFIG_SYS_FLASH_ERASE_TOUT) { | |
308 | printf("Timeout\n"); | |
affae2bf WD |
309 | return 1; |
310 | } | |
311 | /* show that we're waiting */ | |
312 | if ((now - last) > 1000) { /* every second */ | |
098a8a83 | 313 | putc('.'); |
affae2bf WD |
314 | last = now; |
315 | } | |
316 | } | |
317 | ||
318 | DONE: | |
319 | /* reset to read mode */ | |
320 | addr = (volatile unsigned char *)info->start[0]; | |
321 | ||
322 | addr[0] = 0xF0; /* reset bank */ | |
323 | ||
098a8a83 | 324 | printf(" done\n"); |
affae2bf WD |
325 | return 0; |
326 | } | |
327 | ||
328 | /*----------------------------------------------------------------------- | |
329 | * Copy memory to flash, returns: | |
330 | * 0 - OK | |
331 | * 1 - write timeout | |
332 | * 2 - Flash not erased | |
333 | */ | |
334 | ||
098a8a83 | 335 | int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) |
affae2bf WD |
336 | { |
337 | ulong cp, wp, data; | |
338 | int i, l, rc; | |
339 | ||
340 | wp = (addr & ~3); /* get lower word aligned address */ | |
341 | ||
342 | /* | |
343 | * handle unaligned start bytes | |
344 | */ | |
098a8a83 WD |
345 | l = addr - wp; |
346 | ||
347 | if (l != 0) { | |
affae2bf | 348 | data = 0; |
098a8a83 | 349 | for (i = 0, cp = wp; i < l; ++i, ++cp) |
affae2bf | 350 | data = (data << 8) | (*(uchar *)cp); |
098a8a83 WD |
351 | |
352 | for (; i < 4 && cnt > 0; ++i) { | |
affae2bf WD |
353 | data = (data << 8) | *src++; |
354 | --cnt; | |
355 | ++cp; | |
356 | } | |
098a8a83 | 357 | for (; cnt == 0 && i < 4; ++i, ++cp) |
affae2bf | 358 | data = (data << 8) | (*(uchar *)cp); |
affae2bf | 359 | |
098a8a83 WD |
360 | rc = write_word(info, wp, data); |
361 | ||
362 | if (rc != 0) | |
363 | return rc; | |
364 | ||
affae2bf WD |
365 | wp += 4; |
366 | } | |
367 | ||
368 | /* | |
369 | * handle word aligned part | |
370 | */ | |
371 | while (cnt >= 4) { | |
372 | data = 0; | |
098a8a83 | 373 | for (i = 0; i < 4; ++i) |
affae2bf | 374 | data = (data << 8) | *src++; |
098a8a83 WD |
375 | |
376 | rc = write_word(info, wp, data); | |
377 | ||
378 | if (rc != 0) | |
379 | return rc; | |
380 | ||
affae2bf WD |
381 | wp += 4; |
382 | cnt -= 4; | |
383 | } | |
384 | ||
098a8a83 WD |
385 | if (cnt == 0) |
386 | return 0; | |
affae2bf WD |
387 | |
388 | /* | |
389 | * handle unaligned tail bytes | |
390 | */ | |
391 | data = 0; | |
098a8a83 | 392 | for (i = 0, cp = wp; i < 4 && cnt > 0; ++i, ++cp) { |
affae2bf WD |
393 | data = (data << 8) | *src++; |
394 | --cnt; | |
395 | } | |
098a8a83 | 396 | for (; i < 4; ++i, ++cp) |
affae2bf | 397 | data = (data << 8) | (*(uchar *)cp); |
affae2bf | 398 | |
098a8a83 | 399 | return write_word(info, wp, data); |
affae2bf WD |
400 | } |
401 | ||
402 | /*----------------------------------------------------------------------- | |
403 | * Write a word to Flash, returns: | |
404 | * 0 - OK | |
405 | * 1 - write timeout | |
406 | * 2 - Flash not erased | |
407 | */ | |
098a8a83 | 408 | static int write_word(flash_info_t *info, ulong dest, ulong data) |
affae2bf | 409 | { |
098a8a83 WD |
410 | volatile unsigned char *cdest, *cdata; |
411 | volatile unsigned char *addr = | |
412 | (volatile unsigned char *)(info->start[0]); | |
affae2bf WD |
413 | ulong start; |
414 | int flag, count = 4 ; | |
415 | ||
416 | cdest = (volatile unsigned char *)dest ; | |
417 | cdata = (volatile unsigned char *)&data ; | |
418 | ||
419 | /* Check if Flash is (sufficiently) erased */ | |
098a8a83 WD |
420 | if ((*((vu_long *)dest) & data) != data) |
421 | return 2; | |
affae2bf | 422 | |
098a8a83 | 423 | while (count--) { |
affae2bf | 424 | |
098a8a83 WD |
425 | /* Disable interrupts which might cause a timeout here */ |
426 | flag = disable_interrupts(); | |
affae2bf | 427 | |
098a8a83 WD |
428 | addr[0x0555] = 0xAA; |
429 | addr[0x02AA] = 0x55; | |
430 | addr[0x0555] = 0xA0; | |
affae2bf | 431 | |
098a8a83 WD |
432 | *cdest = *cdata; |
433 | ||
434 | /* re-enable interrupts if necessary */ | |
435 | if (flag) | |
436 | enable_interrupts(); | |
affae2bf | 437 | |
098a8a83 WD |
438 | /* data polling for D7 */ |
439 | start = get_timer(0); | |
440 | while ((*cdest ^ *cdata) & 0x80) { | |
441 | if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) | |
442 | return 1; | |
affae2bf | 443 | } |
affae2bf | 444 | |
098a8a83 WD |
445 | cdata++ ; |
446 | cdest++ ; | |
affae2bf | 447 | } |
098a8a83 | 448 | return 0; |
affae2bf | 449 | } |