Home | History | Annotate | Download | only in arch_msm7k
      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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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