• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008, Google Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <boot/boot.h>
30 #include <boot/flash.h>
31 
32 #include <msm7k/dmov.h>
33 #include <msm7k/nand.h>
34 #include <msm7k/shared.h>
35 
36 #define VERBOSE 0
37 
38 typedef struct dmov_ch dmov_ch;
39 struct dmov_ch
40 {
41     volatile unsigned cmd;
42     volatile unsigned result;
43     volatile unsigned status;
44     volatile unsigned config;
45 };
46 
dmov_prep_ch(dmov_ch * ch,unsigned id)47 static void dmov_prep_ch(dmov_ch *ch, unsigned id)
48 {
49     ch->cmd = DMOV_CMD_PTR(id);
50     ch->result = DMOV_RSLT(id);
51     ch->status = DMOV_STATUS(id);
52     ch->config = DMOV_CONFIG(id);
53 }
54 
55 #define SRC_CRCI_NAND_CMD  CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
56 #define DST_CRCI_NAND_CMD  CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
57 #define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
58 #define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
59 
60 static unsigned CFG0, CFG1;
61 
62 #define CFG1_WIDE_FLASH (1U << 1)
63 
64 #define paddr(n) ((unsigned) (n))
65 
dmov_exec_cmdptr(unsigned id,unsigned * ptr)66 static int dmov_exec_cmdptr(unsigned id, unsigned *ptr)
67 {
68     dmov_ch ch;
69     unsigned n;
70 
71     dmov_prep_ch(&ch, id);
72 
73     writel(DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(paddr(ptr)), ch.cmd);
74 
75     while(!(readl(ch.status) & DMOV_STATUS_RSLT_VALID)) ;
76 
77     n = readl(ch.status);
78     while(DMOV_STATUS_RSLT_COUNT(n)) {
79         n = readl(ch.result);
80         if(n != 0x80000002) {
81             dprintf("ERROR: result: %x\n", n);
82             dprintf("ERROR:  flush: %x %x %x %x\n",
83                     readl(DMOV_FLUSH0(DMOV_NAND_CHAN)),
84                     readl(DMOV_FLUSH1(DMOV_NAND_CHAN)),
85                     readl(DMOV_FLUSH2(DMOV_NAND_CHAN)),
86                     readl(DMOV_FLUSH3(DMOV_NAND_CHAN)));
87         }
88         n = readl(ch.status);
89     }
90 
91     return 0;
92 }
93 
94 static unsigned flash_maker = 0;
95 static unsigned flash_device = 0;
96 
flash_read_id(dmov_s * cmdlist,unsigned * ptrlist)97 static void flash_read_id(dmov_s *cmdlist, unsigned *ptrlist)
98 {
99     dmov_s *cmd = cmdlist;
100     unsigned *ptr = ptrlist;
101     unsigned *data = ptrlist + 4;
102 
103     data[0] = 0 | 4;
104     data[1] = NAND_CMD_FETCH_ID;
105     data[2] = 1;
106     data[3] = 0;
107     data[4] = 0;
108 
109     cmd[0].cmd = 0 | CMD_OCB;
110     cmd[0].src = paddr(&data[0]);
111     cmd[0].dst = NAND_FLASH_CHIP_SELECT;
112     cmd[0].len = 4;
113 
114     cmd[1].cmd = DST_CRCI_NAND_CMD;
115     cmd[1].src = paddr(&data[1]);
116     cmd[1].dst = NAND_FLASH_CMD;
117     cmd[1].len = 4;
118 
119     cmd[2].cmd = 0;
120     cmd[2].src = paddr(&data[2]);
121     cmd[2].dst = NAND_EXEC_CMD;
122     cmd[2].len = 4;
123 
124     cmd[3].cmd = SRC_CRCI_NAND_DATA;
125     cmd[3].src = NAND_FLASH_STATUS;
126     cmd[3].dst = paddr(&data[3]);
127     cmd[3].len = 4;
128 
129     cmd[4].cmd = CMD_OCU | CMD_LC;
130     cmd[4].src = NAND_READ_ID;
131     cmd[4].dst = paddr(&data[4]);
132     cmd[4].len = 4;
133 
134     ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
135 
136     dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
137 
138 #if VERBOSE
139     dprintf("status: %x\n", data[3]);
140 #endif
141     dprintf("nandid: %x maker %b device %b\n",
142             data[4], data[4] & 0xff, (data[4] >> 8) & 0xff);
143 
144     flash_maker = data[4] & 0xff;
145     flash_device = (data[4] >> 8) & 0xff;
146 }
147 
flash_erase_block(dmov_s * cmdlist,unsigned * ptrlist,unsigned page)148 static int flash_erase_block(dmov_s *cmdlist, unsigned *ptrlist, unsigned page)
149 {
150     dmov_s *cmd = cmdlist;
151     unsigned *ptr = ptrlist;
152     unsigned *data = ptrlist + 4;
153 
154         /* only allow erasing on block boundaries */
155     if(page & 63) return -1;
156 
157     data[0] = NAND_CMD_BLOCK_ERASE;
158     data[1] = page;
159     data[2] = 0;
160     data[3] = 0 | 4;
161     data[4] = 1;
162     data[5] = 0xeeeeeeee;
163     data[6] = CFG0 & (~(7 << 6));  /* CW_PER_PAGE = 0 */
164     data[7] = CFG1;
165 
166     cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
167     cmd[0].src = paddr(&data[0]);
168     cmd[0].dst = NAND_FLASH_CMD;
169     cmd[0].len = 16;
170 
171     cmd[1].cmd = 0;
172     cmd[1].src = paddr(&data[6]);
173     cmd[1].dst = NAND_DEV0_CFG0;
174     cmd[1].len = 8;
175 
176     cmd[2].cmd = 0;
177     cmd[2].src = paddr(&data[4]);
178     cmd[2].dst = NAND_EXEC_CMD;
179     cmd[2].len = 4;
180 
181     cmd[3].cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;
182     cmd[3].src = NAND_FLASH_STATUS;
183     cmd[3].dst = paddr(&data[5]);
184     cmd[3].len = 4;
185 
186     ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
187 
188     dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
189 
190 #if VERBOSE
191     dprintf("status: %x\n", data[5]);
192 #endif
193 
194         /* we fail if there was an operation error, a mpu error, or the
195         ** erase success bit was not set.
196         */
197     if(data[5] & 0x110) return -1;
198     if(!(data[5] & 0x80)) return -1;
199 
200     return 0;
201 }
202 
203 struct data_flash_io {
204     unsigned cmd;
205     unsigned addr0;
206     unsigned addr1;
207     unsigned chipsel;
208     unsigned cfg0;
209     unsigned cfg1;
210     unsigned exec;
211     unsigned ecc_cfg;
212     unsigned ecc_cfg_save;
213     struct {
214         unsigned flash_status;
215         unsigned buffer_status;
216     } result[4];
217 };
218 
_flash_read_page(dmov_s * cmdlist,unsigned * ptrlist,unsigned page,void * _addr,void * _spareaddr)219 static int _flash_read_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page, void *_addr, void *_spareaddr)
220 {
221     dmov_s *cmd = cmdlist;
222     unsigned *ptr = ptrlist;
223     struct data_flash_io *data = (void*) (ptrlist + 4);
224     unsigned addr = (unsigned) _addr;
225     unsigned spareaddr = (unsigned) _spareaddr;
226     unsigned n;
227 
228     data->cmd = NAND_CMD_PAGE_READ_ECC;
229     data->addr0 = page << 16;
230     data->addr1 = (page >> 16) & 0xff;
231     data->chipsel = 0 | 4; /* flash0 + undoc bit */
232 
233         /* GO bit for the EXEC register */
234     data->exec = 1;
235 
236     data->cfg0 = CFG0;
237     data->cfg1 = CFG1;
238 
239     data->ecc_cfg = 0x203;
240 
241         /* save existing ecc config */
242     cmd->cmd = CMD_OCB;
243     cmd->src = NAND_EBI2_ECC_BUF_CFG;
244     cmd->dst = paddr(&data->ecc_cfg_save);
245     cmd->len = 4;
246     cmd++;
247 
248     for(n = 0; n < 4; n++) {
249             /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
250         cmd->cmd = DST_CRCI_NAND_CMD;
251         cmd->src = paddr(&data->cmd);
252         cmd->dst = NAND_FLASH_CMD;
253         cmd->len = ((n == 0) ? 16 : 4);
254         cmd++;
255 
256         if (n == 0) {
257                 /* block on cmd ready, set configuration */
258             cmd->cmd = 0;
259             cmd->src = paddr(&data->cfg0);
260             cmd->dst = NAND_DEV0_CFG0;
261             cmd->len = 8;
262             cmd++;
263 
264                 /* set our ecc config */
265             cmd->cmd = 0;
266             cmd->src = paddr(&data->ecc_cfg);
267             cmd->dst = NAND_EBI2_ECC_BUF_CFG;
268             cmd->len = 4;
269             cmd++;
270         }
271             /* kick the execute register */
272         cmd->cmd = 0;
273         cmd->src = paddr(&data->exec);
274         cmd->dst = NAND_EXEC_CMD;
275         cmd->len = 4;
276         cmd++;
277 
278             /* block on data ready, then read the status register */
279         cmd->cmd = SRC_CRCI_NAND_DATA;
280         cmd->src = NAND_FLASH_STATUS;
281         cmd->dst = paddr(&data->result[n]);
282         cmd->len = 8;
283         cmd++;
284 
285             /* read data block */
286         cmd->cmd = 0;
287         cmd->src = NAND_FLASH_BUFFER;
288         cmd->dst = addr + n * 516;
289         cmd->len = ((n < 3) ? 516 : 500);
290         cmd++;
291     }
292 
293         /* read extra data */
294     cmd->cmd = 0;
295     cmd->src = NAND_FLASH_BUFFER + 500;
296     cmd->dst = spareaddr;
297     cmd->len = 16;
298     cmd++;
299 
300         /* restore saved ecc config */
301     cmd->cmd = CMD_OCU | CMD_LC;
302     cmd->src = paddr(&data->ecc_cfg_save);
303     cmd->dst = NAND_EBI2_ECC_BUF_CFG;
304     cmd->len = 4;
305 
306     ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
307 
308     dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
309 
310 #if VERBOSE
311     dprintf("read page %d: status: %x %x %x %x\n",
312             page, data[5], data[6], data[7], data[8]);
313 	for(n = 0; n < 4; n++) {
314 	    ptr = (unsigned*)(addr + 512 * n);
315 	    dprintf("data%d:   %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
316 	    ptr = (unsigned*)(spareaddr + 16 * n);
317 	    dprintf("spare data%d   %x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
318 	}
319 #endif
320 
321         /* if any of the writes failed (0x10), or there was a
322         ** protection violation (0x100), we lose
323         */
324     for(n = 0; n < 4; n++) {
325         if (data->result[n].flash_status & 0x110) {
326             return -1;
327         }
328     }
329 
330     return 0;
331 }
332 
_flash_write_page(dmov_s * cmdlist,unsigned * ptrlist,unsigned page,const void * _addr,const void * _spareaddr)333 static int _flash_write_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page,
334                              const void *_addr, const void *_spareaddr)
335 {
336     dmov_s *cmd = cmdlist;
337     unsigned *ptr = ptrlist;
338     struct data_flash_io *data = (void*) (ptrlist + 4);
339     unsigned addr = (unsigned) _addr;
340     unsigned spareaddr = (unsigned) _spareaddr;
341     unsigned n;
342 
343     data->cmd = NAND_CMD_PRG_PAGE;
344     data->addr0 = page << 16;
345     data->addr1 = (page >> 16) & 0xff;
346     data->chipsel = 0 | 4; /* flash0 + undoc bit */
347 
348     data->cfg0 = CFG0;
349     data->cfg1 = CFG1;
350 
351         /* GO bit for the EXEC register */
352     data->exec = 1;
353 
354     data->ecc_cfg = 0x203;
355 
356         /* save existing ecc config */
357     cmd->cmd = CMD_OCB;
358     cmd->src = NAND_EBI2_ECC_BUF_CFG;
359     cmd->dst = paddr(&data->ecc_cfg_save);
360     cmd->len = 4;
361     cmd++;
362 
363     for(n = 0; n < 4; n++) {
364             /* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
365         cmd->cmd = DST_CRCI_NAND_CMD;
366         cmd->src = paddr(&data->cmd);
367         cmd->dst = NAND_FLASH_CMD;
368         cmd->len = ((n == 0) ? 16 : 4);
369         cmd++;
370 
371         if (n == 0) {
372                 /*  set configuration */
373             cmd->cmd = 0;
374             cmd->src = paddr(&data->cfg0);
375             cmd->dst = NAND_DEV0_CFG0;
376             cmd->len = 8;
377             cmd++;
378 
379                 /* set our ecc config */
380             cmd->cmd = 0;
381             cmd->src = paddr(&data->ecc_cfg);
382             cmd->dst = NAND_EBI2_ECC_BUF_CFG;
383             cmd->len = 4;
384             cmd++;
385         }
386 
387             /* write data block */
388         cmd->cmd = 0;
389         cmd->src = addr + n * 516;
390         cmd->dst = NAND_FLASH_BUFFER;
391         cmd->len = ((n < 3) ? 516 : 510);
392         cmd++;
393 
394         if (n == 3) {
395                 /* write extra data */
396             cmd->cmd = 0;
397             cmd->src = spareaddr;
398             cmd->dst = NAND_FLASH_BUFFER + 500;
399             cmd->len = 16;
400             cmd++;
401         }
402 
403             /* kick the execute register */
404         cmd->cmd = 0;
405         cmd->src = paddr(&data->exec);
406         cmd->dst = NAND_EXEC_CMD;
407         cmd->len = 4;
408         cmd++;
409 
410             /* block on data ready, then read the status register */
411         cmd->cmd = SRC_CRCI_NAND_DATA;
412         cmd->src = NAND_FLASH_STATUS;
413         cmd->dst = paddr(&data->result[n]);
414         cmd->len = 8;
415         cmd++;
416     }
417 
418         /* restore saved ecc config */
419     cmd->cmd = CMD_OCU | CMD_LC;
420     cmd->src = paddr(&data->ecc_cfg_save);
421     cmd->dst = NAND_EBI2_ECC_BUF_CFG;
422     cmd->len = 4;
423 
424     ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
425 
426     dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
427 
428 #if VERBOSE
429     dprintf("write page %d: status: %x %x %x %x\n",
430             page, data[5], data[6], data[7], data[8]);
431 #endif
432 
433         /* if any of the writes failed (0x10), or there was a
434         ** protection violation (0x100), or the program success
435         ** bit (0x80) is unset, we lose
436         */
437     for(n = 0; n < 4; n++) {
438         if(data->result[n].flash_status & 0x110) return -1;
439         if(!(data->result[n].flash_status & 0x80)) return -1;
440     }
441 
442     return 0;
443 }
444 
445 unsigned nand_cfg0;
446 unsigned nand_cfg1;
447 
flash_read_config(dmov_s * cmdlist,unsigned * ptrlist)448 static int flash_read_config(dmov_s *cmdlist, unsigned *ptrlist)
449 {
450     cmdlist[0].cmd = CMD_OCB;
451     cmdlist[0].src = NAND_DEV0_CFG0;
452     cmdlist[0].dst = paddr(&CFG0);
453     cmdlist[0].len = 4;
454 
455     cmdlist[1].cmd = CMD_OCU | CMD_LC;
456     cmdlist[1].src = NAND_DEV0_CFG1;
457     cmdlist[1].dst = paddr(&CFG1);
458     cmdlist[1].len = 4;
459 
460     *ptrlist = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
461 
462     dmov_exec_cmdptr(DMOV_NAND_CHAN, ptrlist);
463 
464     if((CFG0 == 0) || (CFG1 == 0)) {
465         return -1;
466     }
467 
468     dprintf("nandcfg: %x %x (initial)\n", CFG0, CFG1);
469 
470 	CFG0 = (3 <<  6)  /* 4 codeword per page for 2k nand */
471 		|  (516 <<  9)  /* 516 user data bytes */
472 		|   (10 << 19)  /* 10 parity bytes */
473 		|    (5 << 27)  /* 5 address cycles */
474 		|    (1 << 30)  /* Read status before data */
475 		|    (1 << 31)  /* Send read cmd */
476             /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */
477 		| ((nand_cfg1 & CFG1_WIDE_FLASH) ? (0 << 23) : (1 << 23));
478 	CFG1 = (0 <<  0)  /* Enable ecc */
479 		|    (7 <<  2)  /* 8 recovery cycles */
480 		|    (0 <<  5)  /* Allow CS deassertion */
481 		|  (465 <<  6)  /* Bad block marker location */
482 		|    (0 << 16)  /* Bad block in user data area */
483 		|    (2 << 17)  /* 6 cycle tWB/tRB */
484 		| (nand_cfg1 & CFG1_WIDE_FLASH); /* preserve wide flash flag */
485 
486     dprintf("nandcfg: %x %x (used)\n", CFG0, CFG1);
487 
488     return 0;
489 }
490 
491 static unsigned *flash_ptrlist;
492 static dmov_s *flash_cmdlist;
493 static void *flash_spare;
494 static void *flash_data;
495 
flash_init(void)496 int flash_init(void)
497 {
498     flash_ptrlist = alloc(1024);
499     flash_cmdlist = alloc(1024);
500     flash_data = alloc(2048);
501     flash_spare = alloc(64);
502 
503     if(flash_read_config(flash_cmdlist, flash_ptrlist)) {
504         dprintf("ERROR: could not read CFG0/CFG1 state\n");
505         for(;;);
506     }
507 
508     flash_read_id(flash_cmdlist, flash_ptrlist);
509 
510     return 0;
511 }
512 
flash_erase(ptentry * ptn)513 int flash_erase(ptentry *ptn)
514 {
515     unsigned block = ptn->start;
516     unsigned count = ptn->length;
517 
518     while(count-- > 0) {
519         if(flash_erase_block(flash_cmdlist, flash_ptrlist, block * 64)) {
520             dprintf("cannot erase @ %d (bad block?)\n", block);
521         }
522         block++;
523     }
524     return 0;
525 }
526 
flash_read_ext(ptentry * ptn,unsigned extra_per_page,unsigned offset,void * data,unsigned bytes)527 int flash_read_ext(ptentry *ptn, unsigned extra_per_page, unsigned offset, void *data, unsigned bytes)
528 {
529     unsigned page = (ptn->start * 64) + (offset / 2048);
530     unsigned lastpage = (ptn->start + ptn->length) * 64;
531     unsigned count = (bytes + 2047 + extra_per_page) / (2048 + extra_per_page);
532     unsigned *spare = (unsigned*) flash_spare;
533     unsigned errors = 0;
534     unsigned char *image = data;
535 
536     if(offset & 2047)
537         return -1;
538 
539     while(page < lastpage) {
540         if(count == 0) {
541             dprintf("flash_read_image: success (%d errors)\n", errors);
542             return 0;
543         }
544 
545         if(_flash_read_page(flash_cmdlist, flash_ptrlist, page++, image, spare)) {
546             errors++;
547             continue;
548         }
549         image += 2048;
550         memcpy(image, spare, extra_per_page);
551         image += extra_per_page;
552         count -= 1;
553     }
554 
555         /* could not find enough valid pages before we hit the end */
556     dprintf("flash_read_image: failed (%d errors)\n", errors);
557     return 0xffffffff;
558 }
559 
flash_write(ptentry * ptn,unsigned extra_per_page,const void * data,unsigned bytes)560 int flash_write(ptentry *ptn, unsigned extra_per_page, const void *data, unsigned bytes)
561 {
562     unsigned page = ptn->start * 64;
563     unsigned lastpage = (ptn->start + ptn->length) * 64;
564     unsigned *spare = (unsigned*) flash_spare;
565     const unsigned char *image = data;
566     unsigned wsize = 2048 + extra_per_page;
567     unsigned n;
568     int r;
569 
570     for(n = 0; n < 16; n++) spare[n] = 0xffffffff;
571 
572     while(bytes > 0) {
573         if(bytes < wsize) {
574             dprintf("flash_write_image: image undersized (%d < %d)\n", bytes, wsize);
575             return -1;
576         }
577         if(page >= lastpage) {
578             dprintf("flash_write_image: out of space\n");
579             return -1;
580         }
581 
582         if((page & 63) == 0) {
583             if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
584                 dprintf("flash_write_image: bad block @ %d\n", page >> 6);
585                 page += 64;
586                 continue;
587             }
588         }
589 
590         if(extra_per_page) {
591             r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, image + 2048);
592         } else {
593             r = _flash_write_page(flash_cmdlist, flash_ptrlist, page++, image, spare);
594         }
595         if(r) {
596             dprintf("flash_write_image: write failure @ page %d (src %d)\n", page, image - (const unsigned char *)data);
597             image -= (page & 63) * wsize;
598             bytes += (page & 63) * wsize;
599             page &= ~63;
600             if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
601                 dprintf("flash_write_image: erase failure @ page %d\n", page);
602             }
603             dprintf("flash_write_image: restart write @ page %d (src %d)\n", page, image - (const unsigned char *)data);
604             page += 64;
605             continue;
606         }
607 
608         image += wsize;
609         bytes -= wsize;
610     }
611 
612         /* erase any remaining pages in the partition */
613     page = (page + 63) & (~63);
614     while(page < lastpage){
615         if(flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
616             dprintf("flash_write_image: bad block @ %d\n", page >> 6);
617         }
618         page += 64;
619     }
620 
621     dprintf("flash_write_image: success\n");
622     return 0;
623 }
624 
flash_read_page(unsigned page,void * data,void * extra)625 static int flash_read_page(unsigned page, void *data, void *extra)
626 {
627     return _flash_read_page(flash_cmdlist, flash_ptrlist,
628                             page, data, extra);
629 }
630 
631