]>
Commit | Line | Data |
---|---|---|
7521af1c WD |
1 | /** |
2 | * @file flash.c | |
3 | */ | |
4 | ||
5 | /* | |
6 | * (C) Copyright 2003 | |
7 | * AMIRIX Systems Inc. | |
8 | * | |
9 | * Originated from ppcboot-2.0.0/board/esd/cpci440/strataflash.c | |
10 | * | |
11 | * (C) Copyright 2002 | |
12 | * Brad Kemp, Seranoa Networks, Brad.Kemp@seranoa.com | |
13 | * | |
14 | * See file CREDITS for list of people who contributed to this | |
15 | * project. | |
16 | * | |
17 | * This program is free software; you can redistribute it and/or | |
18 | * modify it under the terms of the GNU General Public License as | |
19 | * published by the Free Software Foundation; either version 2 of | |
20 | * the License, or (at your option) any later version. | |
21 | * | |
22 | * This program is distributed in the hope that it will be useful, | |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26 | * | |
27 | * You should have received a copy of the GNU General Public License | |
28 | * along with this program; if not, write to the Free Software | |
29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
30 | * MA 02111-1307 USA | |
31 | */ | |
32 | ||
33 | #include <common.h> | |
34 | #include <asm/processor.h> | |
35 | ||
36 | #undef DEBUG_FLASH | |
37 | /* | |
38 | * This file implements a Common Flash Interface (CFI) driver for ppcboot. | |
39 | * The width of the port and the width of the chips are determined at initialization. | |
40 | * These widths are used to calculate the address for access CFI data structures. | |
41 | * It has been tested on an Intel Strataflash implementation. | |
42 | * | |
43 | * References | |
44 | * JEDEC Standard JESD68 - Common Flash Interface (CFI) | |
45 | * JEDEC Standard JEP137-A Common Flash Interface (CFI) ID Codes | |
46 | * Intel Application Note 646 Common Flash Interface (CFI) and Command Sets | |
47 | * Intel 290667-008 3 Volt Intel StrataFlash Memory datasheet | |
48 | * | |
49 | * TODO | |
50 | * Use Primary Extended Query table (PRI) and Alternate Algorithm Query Table (ALT) to determine if protection is available | |
51 | * Add support for other command sets Use the PRI and ALT to determine command set | |
52 | * Verify erase and program timeouts. | |
53 | */ | |
54 | ||
55 | #define FLASH_CMD_CFI 0x98 | |
56 | #define FLASH_CMD_READ_ID 0x90 | |
57 | #define FLASH_CMD_RESET 0xff | |
58 | #define FLASH_CMD_BLOCK_ERASE 0x20 | |
59 | #define FLASH_CMD_ERASE_CONFIRM 0xD0 | |
60 | #define FLASH_CMD_WRITE 0x40 | |
61 | #define FLASH_CMD_PROTECT 0x60 | |
62 | #define FLASH_CMD_PROTECT_SET 0x01 | |
63 | #define FLASH_CMD_PROTECT_CLEAR 0xD0 | |
64 | #define FLASH_CMD_CLEAR_STATUS 0x50 | |
65 | #define FLASH_CMD_WRITE_TO_BUFFER 0xE8 | |
66 | #define FLASH_CMD_WRITE_BUFFER_CONFIRM 0xD0 | |
67 | ||
68 | #define FLASH_STATUS_DONE 0x80 | |
69 | #define FLASH_STATUS_ESS 0x40 | |
70 | #define FLASH_STATUS_ECLBS 0x20 | |
71 | #define FLASH_STATUS_PSLBS 0x10 | |
72 | #define FLASH_STATUS_VPENS 0x08 | |
73 | #define FLASH_STATUS_PSS 0x04 | |
74 | #define FLASH_STATUS_DPS 0x02 | |
75 | #define FLASH_STATUS_R 0x01 | |
76 | #define FLASH_STATUS_PROTECT 0x01 | |
77 | ||
78 | #define FLASH_OFFSET_CFI 0x55 | |
79 | #define FLASH_OFFSET_CFI_RESP 0x10 | |
80 | #define FLASH_OFFSET_WTOUT 0x1F | |
81 | #define FLASH_OFFSET_WBTOUT 0x20 | |
82 | #define FLASH_OFFSET_ETOUT 0x21 | |
83 | #define FLASH_OFFSET_CETOUT 0x22 | |
84 | #define FLASH_OFFSET_WMAX_TOUT 0x23 | |
85 | #define FLASH_OFFSET_WBMAX_TOUT 0x24 | |
86 | #define FLASH_OFFSET_EMAX_TOUT 0x25 | |
87 | #define FLASH_OFFSET_CEMAX_TOUT 0x26 | |
88 | #define FLASH_OFFSET_SIZE 0x27 | |
89 | #define FLASH_OFFSET_INTERFACE 0x28 | |
90 | #define FLASH_OFFSET_BUFFER_SIZE 0x2A | |
91 | #define FLASH_OFFSET_NUM_ERASE_REGIONS 0x2C | |
92 | #define FLASH_OFFSET_ERASE_REGIONS 0x2D | |
93 | #define FLASH_OFFSET_PROTECT 0x02 | |
94 | #define FLASH_OFFSET_USER_PROTECTION 0x85 | |
95 | #define FLASH_OFFSET_INTEL_PROTECTION 0x81 | |
96 | ||
7521af1c WD |
97 | #define FLASH_MAN_CFI 0x01000000 |
98 | ||
7521af1c | 99 | typedef union { |
3df5bea0 WD |
100 | unsigned char c; |
101 | unsigned short w; | |
102 | unsigned long l; | |
7521af1c WD |
103 | } cfiword_t; |
104 | ||
105 | typedef union { | |
3df5bea0 WD |
106 | unsigned char *cp; |
107 | unsigned short *wp; | |
108 | unsigned long *lp; | |
7521af1c WD |
109 | } cfiptr_t; |
110 | ||
111 | #define NUM_ERASE_REGIONS 4 | |
112 | ||
6d0f6bcf | 113 | flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */ |
7521af1c WD |
114 | |
115 | /*----------------------------------------------------------------------- | |
116 | * Functions | |
117 | */ | |
118 | ||
3df5bea0 WD |
119 | static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c); |
120 | static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf); | |
121 | static void flash_write_cmd (flash_info_t * info, int sect, uchar offset, | |
122 | uchar cmd); | |
123 | static int flash_isequal (flash_info_t * info, int sect, uchar offset, | |
124 | uchar cmd); | |
125 | static int flash_isset (flash_info_t * info, int sect, uchar offset, | |
126 | uchar cmd); | |
127 | static int flash_detect_cfi (flash_info_t * info); | |
7521af1c | 128 | static ulong flash_get_size (ulong base, int banknum); |
3df5bea0 WD |
129 | static int flash_write_cfiword (flash_info_t * info, ulong dest, |
130 | cfiword_t cword); | |
131 | static int flash_full_status_check (flash_info_t * info, ulong sector, | |
132 | ulong tout, char *prompt); | |
6d0f6bcf | 133 | #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE |
3df5bea0 WD |
134 | static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, |
135 | int len); | |
7521af1c WD |
136 | #endif |
137 | /*----------------------------------------------------------------------- | |
138 | * create an address based on the offset and the port width | |
139 | */ | |
3df5bea0 | 140 | uchar *flash_make_addr (flash_info_t * info, int sect, int offset) |
7521af1c | 141 | { |
3df5bea0 | 142 | return ((uchar *) (info->start[sect] + (offset * info->chipwidth))); |
7521af1c | 143 | } |
3df5bea0 | 144 | |
7521af1c WD |
145 | /*----------------------------------------------------------------------- |
146 | * read a character at a port width address | |
147 | */ | |
3df5bea0 | 148 | uchar flash_read_uchar (flash_info_t * info, uchar offset) |
7521af1c | 149 | { |
3df5bea0 WD |
150 | if (info->portwidth == FLASH_CFI_8BIT) { |
151 | volatile uchar *cp; | |
152 | uchar c; | |
153 | ||
154 | cp = flash_make_addr (info, 0, offset); | |
155 | c = *cp; | |
7521af1c | 156 | #ifdef DEBUG_FLASH |
3df5bea0 WD |
157 | printf ("flash_read_uchar offset=%04x ptr=%08x c=%02x\n", |
158 | offset, (unsigned int) cp, c); | |
7521af1c | 159 | #endif |
3df5bea0 WD |
160 | return (c); |
161 | ||
162 | } else if (info->portwidth == FLASH_CFI_16BIT) { | |
163 | volatile ushort *sp; | |
164 | ushort s; | |
165 | uchar c; | |
166 | ||
167 | sp = (ushort *) flash_make_addr (info, 0, offset); | |
168 | s = *sp; | |
169 | c = (uchar) s; | |
7521af1c | 170 | #ifdef DEBUG_FLASH |
3df5bea0 | 171 | printf ("flash_read_uchar offset=%04x ptr=%08x s=%04x c=%02x\n", offset, (unsigned int) sp, s, c); |
7521af1c | 172 | #endif |
3df5bea0 | 173 | return (c); |
7521af1c | 174 | |
3df5bea0 | 175 | } |
7521af1c | 176 | |
3df5bea0 | 177 | return 0; |
7521af1c WD |
178 | } |
179 | ||
180 | /*----------------------------------------------------------------------- | |
181 | * read a short word by swapping for ppc format. | |
182 | */ | |
3df5bea0 | 183 | ushort flash_read_ushort (flash_info_t * info, int sect, uchar offset) |
7521af1c | 184 | { |
3df5bea0 WD |
185 | if (info->portwidth == FLASH_CFI_8BIT) { |
186 | volatile uchar *cp; | |
187 | uchar c0, c1; | |
188 | ushort s; | |
189 | ||
190 | cp = flash_make_addr (info, 0, offset); | |
191 | c1 = cp[2]; | |
192 | c0 = cp[0]; | |
193 | s = c1 << 8 | c0; | |
7521af1c | 194 | #ifdef DEBUG_FLASH |
3df5bea0 | 195 | printf ("flash_read_ushort offset=%04x ptr=%08x c1=%02x c0=%02x s=%04x\n", offset, (unsigned int) cp, c1, c0, s); |
7521af1c | 196 | #endif |
3df5bea0 WD |
197 | return (s); |
198 | ||
199 | } else if (info->portwidth == FLASH_CFI_16BIT) { | |
200 | volatile ushort *sp; | |
201 | ushort s; | |
202 | uchar c0, c1; | |
203 | ||
204 | sp = (ushort *) flash_make_addr (info, 0, offset); | |
205 | s = *sp; | |
206 | c1 = (uchar) sp[1]; | |
207 | c0 = (uchar) sp[0]; | |
208 | s = c1 << 8 | c0; | |
7521af1c | 209 | #ifdef DEBUG_FLASH |
3df5bea0 | 210 | printf ("flash_read_ushort offset=%04x ptr=%08x c1=%02x c0=%02x s=%04x\n", offset, (unsigned int) sp, c1, c0, s); |
7521af1c | 211 | #endif |
3df5bea0 | 212 | return (s); |
7521af1c | 213 | |
3df5bea0 | 214 | } |
7521af1c | 215 | |
3df5bea0 | 216 | return 0; |
7521af1c WD |
217 | } |
218 | ||
219 | /*----------------------------------------------------------------------- | |
220 | * read a long word by picking the least significant byte of each maiximum | |
221 | * port size word. Swap for ppc format. | |
222 | */ | |
3df5bea0 | 223 | ulong flash_read_long (flash_info_t * info, int sect, uchar offset) |
7521af1c | 224 | { |
3df5bea0 WD |
225 | if (info->portwidth == FLASH_CFI_8BIT) { |
226 | volatile uchar *cp; | |
227 | uchar c0, c1, c2, c3; | |
228 | ulong l; | |
229 | ||
230 | cp = flash_make_addr (info, 0, offset); | |
231 | c3 = cp[6]; | |
232 | c2 = cp[4]; | |
233 | c1 = cp[2]; | |
234 | c0 = cp[0]; | |
235 | l = c3 << 24 | c2 << 16 | c1 << 8 | c0; | |
7521af1c | 236 | #ifdef DEBUG_FLASH |
3df5bea0 | 237 | printf ("flash_read_long offset=%04x ptr=%08x c3=%02x c2=%02x c1=%02x c0=%02x l=%08x\n", offset, (unsigned int) cp, c3, c2, c1, c0, l); |
7521af1c | 238 | #endif |
3df5bea0 WD |
239 | return (l); |
240 | ||
241 | } else if (info->portwidth == FLASH_CFI_16BIT) { | |
242 | volatile ushort *sp; | |
243 | uchar c0, c1, c2, c3; | |
244 | ulong l; | |
245 | ||
246 | sp = (ushort *) flash_make_addr (info, 0, offset); | |
247 | c3 = (uchar) sp[3]; | |
248 | c2 = (uchar) sp[2]; | |
249 | c1 = (uchar) sp[1]; | |
250 | c0 = (uchar) sp[0]; | |
251 | l = c3 << 24 | c2 << 16 | c1 << 8 | c0; | |
7521af1c | 252 | #ifdef DEBUG_FLASH |
3df5bea0 | 253 | printf ("flash_read_long offset=%04x ptr=%08x c3=%02x c2=%02x c1=%02x c0=%02x l=%08x\n", offset, (unsigned int) sp, c3, c2, c1, c0, l); |
7521af1c | 254 | #endif |
3df5bea0 | 255 | return (l); |
7521af1c | 256 | |
3df5bea0 | 257 | } |
7521af1c | 258 | |
3df5bea0 | 259 | return 0; |
7521af1c WD |
260 | } |
261 | ||
262 | /*----------------------------------------------------------------------- | |
263 | */ | |
264 | unsigned long flash_init (void) | |
265 | { | |
3df5bea0 WD |
266 | unsigned long size; |
267 | ||
268 | size = 0; | |
269 | ||
270 | flash_info[0].flash_id = FLASH_UNKNOWN; | |
271 | flash_info[0].portwidth = FLASH_CFI_16BIT; | |
272 | flash_info[0].chipwidth = FLASH_CFI_16BIT; | |
6d0f6bcf | 273 | size += flash_info[0].size = flash_get_size (CONFIG_SYS_PROGFLASH_BASE, 0); |
3df5bea0 WD |
274 | if (flash_info[0].flash_id == FLASH_UNKNOWN) { |
275 | printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", 1, flash_info[0].size, flash_info[0].size << 20); | |
276 | }; | |
277 | ||
278 | flash_info[1].flash_id = FLASH_UNKNOWN; | |
279 | flash_info[1].portwidth = FLASH_CFI_8BIT; | |
280 | flash_info[1].chipwidth = FLASH_CFI_16BIT; | |
6d0f6bcf | 281 | size += flash_info[1].size = flash_get_size (CONFIG_SYS_CONFFLASH_BASE, 1); |
3df5bea0 WD |
282 | if (flash_info[1].flash_id == FLASH_UNKNOWN) { |
283 | printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", 2, flash_info[1].size, flash_info[1].size << 20); | |
284 | }; | |
285 | ||
286 | return (size); | |
7521af1c WD |
287 | } |
288 | ||
289 | /*----------------------------------------------------------------------- | |
290 | */ | |
3df5bea0 | 291 | int flash_erase (flash_info_t * info, int s_first, int s_last) |
7521af1c | 292 | { |
3df5bea0 WD |
293 | int rcode = 0; |
294 | int prot; | |
295 | int sect; | |
296 | ||
297 | if (info->flash_id != FLASH_MAN_CFI) { | |
298 | printf ("Can't erase unknown flash type - aborted\n"); | |
299 | return 1; | |
300 | } | |
301 | if ((s_first < 0) || (s_first > s_last)) { | |
302 | printf ("- no sectors to erase\n"); | |
303 | return 1; | |
304 | } | |
305 | ||
306 | prot = 0; | |
307 | for (sect = s_first; sect <= s_last; ++sect) { | |
308 | if (info->protect[sect]) { | |
309 | prot++; | |
310 | } | |
311 | } | |
312 | if (prot) { | |
313 | printf ("- Warning: %d protected sectors will not be erased!\n", prot); | |
314 | } else { | |
315 | printf ("\n"); | |
316 | } | |
317 | ||
318 | for (sect = s_first; sect <= s_last; sect++) { | |
319 | if (info->protect[sect] == 0) { /* not protected */ | |
320 | flash_write_cmd (info, sect, 0, | |
321 | FLASH_CMD_CLEAR_STATUS); | |
322 | flash_write_cmd (info, sect, 0, | |
323 | FLASH_CMD_BLOCK_ERASE); | |
324 | flash_write_cmd (info, sect, 0, | |
325 | FLASH_CMD_ERASE_CONFIRM); | |
326 | ||
327 | if (flash_full_status_check | |
328 | (info, sect, info->erase_blk_tout, "erase")) { | |
329 | rcode = 1; | |
330 | } else | |
331 | printf ("."); | |
332 | } | |
333 | } | |
334 | printf (" done\n"); | |
335 | return rcode; | |
7521af1c WD |
336 | } |
337 | ||
338 | /*----------------------------------------------------------------------- | |
339 | */ | |
3df5bea0 | 340 | void flash_print_info (flash_info_t * info) |
7521af1c | 341 | { |
3df5bea0 WD |
342 | int i; |
343 | ||
344 | if (info->flash_id != FLASH_MAN_CFI) { | |
345 | printf ("missing or unknown FLASH type\n"); | |
346 | return; | |
347 | } | |
348 | ||
349 | printf ("CFI conformant FLASH (x%d device in x%d mode)", | |
350 | (info->chipwidth << 3), (info->portwidth << 3)); | |
351 | printf (" Size: %ld MB in %d Sectors\n", | |
352 | info->size >> 20, info->sector_count); | |
353 | printf (" Erase timeout %ld ms, write timeout %ld ms, buffer write timeout %ld ms, buffer size %d\n", info->erase_blk_tout, info->write_tout, info->buffer_write_tout, info->buffer_size); | |
354 | ||
355 | printf (" Sector Start Addresses:"); | |
356 | for (i = 0; i < info->sector_count; ++i) { | |
357 | if ((i % 5) == 0) | |
358 | printf ("\n"); | |
359 | printf (" %08lX%5s", | |
360 | info->start[i], info->protect[i] ? " (RO)" : " "); | |
361 | } | |
362 | printf ("\n"); | |
363 | return; | |
7521af1c WD |
364 | } |
365 | ||
366 | /*----------------------------------------------------------------------- | |
367 | * Copy memory to flash, returns: | |
368 | * 0 - OK | |
369 | * 1 - write timeout | |
370 | * 2 - Flash not erased | |
371 | */ | |
3df5bea0 | 372 | int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) |
7521af1c | 373 | { |
3df5bea0 WD |
374 | ulong wp; |
375 | ulong cp; | |
376 | int aln; | |
377 | cfiword_t cword; | |
378 | int i, rc; | |
379 | ||
380 | /* get lower aligned address */ | |
381 | wp = (addr & ~(info->portwidth - 1)); | |
382 | ||
383 | /* handle unaligned start */ | |
384 | if ((aln = addr - wp) != 0) { | |
385 | cword.l = 0; | |
386 | cp = wp; | |
387 | for (i = 0; i < aln; ++i, ++cp) | |
388 | flash_add_byte (info, &cword, (*(uchar *) cp)); | |
389 | ||
390 | for (; (i < info->portwidth) && (cnt > 0); i++) { | |
391 | flash_add_byte (info, &cword, *src++); | |
392 | cnt--; | |
393 | cp++; | |
394 | } | |
395 | for (; (cnt == 0) && (i < info->portwidth); ++i, ++cp) | |
396 | flash_add_byte (info, &cword, (*(uchar *) cp)); | |
397 | if ((rc = flash_write_cfiword (info, wp, cword)) != 0) | |
398 | return rc; | |
399 | wp = cp; | |
400 | } | |
6d0f6bcf | 401 | #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE |
3df5bea0 WD |
402 | while (cnt >= info->portwidth) { |
403 | i = info->buffer_size > cnt ? cnt : info->buffer_size; | |
404 | if ((rc = flash_write_cfibuffer (info, wp, src, i)) != ERR_OK) | |
405 | return rc; | |
406 | wp += i; | |
407 | src += i; | |
408 | cnt -= i; | |
409 | } | |
7521af1c | 410 | #else |
3df5bea0 WD |
411 | /* handle the aligned part */ |
412 | while (cnt >= info->portwidth) { | |
413 | cword.l = 0; | |
414 | for (i = 0; i < info->portwidth; i++) { | |
415 | flash_add_byte (info, &cword, *src++); | |
416 | } | |
417 | if ((rc = flash_write_cfiword (info, wp, cword)) != 0) | |
418 | return rc; | |
419 | wp += info->portwidth; | |
420 | cnt -= info->portwidth; | |
421 | } | |
6d0f6bcf | 422 | #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */ |
3df5bea0 WD |
423 | if (cnt == 0) { |
424 | return (0); | |
425 | } | |
426 | ||
427 | /* | |
428 | * handle unaligned tail bytes | |
429 | */ | |
430 | cword.l = 0; | |
431 | for (i = 0, cp = wp; (i < info->portwidth) && (cnt > 0); ++i, ++cp) { | |
432 | flash_add_byte (info, &cword, *src++); | |
433 | --cnt; | |
434 | } | |
435 | for (; i < info->portwidth; ++i, ++cp) { | |
436 | flash_add_byte (info, &cword, (*(uchar *) cp)); | |
437 | } | |
438 | ||
439 | return flash_write_cfiword (info, wp, cword); | |
7521af1c WD |
440 | } |
441 | ||
442 | /*----------------------------------------------------------------------- | |
443 | */ | |
3df5bea0 | 444 | int flash_real_protect (flash_info_t * info, long sector, int prot) |
7521af1c | 445 | { |
3df5bea0 WD |
446 | int retcode = 0; |
447 | ||
448 | flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); | |
449 | flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT); | |
450 | if (prot) | |
451 | flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_SET); | |
452 | else | |
453 | flash_write_cmd (info, sector, 0, FLASH_CMD_PROTECT_CLEAR); | |
454 | ||
455 | if ((retcode = | |
456 | flash_full_status_check (info, sector, info->erase_blk_tout, | |
457 | prot ? "protect" : "unprotect")) == 0) { | |
458 | ||
459 | info->protect[sector] = prot; | |
460 | /* Intel's unprotect unprotects all locking */ | |
461 | if (prot == 0) { | |
462 | int i; | |
463 | ||
464 | for (i = 0; i < info->sector_count; i++) { | |
465 | if (info->protect[i]) | |
466 | flash_real_protect (info, i, 1); | |
467 | } | |
468 | } | |
469 | } | |
470 | ||
471 | return retcode; | |
7521af1c | 472 | } |
3df5bea0 | 473 | |
7521af1c WD |
474 | /*----------------------------------------------------------------------- |
475 | * wait for XSR.7 to be set. Time out with an error if it does not. | |
476 | * This routine does not set the flash to read-array mode. | |
477 | */ | |
3df5bea0 WD |
478 | static int flash_status_check (flash_info_t * info, ulong sector, ulong tout, |
479 | char *prompt) | |
7521af1c | 480 | { |
3df5bea0 WD |
481 | ulong start; |
482 | ||
483 | /* Wait for command completion */ | |
484 | start = get_timer (0); | |
485 | while (!flash_isset (info, sector, 0, FLASH_STATUS_DONE)) { | |
486 | if (get_timer (start) > info->erase_blk_tout) { | |
487 | printf ("Flash %s timeout at address %lx\n", prompt, | |
488 | info->start[sector]); | |
489 | flash_write_cmd (info, sector, 0, FLASH_CMD_RESET); | |
490 | return ERR_TIMOUT; | |
491 | } | |
492 | } | |
493 | return ERR_OK; | |
7521af1c | 494 | } |
3df5bea0 | 495 | |
7521af1c WD |
496 | /*----------------------------------------------------------------------- |
497 | * Wait for XSR.7 to be set, if it times out print an error, otherwise do a full status check. | |
498 | * This routine sets the flash to read-array mode. | |
499 | */ | |
3df5bea0 WD |
500 | static int flash_full_status_check (flash_info_t * info, ulong sector, |
501 | ulong tout, char *prompt) | |
7521af1c | 502 | { |
3df5bea0 WD |
503 | int retcode; |
504 | ||
505 | retcode = flash_status_check (info, sector, tout, prompt); | |
506 | if ((retcode == ERR_OK) | |
507 | && !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) { | |
508 | retcode = ERR_INVAL; | |
509 | printf ("Flash %s error at address %lx\n", prompt, | |
510 | info->start[sector]); | |
511 | if (flash_isset | |
512 | (info, sector, 0, | |
513 | FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) { | |
514 | printf ("Command Sequence Error.\n"); | |
515 | } else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) { | |
516 | printf ("Block Erase Error.\n"); | |
517 | retcode = ERR_NOT_ERASED; | |
518 | } else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) { | |
519 | printf ("Locking Error\n"); | |
520 | } | |
521 | if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) { | |
522 | printf ("Block locked.\n"); | |
523 | retcode = ERR_PROTECTED; | |
524 | } | |
525 | if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS)) | |
526 | printf ("Vpp Low Error.\n"); | |
527 | } | |
528 | flash_write_cmd (info, sector, 0, FLASH_CMD_RESET); | |
529 | return retcode; | |
7521af1c | 530 | } |
3df5bea0 | 531 | |
7521af1c WD |
532 | /*----------------------------------------------------------------------- |
533 | */ | |
3df5bea0 | 534 | static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c) |
7521af1c | 535 | { |
3df5bea0 WD |
536 | switch (info->portwidth) { |
537 | case FLASH_CFI_8BIT: | |
538 | cword->c = c; | |
539 | break; | |
540 | case FLASH_CFI_16BIT: | |
541 | cword->w = (cword->w << 8) | c; | |
542 | break; | |
543 | case FLASH_CFI_32BIT: | |
544 | cword->l = (cword->l << 8) | c; | |
545 | } | |
7521af1c WD |
546 | } |
547 | ||
7521af1c WD |
548 | /*----------------------------------------------------------------------- |
549 | * make a proper sized command based on the port and chip widths | |
550 | */ | |
3df5bea0 | 551 | static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf) |
7521af1c | 552 | { |
3df5bea0 WD |
553 | /*int i; */ |
554 | uchar *cp = (uchar *) cmdbuf; | |
555 | ||
556 | /* for(i=0; i< info->portwidth; i++) */ | |
557 | /* *cp++ = ((i+1) % info->chipwidth) ? '\0':cmd; */ | |
558 | if (info->portwidth == FLASH_CFI_8BIT | |
559 | && info->chipwidth == FLASH_CFI_16BIT) { | |
560 | cp[0] = cmd; | |
561 | } else if (info->portwidth == FLASH_CFI_16BIT | |
562 | && info->chipwidth == FLASH_CFI_16BIT) { | |
563 | cp[0] = '\0'; | |
564 | cp[1] = cmd; | |
565 | }; | |
7521af1c WD |
566 | } |
567 | ||
568 | /* | |
569 | * Write a proper sized command to the correct address | |
570 | */ | |
3df5bea0 WD |
571 | static void flash_write_cmd (flash_info_t * info, int sect, uchar offset, |
572 | uchar cmd) | |
7521af1c WD |
573 | { |
574 | ||
3df5bea0 WD |
575 | volatile cfiptr_t addr; |
576 | cfiword_t cword; | |
577 | ||
578 | addr.cp = flash_make_addr (info, sect, offset); | |
579 | flash_make_cmd (info, cmd, &cword); | |
580 | switch (info->portwidth) { | |
581 | case FLASH_CFI_8BIT: | |
582 | *addr.cp = cword.c; | |
583 | break; | |
584 | case FLASH_CFI_16BIT: | |
585 | *addr.wp = cword.w; | |
586 | break; | |
587 | case FLASH_CFI_32BIT: | |
588 | *addr.lp = cword.l; | |
589 | break; | |
590 | } | |
7521af1c WD |
591 | } |
592 | ||
593 | /*----------------------------------------------------------------------- | |
594 | */ | |
3df5bea0 WD |
595 | static int flash_isequal (flash_info_t * info, int sect, uchar offset, |
596 | uchar cmd) | |
7521af1c | 597 | { |
3df5bea0 WD |
598 | cfiptr_t cptr; |
599 | cfiword_t cword; | |
600 | int retval; | |
601 | ||
602 | cptr.cp = flash_make_addr (info, sect, offset); | |
603 | flash_make_cmd (info, cmd, &cword); | |
604 | switch (info->portwidth) { | |
605 | case FLASH_CFI_8BIT: | |
606 | retval = (cptr.cp[0] == cword.c); | |
607 | break; | |
608 | case FLASH_CFI_16BIT: | |
609 | retval = (cptr.wp[0] == cword.w); | |
610 | break; | |
611 | case FLASH_CFI_32BIT: | |
612 | retval = (cptr.lp[0] == cword.l); | |
613 | break; | |
614 | default: | |
615 | retval = 0; | |
616 | break; | |
617 | } | |
618 | return retval; | |
7521af1c | 619 | } |
3df5bea0 | 620 | |
7521af1c WD |
621 | /*----------------------------------------------------------------------- |
622 | */ | |
3df5bea0 WD |
623 | static int flash_isset (flash_info_t * info, int sect, uchar offset, |
624 | uchar cmd) | |
7521af1c | 625 | { |
3df5bea0 WD |
626 | cfiptr_t cptr; |
627 | cfiword_t cword; | |
628 | int retval; | |
629 | ||
630 | cptr.cp = flash_make_addr (info, sect, offset); | |
631 | flash_make_cmd (info, cmd, &cword); | |
632 | switch (info->portwidth) { | |
633 | case FLASH_CFI_8BIT: | |
634 | retval = ((cptr.cp[0] & cword.c) == cword.c); | |
635 | break; | |
636 | case FLASH_CFI_16BIT: | |
637 | retval = ((cptr.wp[0] & cword.w) == cword.w); | |
638 | break; | |
639 | case FLASH_CFI_32BIT: | |
640 | retval = ((cptr.lp[0] & cword.l) == cword.l); | |
641 | break; | |
642 | default: | |
643 | retval = 0; | |
644 | break; | |
645 | } | |
646 | return retval; | |
7521af1c WD |
647 | } |
648 | ||
649 | /*----------------------------------------------------------------------- | |
650 | * detect if flash is compatible with the Common Flash Interface (CFI) | |
651 | * http://www.jedec.org/download/search/jesd68.pdf | |
652 | * | |
653 | */ | |
3df5bea0 | 654 | static int flash_detect_cfi (flash_info_t * info) |
7521af1c WD |
655 | { |
656 | ||
657 | #if 0 | |
3df5bea0 WD |
658 | for (info->portwidth = FLASH_CFI_8BIT; |
659 | info->portwidth <= FLASH_CFI_32BIT; info->portwidth <<= 1) { | |
660 | for (info->chipwidth = FLASH_CFI_BY8; | |
661 | info->chipwidth <= info->portwidth; | |
662 | info->chipwidth <<= 1) { | |
663 | flash_write_cmd (info, 0, 0, FLASH_CMD_RESET); | |
664 | flash_write_cmd (info, 0, FLASH_OFFSET_CFI, | |
665 | FLASH_CMD_CFI); | |
666 | if (flash_isequal | |
667 | (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') | |
668 | && flash_isequal (info, 0, | |
669 | FLASH_OFFSET_CFI_RESP + 1, 'R') | |
670 | && flash_isequal (info, 0, | |
671 | FLASH_OFFSET_CFI_RESP + 2, 'Y')) | |
672 | return 1; | |
673 | } | |
674 | } | |
7521af1c | 675 | #endif |
3df5bea0 WD |
676 | flash_write_cmd (info, 0, 0, FLASH_CMD_RESET); |
677 | flash_write_cmd (info, 0, FLASH_OFFSET_CFI, FLASH_CMD_CFI); | |
678 | if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q') && | |
679 | flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R') && | |
680 | flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) { | |
681 | return 1; | |
682 | } else { | |
683 | return 0; | |
684 | }; | |
7521af1c | 685 | } |
3df5bea0 | 686 | |
7521af1c WD |
687 | /* |
688 | * The following code cannot be run from FLASH! | |
689 | * | |
690 | */ | |
691 | static ulong flash_get_size (ulong base, int banknum) | |
692 | { | |
3df5bea0 WD |
693 | flash_info_t *info = &flash_info[banknum]; |
694 | int i, j; | |
695 | int sect_cnt; | |
696 | unsigned long sector; | |
697 | unsigned long tmp; | |
698 | int size_ratio; | |
699 | uchar num_erase_regions; | |
700 | int erase_region_size; | |
701 | int erase_region_count; | |
702 | ||
703 | info->start[0] = base; | |
704 | ||
705 | if (flash_detect_cfi (info)) { | |
7521af1c | 706 | #ifdef DEBUG_FLASH |
3df5bea0 | 707 | printf ("portwidth=%d chipwidth=%d\n", info->portwidth, info->chipwidth); /* test-only */ |
7521af1c | 708 | #endif |
3df5bea0 WD |
709 | size_ratio = 1; /* info->portwidth / info->chipwidth; */ |
710 | num_erase_regions = | |
711 | flash_read_uchar (info, | |
712 | FLASH_OFFSET_NUM_ERASE_REGIONS); | |
7521af1c | 713 | #ifdef DEBUG_FLASH |
3df5bea0 | 714 | printf ("found %d erase regions\n", num_erase_regions); |
7521af1c | 715 | #endif |
3df5bea0 WD |
716 | sect_cnt = 0; |
717 | sector = base; | |
718 | for (i = 0; i < num_erase_regions; i++) { | |
719 | if (i > NUM_ERASE_REGIONS) { | |
720 | printf ("%d erase regions found, only %d used\n", num_erase_regions, NUM_ERASE_REGIONS); | |
721 | break; | |
722 | } | |
723 | tmp = flash_read_long (info, 0, | |
724 | FLASH_OFFSET_ERASE_REGIONS); | |
725 | erase_region_count = (tmp & 0xffff) + 1; | |
726 | tmp >>= 16; | |
727 | erase_region_size = | |
728 | (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; | |
729 | for (j = 0; j < erase_region_count; j++) { | |
730 | info->start[sect_cnt] = sector; | |
731 | sector += (erase_region_size * size_ratio); | |
732 | info->protect[sect_cnt] = | |
733 | flash_isset (info, sect_cnt, | |
734 | FLASH_OFFSET_PROTECT, | |
735 | FLASH_STATUS_PROTECT); | |
736 | sect_cnt++; | |
737 | } | |
738 | } | |
739 | ||
740 | info->sector_count = sect_cnt; | |
741 | /* multiply the size by the number of chips */ | |
742 | info->size = | |
743 | (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * | |
744 | size_ratio; | |
745 | info->buffer_size = | |
746 | (1 << | |
747 | flash_read_ushort (info, 0, | |
748 | FLASH_OFFSET_BUFFER_SIZE)); | |
749 | tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT); | |
750 | info->erase_blk_tout = | |
751 | (tmp * | |
752 | (1 << | |
753 | flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT))); | |
754 | tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT); | |
755 | info->buffer_write_tout = | |
756 | (tmp * | |
757 | (1 << | |
758 | flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT))); | |
759 | tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT); | |
760 | info->write_tout = | |
761 | (tmp * | |
762 | (1 << | |
763 | flash_read_uchar (info, | |
764 | FLASH_OFFSET_WMAX_TOUT))) / 1000; | |
765 | info->flash_id = FLASH_MAN_CFI; | |
766 | } | |
767 | ||
768 | flash_write_cmd (info, 0, 0, FLASH_CMD_RESET); | |
769 | return (info->size); | |
7521af1c WD |
770 | } |
771 | ||
7521af1c WD |
772 | /*----------------------------------------------------------------------- |
773 | */ | |
3df5bea0 WD |
774 | static int flash_write_cfiword (flash_info_t * info, ulong dest, |
775 | cfiword_t cword) | |
7521af1c WD |
776 | { |
777 | ||
3df5bea0 WD |
778 | cfiptr_t ctladdr; |
779 | cfiptr_t cptr; | |
780 | int flag; | |
781 | ||
782 | ctladdr.cp = flash_make_addr (info, 0, 0); | |
783 | cptr.cp = (uchar *) dest; | |
784 | ||
785 | /* Check if Flash is (sufficiently) erased */ | |
786 | switch (info->portwidth) { | |
787 | case FLASH_CFI_8BIT: | |
788 | flag = ((cptr.cp[0] & cword.c) == cword.c); | |
789 | break; | |
790 | case FLASH_CFI_16BIT: | |
791 | flag = ((cptr.wp[0] & cword.w) == cword.w); | |
792 | break; | |
793 | case FLASH_CFI_32BIT: | |
794 | flag = ((cptr.lp[0] & cword.l) == cword.l); | |
795 | break; | |
796 | default: | |
797 | return 2; | |
798 | } | |
799 | if (!flag) | |
800 | return 2; | |
801 | ||
802 | /* Disable interrupts which might cause a timeout here */ | |
803 | flag = disable_interrupts (); | |
804 | ||
805 | flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS); | |
806 | flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE); | |
807 | ||
808 | switch (info->portwidth) { | |
809 | case FLASH_CFI_8BIT: | |
810 | cptr.cp[0] = cword.c; | |
811 | break; | |
812 | case FLASH_CFI_16BIT: | |
813 | cptr.wp[0] = cword.w; | |
814 | break; | |
815 | case FLASH_CFI_32BIT: | |
816 | cptr.lp[0] = cword.l; | |
817 | break; | |
818 | } | |
819 | ||
820 | /* re-enable interrupts if necessary */ | |
821 | if (flag) | |
822 | enable_interrupts (); | |
823 | ||
824 | return flash_full_status_check (info, 0, info->write_tout, "write"); | |
7521af1c WD |
825 | } |
826 | ||
6d0f6bcf | 827 | #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE |
7521af1c WD |
828 | |
829 | /* loop through the sectors from the highest address | |
830 | * when the passed address is greater or equal to the sector address | |
831 | * we have a match | |
832 | */ | |
3df5bea0 | 833 | static int find_sector (flash_info_t * info, ulong addr) |
7521af1c | 834 | { |
3df5bea0 WD |
835 | int sector; |
836 | ||
837 | for (sector = info->sector_count - 1; sector >= 0; sector--) { | |
838 | if (addr >= info->start[sector]) | |
839 | break; | |
840 | } | |
841 | return sector; | |
7521af1c WD |
842 | } |
843 | ||
3df5bea0 WD |
844 | static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp, |
845 | int len) | |
7521af1c WD |
846 | { |
847 | ||
3df5bea0 WD |
848 | int sector; |
849 | int cnt; | |
850 | int retcode; | |
851 | volatile cfiptr_t src; | |
852 | volatile cfiptr_t dst; | |
853 | ||
854 | src.cp = cp; | |
855 | dst.cp = (uchar *) dest; | |
856 | sector = find_sector (info, dest); | |
857 | flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); | |
858 | flash_write_cmd (info, sector, 0, FLASH_CMD_WRITE_TO_BUFFER); | |
859 | if ((retcode = | |
860 | flash_status_check (info, sector, info->buffer_write_tout, | |
861 | "write to buffer")) == ERR_OK) { | |
862 | switch (info->portwidth) { | |
863 | case FLASH_CFI_8BIT: | |
864 | cnt = len; | |
865 | break; | |
866 | case FLASH_CFI_16BIT: | |
867 | cnt = len >> 1; | |
868 | break; | |
869 | case FLASH_CFI_32BIT: | |
870 | cnt = len >> 2; | |
871 | break; | |
872 | default: | |
873 | return ERR_INVAL; | |
874 | break; | |
875 | } | |
876 | flash_write_cmd (info, sector, 0, (uchar) cnt - 1); | |
877 | while (cnt-- > 0) { | |
878 | switch (info->portwidth) { | |
879 | case FLASH_CFI_8BIT: | |
880 | *dst.cp++ = *src.cp++; | |
881 | break; | |
882 | case FLASH_CFI_16BIT: | |
883 | *dst.wp++ = *src.wp++; | |
884 | break; | |
885 | case FLASH_CFI_32BIT: | |
886 | *dst.lp++ = *src.lp++; | |
887 | break; | |
888 | default: | |
889 | return ERR_INVAL; | |
890 | break; | |
891 | } | |
892 | } | |
893 | flash_write_cmd (info, sector, 0, | |
894 | FLASH_CMD_WRITE_BUFFER_CONFIRM); | |
895 | retcode = | |
896 | flash_full_status_check (info, sector, | |
897 | info->buffer_write_tout, | |
898 | "buffer write"); | |
899 | } | |
900 | flash_write_cmd (info, sector, 0, FLASH_CMD_CLEAR_STATUS); | |
901 | return retcode; | |
7521af1c | 902 | } |
6d0f6bcf | 903 | #endif /* CONFIG_SYS_USE_FLASH_BUFFER_WRITE */ |