blob: f9e861942d922f6bd9c2ee72a7691c30be82e5dd [file] [log] [blame]
wdenk9f837932003-10-09 19:00:25 +00001/*
2 * board/eva/flash.c
3 *
4 * (C) Copyright 2002
5 * Sangmoon Kim, Etin Systems, dogoil@etinsys.com.
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
26#include <common.h>
27#include <asm/processor.h>
28#include <asm/pci_io.h>
29#include <mpc824x.h>
Becky Brucedad8c912009-02-03 18:10:51 -060030#include <asm/mmu.h>
wdenk9f837932003-10-09 19:00:25 +000031
32int (*do_flash_erase)(flash_info_t*, uint32_t, uint32_t);
33int (*write_dword)(flash_info_t*, ulong, uint64_t);
34
35typedef uint64_t cfi_word;
36
37#define cfi_read(flash, addr) *((volatile cfi_word*)(flash->start[0] + addr))
38
39#define cfi_write(flash, val, addr) \
40 move64((cfi_word*)&val, \
41 (cfi_word*)(flash->start[0] + addr))
42
43#define CMD(x) ((((cfi_word)x)<<48)|(((cfi_word)x)<<32)|(((cfi_word)x)<<16)|(((cfi_word)x)))
44
45static void write32(unsigned long addr, uint32_t value)
46{
47 *(volatile uint32_t*)(addr) = value;
48 asm volatile("sync");
49}
50
51static uint32_t read32(unsigned long addr)
52{
53 uint32_t value;
54 value = *(volatile uint32_t*)addr;
55 asm volatile("sync");
56 return value;
57}
58
59static cfi_word cfi_cmd(flash_info_t *flash, uint8_t cmd, uint32_t addr)
60{
61 uint32_t base = flash->start[0];
62 uint32_t val=(cmd << 16) | cmd;
63 addr <<= 3;
64 write32(base + addr, val);
65 return addr;
66}
67
68static uint16_t cfi_read_query(flash_info_t *flash, uint32_t addr)
69{
70 uint32_t base = flash->start[0];
71 addr <<= 3;
72 return (uint16_t)read32(base + addr);
73}
74
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020075flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */
wdenk9f837932003-10-09 19:00:25 +000076
77static void move64(uint64_t *src, uint64_t *dest)
78{
79 asm volatile("lfd 0, 0(3)\n\t" /* fpr0 = *scr */
80 "stfd 0, 0(4)" /* *dest = fpr0 */
81 : : : "fr0" ); /* Clobbers fr0 */
82 return;
83}
84
85static int cfi_write_dword(flash_info_t *flash, ulong dest, cfi_word data)
86{
87 unsigned long start;
88 cfi_word status = 0;
89
90 status = cfi_read(flash, dest);
91 data &= status;
92
93 cfi_cmd(flash, 0x40, 0);
94 cfi_write(flash, data, dest);
95
96 udelay(10);
97 start = get_timer (0);
98 for(;;) {
99 status = cfi_read(flash, dest);
100 status &= CMD(0x80);
101 if(status == CMD(0x80))
102 break;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200103 if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
wdenk9f837932003-10-09 19:00:25 +0000104 cfi_cmd(flash, 0xff, 0);
105 return 1;
106 }
107 udelay(1);
108 }
109 cfi_cmd(flash, 0xff, 0);
110
111 return 0;
112}
113
114static int jedec_write_dword (flash_info_t *flash, ulong dest, cfi_word data)
115{
116 ulong start;
117 cfi_word status = 0;
118
119 status = cfi_read(flash, dest);
120 if(status != CMD(0xffff)) return 2;
121
122 cfi_cmd(flash, 0xaa, 0x555);
123 cfi_cmd(flash, 0x55, 0x2aa);
124 cfi_cmd(flash, 0xa0, 0x555);
125
126 cfi_write(flash, data, dest);
127
128 udelay(10);
129 start = get_timer (0);
130 status = ~data;
131 while(status != data) {
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200132 if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT)
wdenk9f837932003-10-09 19:00:25 +0000133 return 1;
134 status = cfi_read(flash, dest);
135 udelay(1);
136 }
137 return 0;
138}
139
140static __inline__ unsigned long get_msr(void)
141{
142 unsigned long msr;
143 __asm__ __volatile__ ("mfmsr %0" : "=r" (msr) :);
144 return msr;
145}
146
147static __inline__ void set_msr(unsigned long msr)
148{
149 __asm__ __volatile__ ("mtmsr %0" : : "r" (msr));
150}
151
152int write_buff (flash_info_t *flash, uchar *src, ulong addr, ulong cnt)
153{
154 ulong wp;
155 int i, s, l, rc;
156 cfi_word data;
157 uint8_t *t = (uint8_t*)&data;
158 unsigned long base = flash->start[0];
159 uint32_t msr;
160
161 if (flash->flash_id == FLASH_UNKNOWN)
162 return 4;
163
164 if (cnt == 0)
165 return 0;
166
167 addr -= base;
168
169 msr = get_msr();
170 set_msr(msr|MSR_FP);
171
172 wp = (addr & ~7); /* get lower word aligned address */
173
174 if((addr-wp) != 0) {
175 data = cfi_read(flash, wp);
176 s = addr & 7;
177 l = ( cnt < (8-s) ) ? cnt : (8-s);
178 for(i = 0; i < l; i++)
179 t[s+i] = *src++;
180 if ((rc = write_dword(flash, wp, data)) != 0)
181 goto DONE;
182 wp += 8;
183 cnt -= l;
184 }
185
186 while (cnt >= 8) {
187 for (i = 0; i < 8; i++)
188 t[i] = *src++;
189 if ((rc = write_dword(flash, wp, data)) != 0)
190 goto DONE;
191 wp += 8;
192 cnt -= 8;
193 }
194
195 if (cnt == 0) {
196 rc = 0;
197 goto DONE;
198 }
199
200 data = cfi_read(flash, wp);
201 for(i = 0; i < cnt; i++)
202 t[i] = *src++;
203 rc = write_dword(flash, wp, data);
204DONE:
205 set_msr(msr);
206 return rc;
207}
208
209static int cfi_erase_oneblock(flash_info_t *flash, uint32_t sect)
210{
211 int sa;
212 int flag;
213 ulong start, last, now;
214 cfi_word status;
215
216 flag = disable_interrupts();
217
218 sa = (flash->start[sect] - flash->start[0]);
219 write32(flash->start[sect], 0x00200020);
220 write32(flash->start[sect], 0x00d000d0);
221
222 if (flag)
223 enable_interrupts();
224
225 udelay(1000);
226 start = get_timer (0);
227 last = start;
228
229 for (;;) {
230 status = cfi_read(flash, sa);
231 status &= CMD(0x80);
232 if (status == CMD(0x80))
233 break;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200234 if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
wdenk9f837932003-10-09 19:00:25 +0000235 cfi_cmd(flash, 0xff, 0);
236 printf ("Timeout\n");
237 return ERR_TIMOUT;
238 }
239
240 if ((now - last) > 1000) {
241 serial_putc ('.');
242 last = now;
243 }
244 udelay(10);
245 }
246 cfi_cmd(flash, 0xff, 0);
247 return ERR_OK;
248}
249
250static int cfi_erase(flash_info_t *flash, uint32_t s_first, uint32_t s_last)
251{
252 int sect;
253 int rc = ERR_OK;
254
255 for (sect = s_first; sect <= s_last; sect++) {
256 if (flash->protect[sect] == 0) {
257 rc = cfi_erase_oneblock(flash, sect);
258 if (rc != ERR_OK) break;
259 }
260 }
261 printf (" done\n");
262 return rc;
263}
264
265static int jedec_erase(flash_info_t *flash, uint32_t s_first, uint32_t s_last)
266{
267 int sect;
268 cfi_word status;
269 int sa = -1;
270 int flag;
271 ulong start, last, now;
272
273 flag = disable_interrupts();
274
275 cfi_cmd(flash, 0xaa, 0x555);
276 cfi_cmd(flash, 0x55, 0x2aa);
277 cfi_cmd(flash, 0x80, 0x555);
278 cfi_cmd(flash, 0xaa, 0x555);
279 cfi_cmd(flash, 0x55, 0x2aa);
280 for ( sect = s_first; sect <= s_last; sect++) {
281 if (flash->protect[sect] == 0) {
282 sa = flash->start[sect] - flash->start[0];
283 write32(flash->start[sect], 0x00300030);
284 }
285 }
286 if (flag)
287 enable_interrupts();
288
289 if (sa < 0)
290 goto DONE;
291
292 udelay (1000);
293 start = get_timer (0);
294 last = start;
295 for(;;) {
296 status = cfi_read(flash, sa);
297 if (status == CMD(0xffff))
298 break;
299
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200300 if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
wdenk9f837932003-10-09 19:00:25 +0000301 printf ("Timeout\n");
302 return ERR_TIMOUT;
303 }
304
305 if ((now - last) > 1000) {
306 serial_putc ('.');
307 last = now;
308 }
309 udelay(10);
310 }
311DONE:
312 cfi_cmd(flash, 0xf0, 0);
313
314 printf (" done\n");
315
316 return ERR_OK;
317}
318
319int flash_erase (flash_info_t *flash, int s_first, int s_last)
320{
321 int sect;
322 int prot;
323
324 if ((s_first < 0) || (s_first > s_last)) {
325 if (flash->flash_id == FLASH_UNKNOWN)
326 printf ("- missing\n");
327 else
328 printf ("- no sectors to erase\n");
329 return ERR_NOT_ERASED;
330 }
331 if (flash->flash_id == FLASH_UNKNOWN) {
332 printf ("Can't erase unknown flash type - aborted\n");
333 return ERR_NOT_ERASED;
334 }
335
336 prot = 0;
337 for (sect = s_first; sect <= s_last; sect++)
338 if (flash->protect[sect]) prot++;
339
340 if (prot)
341 printf ("- Warning: %d protected sectors will not be erased!\n",
342 prot);
343 else
344 printf ("\n");
345
346 return do_flash_erase(flash, s_first, s_last);
347}
348
349struct jedec_flash_info {
350 const uint16_t mfr_id;
351 const uint16_t dev_id;
352 const char *name;
353 const int DevSize;
354 const int InterfaceDesc;
355 const int NumEraseRegions;
356 const ulong regions[4];
357};
358
359#define ERASEINFO(size,blocks) (size<<8)|(blocks-1)
360
361#define SIZE_1MiB 20
362#define SIZE_2MiB 21
363#define SIZE_4MiB 22
364
365static const struct jedec_flash_info jedec_table[] = {
366 {
367 mfr_id: (uint16_t)AMD_MANUFACT,
368 dev_id: (uint16_t)AMD_ID_LV800T,
369 name: "AMD AM29LV800T",
370 DevSize: SIZE_1MiB,
371 NumEraseRegions: 4,
372 regions: {ERASEINFO(0x10000,15),
373 ERASEINFO(0x08000,1),
374 ERASEINFO(0x02000,2),
375 ERASEINFO(0x04000,1)
376 }
377 }, {
378 mfr_id: (uint16_t)AMD_MANUFACT,
379 dev_id: (uint16_t)AMD_ID_LV800B,
380 name: "AMD AM29LV800B",
381 DevSize: SIZE_1MiB,
382 NumEraseRegions: 4,
383 regions: {ERASEINFO(0x10000,15),
384 ERASEINFO(0x08000,1),
385 ERASEINFO(0x02000,2),
386 ERASEINFO(0x04000,1)
387 }
388 }, {
389 mfr_id: (uint16_t)AMD_MANUFACT,
390 dev_id: (uint16_t)AMD_ID_LV160T,
391 name: "AMD AM29LV160T",
392 DevSize: SIZE_2MiB,
393 NumEraseRegions: 4,
394 regions: {ERASEINFO(0x10000,31),
395 ERASEINFO(0x08000,1),
396 ERASEINFO(0x02000,2),
397 ERASEINFO(0x04000,1)
398 }
399 }, {
400 mfr_id: (uint16_t)AMD_MANUFACT,
401 dev_id: (uint16_t)AMD_ID_LV160B,
402 name: "AMD AM29LV160B",
403 DevSize: SIZE_2MiB,
404 NumEraseRegions: 4,
405 regions: {ERASEINFO(0x04000,1),
406 ERASEINFO(0x02000,2),
407 ERASEINFO(0x08000,1),
408 ERASEINFO(0x10000,31)
409 }
410 }, {
411 mfr_id: (uint16_t)AMD_MANUFACT,
412 dev_id: (uint16_t)AMD_ID_LV320T,
413 name: "AMD AM29LV320T",
414 DevSize: SIZE_4MiB,
415 NumEraseRegions: 2,
416 regions: {ERASEINFO(0x10000,63),
417 ERASEINFO(0x02000,8)
418 }
419
420 }, {
421 mfr_id: (uint16_t)AMD_MANUFACT,
422 dev_id: (uint16_t)AMD_ID_LV320B,
423 name: "AMD AM29LV320B",
424 DevSize: SIZE_4MiB,
425 NumEraseRegions: 2,
426 regions: {ERASEINFO(0x02000,8),
427 ERASEINFO(0x10000,63)
428 }
429 }
430};
431
432static ulong cfi_init(uint32_t base, flash_info_t *flash)
433{
434 int sector;
435 int block;
436 int block_count;
437 int offset = 0;
438 int reverse = 0;
439 int primary;
440 int mfr_id;
441 int dev_id;
442
443 flash->start[0] = base;
444 cfi_cmd(flash, 0xF0, 0);
445 cfi_cmd(flash, 0x98, 0);
446 if ( !( cfi_read_query(flash, 0x10) == 'Q' &&
447 cfi_read_query(flash, 0x11) == 'R' &&
448 cfi_read_query(flash, 0x12) == 'Y' )) {
449 cfi_cmd(flash, 0xff, 0);
450 return 0;
451 }
452
453 flash->size = 1 << cfi_read_query(flash, 0x27);
454 flash->size *= 4;
455 block_count = cfi_read_query(flash, 0x2c);
456 primary = cfi_read_query(flash, 0x15);
457 if ( cfi_read_query(flash, primary + 4) == 0x30)
458 reverse = (cfi_read_query(flash, 0x1) & 0x01);
459 else
460 reverse = (cfi_read_query(flash, primary+15) == 3);
461
462 flash->sector_count = 0;
463
464 for ( block = reverse ? block_count - 1 : 0;
465 reverse ? block >= 0 : block < block_count;
466 reverse ? block-- : block ++) {
467 int sector_size =
468 (cfi_read_query(flash, 0x2d + block*4+2) |
469 (cfi_read_query(flash, 0x2d + block*4+3) << 8)) << 8;
470 int sector_count =
471 (cfi_read_query(flash, 0x2d + block*4+0) |
472 (cfi_read_query(flash, 0x2d + block*4+1) << 8)) + 1;
473 for(sector = 0; sector < sector_count; sector++) {
474 flash->start[flash->sector_count++] = base + offset;
475 offset += sector_size * 4;
476 }
477 }
478 mfr_id = cfi_read_query(flash, 0x00);
479 dev_id = cfi_read_query(flash, 0x01);
480
481 cfi_cmd(flash, 0xff, 0);
482
483 flash->flash_id = (mfr_id << 16) | dev_id;
484
485 for (sector = 0; sector < flash->sector_count; sector++) {
486 write32(flash->start[sector], 0x00600060);
487 write32(flash->start[sector], 0x00d000d0);
488 }
489 cfi_cmd(flash, 0xff, 0);
490
491 for (sector = 0; sector < flash->sector_count; sector++)
492 flash->protect[sector] = 0;
493
494 do_flash_erase = cfi_erase;
495 write_dword = cfi_write_dword;
496
497 return flash->size;
498}
499
500static ulong jedec_init(unsigned long base, flash_info_t *flash)
501{
502 int i;
503 int block, block_count;
504 int sector, offset;
505 int mfr_id, dev_id;
506 flash->start[0] = base;
507 cfi_cmd(flash, 0xF0, 0x000);
508 cfi_cmd(flash, 0xAA, 0x555);
509 cfi_cmd(flash, 0x55, 0x2AA);
510 cfi_cmd(flash, 0x90, 0x555);
511 mfr_id = cfi_read_query(flash, 0x000);
512 dev_id = cfi_read_query(flash, 0x0001);
513 cfi_cmd(flash, 0xf0, 0x000);
514
515 for(i=0; i<sizeof(jedec_table)/sizeof(struct jedec_flash_info); i++) {
516 if((jedec_table[i].mfr_id == mfr_id) &&
517 (jedec_table[i].dev_id == dev_id)) {
518
519 flash->flash_id = (mfr_id << 16) | dev_id;
520 flash->size = 1 << jedec_table[0].DevSize;
521 flash->size *= 4;
522 block_count = jedec_table[i].NumEraseRegions;
523 offset = 0;
524 flash->sector_count = 0;
525 for (block = 0; block < block_count; block++) {
526 int sector_size = jedec_table[i].regions[block];
527 int sector_count = (sector_size & 0xff) + 1;
528 sector_size >>= 8;
529 for (sector=0; sector<sector_count; sector++) {
530 flash->start[flash->sector_count++] =
531 base + offset;
532 offset += sector_size * 4;
533 }
534 }
535 break;
536 }
537 }
538
539 for (sector = 0; sector < flash->sector_count; sector++)
540 flash->protect[sector] = 0;
541
542 do_flash_erase = jedec_erase;
543 write_dword = jedec_write_dword;
544
545 return flash->size;
546}
547
548inline void mtibat1u(unsigned int x)
549{
550 __asm__ __volatile__ ("mtspr 530, %0" :: "r" (x));
551}
552
553inline void mtibat1l(unsigned int x)
554{
555 __asm__ __volatile__ ("mtspr 531, %0" :: "r" (x));
556}
557
558inline void mtdbat1u(unsigned int x)
559{
560 __asm__ __volatile__ ("mtspr 538, %0" :: "r" (x));
561}
562
563inline void mtdbat1l(unsigned int x)
564{
565 __asm__ __volatile__ ("mtspr 539, %0" :: "r" (x));
566}
567
568unsigned long flash_init (void)
569{
570 unsigned long size = 0;
571 int i;
572 unsigned int msr;
573
574 /* BAT1 */
575 CONFIG_WRITE_WORD(ERCR3, 0x0C00000C);
576 CONFIG_WRITE_WORD(ERCR4, 0x0800000C);
577 msr = get_msr();
578 set_msr(msr & ~(MSR_IR | MSR_DR));
579 mtibat1l(0x70000000 | BATL_PP_10 | BATL_CACHEINHIBIT);
580 mtibat1u(0x70000000 | BATU_BL_256M | BATU_VS | BATU_VP);
581 mtdbat1l(0x70000000 | BATL_PP_10 | BATL_CACHEINHIBIT);
582 mtdbat1u(0x70000000 | BATU_BL_256M | BATU_VS | BATU_VP);
583 set_msr(msr);
584
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200585 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++)
wdenk9f837932003-10-09 19:00:25 +0000586 flash_info[i].flash_id = FLASH_UNKNOWN;
587 size = cfi_init(FLASH_BASE0_PRELIM, &flash_info[0]);
588 if (!size)
589 size = jedec_init(FLASH_BASE0_PRELIM, &flash_info[0]);
590
591 if (flash_info[0].flash_id == FLASH_UNKNOWN)
592 printf ("# Unknown FLASH on Bank 1 - Size = 0x%08lx = %ld MB\n",
593 size, size<<20);
594
595 return size;
596}
597
598void flash_print_info (flash_info_t *flash)
599{
600 int i;
601 int k;
602 int size;
603 int erased;
604 volatile unsigned long *p;
605
606 if (flash->flash_id == FLASH_UNKNOWN) {
607 printf ("missing or unknown FLASH type\n");
608 flash_init();
609 }
610
611 if (flash->flash_id == FLASH_UNKNOWN) {
612 printf ("missing or unknown FLASH type\n");
613 return;
614 }
615
616 switch (((flash->flash_id) >> 16) & 0xff) {
617 case 0x01:
618 printf ("AMD ");
619 break;
620 case 0x04:
621 printf("FUJITSU ");
622 break;
623 case 0x20:
624 printf("STM ");
625 break;
626 case 0xBF:
627 printf("SST ");
628 break;
629 case 0x89:
630 case 0xB0:
631 printf("INTEL ");
632 break;
633 default:
634 printf ("Unknown Vendor ");
635 break;
636 }
637
638 switch ((flash->flash_id) & 0xffff) {
639 case (uint16_t)AMD_ID_LV800T:
640 printf ("AM29LV800T\n");
641 break;
642 case (uint16_t)AMD_ID_LV800B:
643 printf ("AM29LV800B\n");
644 break;
645 case (uint16_t)AMD_ID_LV160T:
646 printf ("AM29LV160T\n");
647 break;
648 case (uint16_t)AMD_ID_LV160B:
649 printf ("AM29LV160B\n");
650 break;
651 case (uint16_t)AMD_ID_LV320T:
652 printf ("AM29LV320T\n");
653 break;
654 case (uint16_t)AMD_ID_LV320B:
655 printf ("AM29LV320B\n");
656 break;
657 case (uint16_t)INTEL_ID_28F800C3T:
658 printf ("28F800C3T\n");
659 break;
660 case (uint16_t)INTEL_ID_28F800C3B:
661 printf ("28F800C3B\n");
662 break;
663 case (uint16_t)INTEL_ID_28F160C3T:
664 printf ("28F160C3T\n");
665 break;
666 case (uint16_t)INTEL_ID_28F160C3B:
667 printf ("28F160C3B\n");
668 break;
669 case (uint16_t)INTEL_ID_28F320C3T:
670 printf ("28F320C3T\n");
671 break;
672 case (uint16_t)INTEL_ID_28F320C3B:
673 printf ("28F320C3B\n");
674 break;
675 case (uint16_t)INTEL_ID_28F640C3T:
676 printf ("28F640C3T\n");
677 break;
678 case (uint16_t)INTEL_ID_28F640C3B:
679 printf ("28F640C3B\n");
680 break;
681 default:
682 printf ("Unknown Chip Type\n");
683 break;
684 }
685
686 if (flash->size >= (1 << 20)) {
687 printf (" Size: %ld MB in %d Sectors\n",
688 flash->size >> 20, flash->sector_count);
689 } else {
690 printf (" Size: %ld kB in %d Sectors\n",
691 flash->size >> 10, flash->sector_count);
692 }
693
694 printf (" Sector Start Addresses:");
695 for (i = 0; i < flash->sector_count; ++i) {
696 /* Check if whole sector is erased*/
697 if (i != (flash->sector_count-1))
698 size = flash->start[i+1] - flash->start[i];
699 else
700 size = flash->start[0] + flash->size - flash->start[i];
701
702 erased = 1;
703 p = (volatile unsigned long *)flash->start[i];
704 size = size >> 2; /* divide by 4 for longword access */
705 for (k=0; k<size; k++) {
706 if (*p++ != 0xffffffff) {
707 erased = 0;
708 break;
709 }
710 }
711
712 if ((i % 5) == 0)
713 printf ("\n ");
714
715 printf (" %08lX%s%s",
716 flash->start[i],
717 erased ? " E" : " ",
718 flash->protect[i] ? "RO " : " ");
719 }
720 printf ("\n");
721}