Mercurial > nand-ecoscentric
view packages/io/nand/current/src/nand_bbt.c @ 2991:1368d5ff0323
nand_bbt.c: Fix logic error on initial scan (introduced 2009-11-09)
| author | Ross Younger <wry@ecoscentric.com> |
|---|---|
| date | Fri, 13 Nov 2009 17:23:51 +0000 |
| parents | 7050b2f79b6e |
| children | 3d3b0d53c2e5 |
line wrap: on
line source
//============================================================================= // // nand_bbt.c // // eCos NAND flash library - bad block table // //============================================================================= // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 2009 eCosCentric Limited. // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 or (at your option) any later // version. // // eCos is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License // along with eCos; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // As a special exception, if other files instantiate templates or use // macros or inline functions from this file, or you compile this file // and link it with other works to produce a work based on this file, // this file does not by itself cause the resulting work to be covered by // the GNU General Public License. However the source code for this file // must still be made available in accordance with section (3) of the GNU // General Public License v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //============================================================================= //#####DESCRIPTIONBEGIN#### // // Author(s): wry // Date: 2009-03-11 // //####DESCRIPTIONEND#### //============================================================================= /* This source is intended to be on-chip compatible with the Linux MTD layer. * TODO: Multi-chip devices are not yet implemented. * ASSUMPTION: 1<<blockcount_bits will fit in an int. */ #include <cyg/infra/cyg_ass.h> #include <cyg/infra/diag.h> #include <cyg/nand/nand.h> #include <cyg/nand/util.h> #include "nand_bbt.h" #include CYGBLD_ISO_ERRNO_CODES_HEADER #include <string.h> #include <stdlib.h> /* ============================================================ */ // We need a global page buffer so we can manipulate the // Bad Block Table. This has to be big enough for the largest // sized page we will handle, so each driver has to declare // it with a _requires_ statement in its CDL. #if CYGNUM_NAND_PAGEBUFFER == 0 #error CYGNUM_NAND_PAGEBUFFER not set /* If you've tripped over this error, it means that either there are * no NAND drivers present in your config, or they don't correctly * impose a requirement on CYGNUM_NAND_PAGEBUFFER and therefore * the logic that uses the global pagebuffer (in nand_bbt.c) could * not possibly work correctly. */ #endif static unsigned char bbt_pagebuf[CYGNUM_NAND_PAGEBUFFER]; cyg_drv_mutex_t nand_bbt_pagebuf_lock; __externC void cyg_nand_bbt_initx(void) { cyg_drv_mutex_init(&nand_bbt_pagebuf_lock); } #define LOCK_PAGEBUF() do { \ cyg_drv_mutex_lock(&nand_bbt_pagebuf_lock); \ i_locked = 1; \ } while(0) #define UNLOCK_PAGEBUF() do { \ cyg_drv_mutex_unlock(&nand_bbt_pagebuf_lock); \ i_locked = 0; \ } while(0) CYG_BYTE* nandi_grab_pagebuf(void) { cyg_drv_mutex_lock(&nand_bbt_pagebuf_lock); return &bbt_pagebuf[0]; } void nandi_release_pagebuf(void) { cyg_drv_mutex_unlock(&nand_bbt_pagebuf_lock); } #ifdef CYGSEM_IO_NAND_USE_BBT #ifndef CYGSEM_IO_NAND_READONLY static int cyg_nand_bbti_write_tables(cyg_nand_device *dev, int is_initial); #endif /* Mapping between on-chip and in-ram statuses ===================== */ static inline cyg_nand_bbt_status_t chip_2_ram(nand_bbti_onchip_status_t in) { switch(in) { case BBTI_FACTORY_BAD: return CYG_NAND_BBT_FACTORY_BAD; case BBTI_WORNBAD_1: return CYG_NAND_BBT_WORNBAD; case BBTI_WORNBAD_2: return CYG_NAND_BBT_WORNBAD; case BBTI_OK: return CYG_NAND_BBT_OK; } CYG_FAIL("Invalid input to chip_2_ram"); return CYG_NAND_BBT_FACTORY_BAD; } static inline nand_bbti_onchip_status_t ram_2_chip(cyg_nand_bbt_status_t in) { switch(in) { case CYG_NAND_BBT_FACTORY_BAD: return BBTI_FACTORY_BAD; case CYG_NAND_BBT_WORNBAD: return BBTI_WORNBAD_1; case CYG_NAND_BBT_RESERVED: return BBTI_OK; case CYG_NAND_BBT_OK: return BBTI_OK; } CYG_FAIL("Invalid input to ram_2_chip"); return BBTI_FACTORY_BAD; } /* ============================================================= */ /* Update the in-RAM table, but do _not_ write it out. Unchecked! */ static void bbti_mark_raw(cyg_nand_device *dev, cyg_nand_block_addr blk, cyg_nand_bbt_status_t st) { unsigned offset = blk >> 2; unsigned byteshift = (blk & 3) * 2; dev->bbt.data[offset] = (dev->bbt.data[offset] &~ (3 << byteshift)) | (st << byteshift); } int cyg_nand_bbti_query(cyg_nand_device *dev, cyg_nand_block_addr blk) { CYG_ASSERT(blk >= 0 && blk <= NAND_BLOCKCOUNT(dev), "valid block"); unsigned offset = blk >> 2; unsigned byteshift = (blk & 3) * 2; return ( dev->bbt.data[offset] >> byteshift ) & 3; } int cyg_nand_bbti_markany(cyg_nand_device *dev, cyg_nand_block_addr blk, cyg_nand_bbt_status_t st) { CYG_ASSERT(blk >= 0 && blk <= NAND_BLOCKCOUNT(dev), "valid block"); bbti_mark_raw(dev, blk, st); #ifdef CYGSEM_IO_NAND_READONLY NAND_CHATTER(2,dev,"ignoring mark request on read-only config\n"); return -ENOSYS; #else int rv = cyg_nand_bbti_write_tables(dev,0); if (rv) NAND_ERROR(dev,"nand_bbti_mark %d as %d: %d (%s)\n", blk, st, -rv, strerror(-rv)); return rv; #endif } int cyg_nand_bbti_markbad(cyg_nand_device *dev, cyg_nand_block_addr blk) { CYG_ASSERT(blk >= 0 && blk <= NAND_BLOCKCOUNT(dev), "valid block"); return cyg_nand_bbti_markany(dev, blk, CYG_NAND_BBT_WORNBAD); } /* Search for, and read in, the BBTs. * * Algorithm: Start at the last block, work backwards (up to 4 blocks) * looking for the signature. * DEVICE MUST BE LOCKED. * Table descriptor patterns are 'Bbt0' primary / '1tbB' mirror. * Descriptors are located at offset 8 of the OOB area of the first * page of the block, length 4 bytes. * Table version tags are ON by default. * Version tag is a _single byte_ at offset 12 within the OOB area of * the first page of the block. A freshly created table has version 1; * it is incremented whenever the table is updated. * (FIXME: What happens after version 255 is not defined.) * If both tables are the same version, either may be used. * If their versions differ, the higher version is used. */ static CYG_BYTE nand_pattern_primary[] = { 'B','b','t','0' }; static CYG_BYTE nand_pattern_mirror [] = { '1','t','b','B' }; #define NAND_PATTERN_OFFSET 8 #define NAND_PATTERN_SIZE 4 #define NAND_VERSION_OFFSET 12 #define NAND_VERSION_SIZE 1 #define EG(x) do { rv = (x); if (rv != 0) goto err_exit; } while(0) /* Of these two block states, which is worst? * "current" is a CYG_NAND_BBT_ (in-RAM) state. * "onchip" is a BBTI_ state (on-chip!) * Output is in-RAM format (CYG_NAND_BBT_). * */ static inline CYG_BYTE worst_of_the_two(CYG_BYTE current, CYG_BYTE onchip) { CYG_ASSERT( (current == (current&3)) && (onchip == (onchip&3)), "invalid input to w_o_t_t"); /* * OnChip | OK WornBad Reserved FactoryBad < Current * | 00 01 10 11 * +--------------------------------------- * FB 00 | FB 11 FB 11 FB 11 FB 11 { * | * WB 01 | WB 01 WB 01 WB 01 FB 11 { Output * or 10 | * | * OK 11 | OK 00 WB 01 Res 10 FB 11 { * * ('Reserved' never appears on chip.) */ // Let the compiler take the strain, lookup tables are vv cheap switch ( (onchip << 2) | current) { // I would have used '0b0000' syntax, but gcc 3 doesn't support it case 0: case 1: case 2: case 3: return 3; case 4: case 8: case 5: case 9: case 6: case 10: return 1; case 7: case 11: return 3; case 12: return 0; case 13: return 1; case 14: return 2; case 15: return 3; } /* should never get here, indicates bad input and asserts disabled */ return current; // least bad alternative if asserts disabled } static int bbti_incorporate_one(cyg_nand_device *dev, cyg_nand_block_addr blk, CYG_BYTE version) { int rv = 0, i_locked = 0; LOCK_PAGEBUF(); cyg_nand_page_addr pg = CYG_NAND_BLOCK2PAGEADDR(dev,blk); unsigned char *optr = dev->bbt.data; if (version > dev->bbt.version) dev->bbt.version = version; int blks_to_read = 1 << dev->blockcount_bits; while (blks_to_read>0) { rv = nandi_read_whole_page_raw(dev, pg, bbt_pagebuf, 0, 0, 1); // read_page_raw does the ecc for us, and we don't care about the OOB here as we already know it's one of ours. if (rv<0) { // This is bad. int is_primary = blk == dev->bbt.primary; NAND_CHATTER(1,dev, "Reading BBT %s (page %u): uncorrectable error\n", is_primary ? "primary" : "mirror", pg); (void) is_primary; EG(rv); } if (rv>0) { NAND_CHATTER(2,dev,"Reading BBT page %u: ECC repaired, code %d\n", pg, rv); rv=0; } unsigned char *iptr = bbt_pagebuf; int data = blks_to_read / 4; /* one byte per 4 blocks on-chip */ if (data > (1<<dev->page_bits)) data = (1<<dev->page_bits); while (data) { unsigned char inp = *iptr; unsigned char cur = *optr; unsigned char out = 0; int j; // Each byte tells us about four blocks; so does each output byte // NB: Currently hard-coded to 2 bits both in RAM and on chip! for (j=0; j<8; j+=2) { unsigned char newchip = (inp >> j) & 3; //unsigned char new_ram = chip_2_ram(newchip); unsigned char curstat = (cur >> j) & 3; unsigned char new_ram = worst_of_the_two(curstat, newchip); // MTD does it in this order too. out |= new_ram << j; } *optr = out; ++iptr; ++optr; --data; blks_to_read -= 4; } // inner while(data) ++pg; } // outer while(blks_to_read>0) err_exit: if (i_locked) UNLOCK_PAGEBUF(); return rv; } /* ============================================================= */ static int nandi_bbt_packing_test(cyg_nand_device *dev) { // Startup sanity check: Can we pack the BBT marker and version into the app spare area, do they appear in the right place, and can we extract them? CYG_BYTE appspare[NAND_APPSPARE_PER_PAGE(dev)], packed[NAND_SPARE_PER_PAGE(dev)], testdata[NAND_PATTERN_SIZE > NAND_VERSION_SIZE ? NAND_PATTERN_SIZE : NAND_VERSION_SIZE], ecc[CYG_NAND_ECCPERPAGE(dev)]; int i, rv, fail = 0; #define MAGIC 42 memset(ecc, 0xff, sizeof ecc); memset(testdata, 0, sizeof testdata); for (i=0; i<NAND_PATTERN_SIZE; i++) testdata[i] = i+MAGIC; i = nand_oob_packed_write(dev, NAND_PATTERN_OFFSET, NAND_PATTERN_SIZE, appspare, testdata); if (i == 0) i = nand_oob_packed_write(dev, NAND_VERSION_OFFSET, NAND_VERSION_SIZE, appspare, testdata); if (i != 0) { NAND_ERROR(dev,"BUG: BBT ident/version offset will not fit into this device's spare area\n"); /* Read the warning in nand_oob.c by nand_mtd_oob_8. * To make that work, you have to teach this layer how to find * the BBT on such a device. */ EG(-ENOSYS); } nand_oob_pack(dev, appspare, sizeof appspare, ecc, packed); for (i=0; i<NAND_PATTERN_SIZE; i++) if (packed[NAND_PATTERN_OFFSET + i] != i + MAGIC) ++fail; for (i=0; i<NAND_VERSION_SIZE; i++) if (packed[NAND_PATTERN_OFFSET + i] != i + MAGIC) ++fail; if (fail) { NAND_ERROR(dev, "BUG: NAND BBT tag pack did not work"); EG(-ENOSYS); } memset(appspare, 0xff, sizeof appspare); nand_oob_unpack(dev, appspare, sizeof appspare, ecc, packed); memset(testdata, 0, sizeof testdata); i = nand_oob_packed_read(dev, NAND_PATTERN_OFFSET, NAND_PATTERN_SIZE, appspare, testdata); for (i=0; i<NAND_PATTERN_SIZE; i++) if (testdata[i] != i + MAGIC) ++fail; memset(testdata, 0, sizeof testdata); i = nand_oob_packed_read(dev, NAND_VERSION_OFFSET, NAND_VERSION_SIZE, appspare, testdata); for (i=0; i<NAND_VERSION_SIZE; i++) if (testdata[i] != i + MAGIC) ++fail; for (i=0; i < sizeof ecc; i++) if (ecc[i] != 0xff) ++fail; if (fail) { NAND_ERROR(dev, "BUG: NAND BBT tag unpack did not work"); EG(-ENOSYS); } rv = 0; err_exit: return rv; } /* ============================================================= */ #if (NAND_VERSION_SIZE != 1) #error CT_ASSERT: BBT version handling needs recoded #endif /* Looks for the BBT copies, or for plausible places to put them. * If @find_only@: outputs are set to 0xFFFFFFFF where the respective * ident patterns are not found. * Else: outputs are set to plausible-looking places to write BBTs; * where the ident patterns are found, those block IDs are used. * Only if there is nowhere plausible to go (blocks marked bad) then * the relevant output(s) are set to 0xFFFFFFFF. * If live BBTs were found, their version identifiers will be stored * in *pri_ver and *mir_ver. If not, they will be untouched. */ static int nandi_find_bbt_locations(cyg_nand_device *dev, cyg_nand_block_addr *pri_o, CYG_BYTE*pri_ver, cyg_nand_block_addr *mir_o, CYG_BYTE*mir_ver, int find_only) { cyg_nand_block_addr start = (1<<dev->blockcount_bits) - 1; CYG_BYTE oobbuf[NAND_APPSPARE_PER_PAGE(dev)]; CYG_BYTE patternbuf[NAND_PATTERN_SIZE]; CYG_BYTE versionbuf[NAND_VERSION_SIZE]; int i, rv=0; cyg_nand_block_addr pri, mir, maybepri, maybemir; pri = mir = maybepri = maybemir = 0xFFFFFFFF; for (i=0; i<4; i++) { cyg_nand_block_addr blk = start-i; cyg_nand_page_addr pg = CYG_NAND_BLOCK2PAGEADDR(dev,blk); #define BAD(b) ((cyg_nand_bbti_query(dev,b) != CYG_NAND_BBT_OK) && (cyg_nand_bbti_query(dev,b) != CYG_NAND_BBT_RESERVED)) if (BAD(blk)) continue; // look for the pattern memset(oobbuf, 0xff, sizeof oobbuf); rv = nandi_read_whole_page_raw(dev, pg, 0, oobbuf, sizeof oobbuf, 1); if (rv<0) { NAND_CHATTER(1,dev, "find_bbt_locations: Error %d reading OOB of page %u (block %u)\n", -rv, pg, blk); EG(-EIO); } rv = nand_oob_packed_read(dev, NAND_PATTERN_OFFSET, NAND_PATTERN_SIZE, oobbuf, patternbuf); if (rv != 0) EG(-EIO); // should never fail given the sanity check above rv = nand_oob_packed_read(dev, NAND_VERSION_OFFSET, NAND_VERSION_SIZE, oobbuf, versionbuf); if (rv != 0) EG(-EIO); // should never fail given the sanity check above if (0==memcmp(patternbuf, nand_pattern_primary, NAND_PATTERN_SIZE)) { CYG_BYTE found_ver = *versionbuf; if (found_ver > *pri_ver) { *pri_ver = found_ver; pri = blk; continue; } } if (0==memcmp(patternbuf, nand_pattern_mirror, NAND_PATTERN_SIZE)) { CYG_BYTE found_ver = *versionbuf; if (found_ver > *mir_ver) { *mir_ver = found_ver; mir = blk; continue; } } if (find_only) continue; // It's not an existing table, and it's not bad, so it might be // a plausible place to put one. if (maybepri == 0xFFFFFFFF) { maybepri = blk; continue; } if (maybemir == 0xFFFFFFFF) { maybemir = blk; continue; } #undef BAD } *pri_o = (pri == 0xFFFFFFFF) ? maybepri : pri; *mir_o = (mir == 0xFFFFFFFF) ? maybemir : mir; err_exit: return rv; } int cyg_nand_bbti_find_tables(cyg_nand_device *dev) { int rv = 0, got = 0; CYG_BYTE pri_ver = 0, mir_ver = 0; NAND_CHATTER(3,dev, "Looking for bad-block table:\n"); /* TODO: What happens if a BBT block goes bad? Can we put it beyond use? * - erasing it _should_ clear the descriptor pattern. * Obviously, we should look for further instances of the BBT patterns * with fresher version tag(s). */ EG(nandi_bbt_packing_test(dev)); EG(nandi_find_bbt_locations(dev, &dev->bbt.primary, &pri_ver, &dev->bbt.mirror, &mir_ver, 1)); /* OK, we found one or both, so read them in. * We must read both, if available, and OR them together. */ // TODO: Default pattern hard-coded for now. #define all_ok_pattern ( CYG_NAND_BBT_OK | (CYG_NAND_BBT_OK << 2) | \ (CYG_NAND_BBT_OK << 4) | (CYG_NAND_BBT_OK << 6) ) memset(dev->bbt.data, all_ok_pattern, dev->bbt.datasize); if (dev->bbt.primary != 0xFFFFFFFF) { ++got; NAND_CHATTER(3,dev, "Found primary BBT in block %u\n", dev->bbt.primary); rv = bbti_incorporate_one(dev, dev->bbt.primary, pri_ver); bbti_mark_raw(dev, dev->bbt.primary, CYG_NAND_BBT_RESERVED); } int rv2 = 0; if (dev->bbt.mirror != 0xFFFFFFFF) { ++got; NAND_CHATTER(3,dev, "Found mirror BBT in block %u\n", dev->bbt.mirror); rv2 = bbti_incorporate_one(dev, dev->bbt.mirror, mir_ver); bbti_mark_raw(dev, dev->bbt.mirror, CYG_NAND_BBT_RESERVED); } if (!got) { NAND_CHATTER(3,dev,"Found no BBTs on chip\n"); EG(-ENOENT); } if ( (rv < 0) && (rv2 < 0) ) { NAND_ERROR(dev, "No BBT usable - disabling device\n"); EG(-EIO); } /* TODO: The choice of BBT location and behaviour (BBT versioning; * on-chip format) could in future be affected by CDL options or by * device-specific settings. * The Linux MTD layer has some examples of this. */ err_exit: return rv; } /* Creates the initial BBTs by running a scan for factory bad blocks. * Returns: 0 if OK, else a -ve error code. */ int cyg_nand_bbti_build_tables(cyg_nand_device *dev) { cyg_nand_block_addr blk, max = (1<<dev->blockcount_bits); NAND_CHATTER(1, dev, "Scanning for factory-bad blocks:\n"); for (blk=0; blk < max; blk++) { int rv = dev->fns->is_factory_bad(dev, blk); if (rv<0) { NAND_ERROR(dev,"NAND factory-bad scan failed on block %u (%u: %s)\n", blk, -rv, strerror(-rv)); return rv; } if (rv) { bbti_mark_raw(dev, blk, CYG_NAND_BBT_FACTORY_BAD); NAND_CHATTER(2, dev, "bad: %u\n", blk); } } NAND_CHATTER(2, dev, "... done\n"); #ifdef CYGSEM_IO_NAND_READONLY NAND_CHATTER(1,dev, "Read-only config: not writing out BBT\n"); return 0; #else /* This is a virgin device, so we have free choice. * write_tables() takes care of choosing where. */ dev->bbt.version = 0; /* write_tables() will bump to 1, which is correct */ int rv = cyg_nand_bbti_write_tables(dev, 1); if (rv<0) NAND_ERROR(dev,"Error %d writing out initial BBT: %s\n", -rv, strerror(-rv)); else NAND_CHATTER(3,dev, "Chosen BBT locations: primary %u, mirror %u\n", dev->bbt.primary, dev->bbt.mirror); if (dev->bbt.primary != 0xFFFFFFFF) bbti_mark_raw(dev, dev->bbt.primary, CYG_NAND_BBT_RESERVED); if (dev->bbt.mirror != 0xFFFFFFFF) bbti_mark_raw(dev, dev->bbt.mirror, CYG_NAND_BBT_RESERVED); return rv; #endif } #ifndef CYGSEM_IO_NAND_READONLY /* Erases and writes out a single copy of the BBT. * If an error occurred, returns appropriately; if -EIO, there was * evidently a write error in writing out the BBT and we need to restart * the write loop... */ static int bbti_write_one_table(cyg_nand_device *dev, cyg_nand_block_addr blk, CYG_BYTE *pattern) { const unsigned pagesize = 1 << dev->page_bits; size_t bbt_disk_size = (1<<dev->blockcount_bits)/4; int st = cyg_nand_bbti_query(dev, blk); if (st == CYG_NAND_BBT_WORNBAD) { NAND_ERROR(dev, "BBT block %u is worn-bad, not updating\n", blk); return 0; } int rv = dev->fns->erase_block(dev, blk); if (rv<0) { /* We can't arbitrarily pick another block for the BBT, * because we might trample on application data. * All the Linux MTD layer does is warn and error out, * so that's good enough for us. */ NAND_ERROR(dev,"Erase BBT block %u failed; cannot update table!\n", blk); return rv; } cyg_nand_page_addr pg = CYG_NAND_BLOCK2PAGEADDR(dev,blk); CYG_BYTE *iptr = dev->bbt.data; int to_write = bbt_disk_size; // on-chip size, number of bytes int i_locked = 0; LOCK_PAGEBUF(); while (to_write) { CYG_BYTE *optr = bbt_pagebuf; int this_write = to_write; if (this_write >= pagesize) this_write = pagesize; else memset(bbt_pagebuf, 0xff, sizeof(bbt_pagebuf)); /* Hard-coded 2-bit conversion for now. */ while (this_write) { CYG_BYTE d = *iptr, out = 0; int j; for (j=0; j<8; j+=2) { CYG_BYTE inp = (d >> j) & 3; out |= ram_2_chip(inp) << j; } *optr = out; --to_write; --this_write; ++iptr; ++optr; } /* Prep ECC & OOB, then send */ CYG_BYTE appspare[NAND_APPSPARE_PER_PAGE(dev)]; memset(appspare, 0xff, sizeof appspare); rv = nand_oob_packed_write(dev, NAND_PATTERN_OFFSET, NAND_PATTERN_SIZE, appspare, pattern); if (rv != 0) EG(-EIO); // Should never fail, we've passed the startup sanity check. rv = nand_oob_packed_write(dev, NAND_VERSION_OFFSET, NAND_VERSION_SIZE, appspare, &dev->bbt.version); if (rv != 0) EG(-EIO); // Should never fail, we've passed the startup sanity check. rv = nandi_write_page_raw(dev, pg, bbt_pagebuf, appspare, sizeof appspare); if (rv==-EIO) { /* Ouch. Our BBT block has failed. We cannot write another, * as we might trample app data. We'll mark bad, and hope * that the other copy is still usable. */ bbti_mark_raw(dev, blk, CYG_NAND_BBT_WORNBAD); NAND_ERROR(dev, "Write error on BBT block %u, marking bad\n", blk); goto err_exit; } if (rv<0) { /* As with an erase fail, we can't do very much here. */ NAND_ERROR(dev,"Unexpected error writing to BBT block %u; cannot update table!\n", blk); goto err_exit; } ++pg; } err_exit: UNLOCK_PAGEBUF(); return rv; } /* Increments the BBT version, then writes out _both_ tables. * Caller must have the devlock. */ static int cyg_nand_bbti_write_tables(cyg_nand_device *dev, int is_initial) { int rv, retries=-1; top: ++retries; if (dev->bbt.version == 255) NAND_ERROR(dev,"Warning! NAND BBT version would overflow, doing the best we can\n"); else dev->bbt.version ++; CYG_BYTE pri_ver, mir_ver; rv = nandi_find_bbt_locations(dev, &dev->bbt.primary, &pri_ver, &dev->bbt.mirror, &mir_ver, is_initial ? 0 : 1); #define RETRY_LIMIT 3 if (dev->bbt.primary == 0xFFFFFFFF) { NAND_ERROR(dev,"Cannot find a location to write primary BBT!\n"); return -EIO; } else { rv = bbti_write_one_table(dev, dev->bbt.primary, nand_pattern_primary); if (rv == -EIO && retries < RETRY_LIMIT) goto top; } if (dev->bbt.mirror == 0xFFFFFFFF) { NAND_ERROR(dev,"Cannot find a location to write mirror BBT!\n"); return -EIO; } else { rv = bbti_write_one_table(dev, dev->bbt.mirror, nand_pattern_mirror); if (rv == -EIO && retries < RETRY_LIMIT) goto top; } if (retries >= RETRY_LIMIT) NAND_ERROR(dev, "Warning! Retry limit on BBT writes reached, this shouldn't happen...\n"); return 0; } #endif #endif
