]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/etin/debris/flash.c
imported Freescale specific U-Boot additions for i.MX28,... release L2.6.31_10.08.01
[karo-tx-uboot.git] / board / etin / debris / flash.c
1 /*
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>
30 #include <asm/mmu.h>
31
32 int (*do_flash_erase)(flash_info_t*, uint32_t, uint32_t);
33 int (*write_dword)(flash_info_t*, ulong, uint64_t);
34
35 typedef 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
45 static void write32(unsigned long addr, uint32_t value)
46 {
47         *(volatile uint32_t*)(addr) = value;
48         asm volatile("sync");
49 }
50
51 static 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
59 static 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
68 static 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
75 flash_info_t    flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* info for FLASH chips */
76
77 static 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
85 static 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;
103                 if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
104                         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
114 static 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) {
132                 if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT)
133                         return 1;
134                 status = cfi_read(flash, dest);
135                 udelay(1);
136         }
137         return 0;
138 }
139
140 static __inline__ unsigned long get_msr(void)
141 {
142         unsigned long msr;
143         __asm__ __volatile__ ("mfmsr %0" : "=r" (msr) :);
144         return msr;
145 }
146
147 static __inline__ void set_msr(unsigned long msr)
148 {
149         __asm__ __volatile__ ("mtmsr %0" : : "r" (msr));
150 }
151
152 int 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);
204 DONE:
205         set_msr(msr);
206         return rc;
207 }
208
209 static 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;
234                 if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
235                         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
250 static 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
265 static 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
300                 if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
301                         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         }
311 DONE:
312         cfi_cmd(flash, 0xf0, 0);
313
314         printf (" done\n");
315
316         return ERR_OK;
317 }
318
319 int 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
349 struct 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
365 static 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
432 static 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
500 static 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
548 inline void mtibat1u(unsigned int x)
549 {
550         __asm__ __volatile__ ("mtspr   530, %0" :: "r" (x));
551 }
552
553 inline void mtibat1l(unsigned int x)
554 {
555         __asm__ __volatile__ ("mtspr   531, %0" :: "r" (x));
556 }
557
558 inline void mtdbat1u(unsigned int x)
559 {
560         __asm__ __volatile__ ("mtspr   538, %0" :: "r" (x));
561 }
562
563 inline void mtdbat1l(unsigned int x)
564 {
565         __asm__ __volatile__ ("mtspr   539, %0" :: "r" (x));
566 }
567
568 unsigned 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
585         for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++)
586                 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
598 void 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 }