#if defined(CONFIG_BCM_KF_MTD_BCMNAND) /* <:copyright-BRCM:2012:DUAL/GPL:standard Copyright (c) 2012 Broadcom All Rights Reserved This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation (the "GPL"). This program 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. A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php, or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. :> File: brcmnand_bbt.c Description: NAND driver for Samsung K9F1G08U0A chip with Broadcom NAND controller. The main difference between the Broadcom controller and OneNAND is that the Broadcom NAND controller has only a 512B cache (bufferram) regardless of the flash chip, whereas OneNAND has multiple bufferram's to match the page size. This complicates this driver quite a bit, because, for large page flash (2K page) we have to read in all 4 slides before we know for sure whether a page is bad. * When brcmnand_scan_bbt is called, then it tries to find the bad block table * depending on the options in the bbt descriptor(s). If a bbt is found * then the contents are read and the memory based bbt is created. If a * mirrored bbt is selected then the mirror is searched too and the * versions are compared. If the mirror has a greater version number * than the mirror bbt is used to build the memory based bbt. * If the tables are not versioned, then we "or" the bad block information. * If one of the bbt's is out of date or does not exist it is (re)created. * If no bbt exists at all then the device is scanned for factory marked * good / bad blocks and the bad block tables are created. * * For manufacturer created bbts like the one found on M-SYS DOC devices * the bbt is searched and read but never created * * The autogenerated bad block table is located in the last good blocks * of the device. The table is mirrored, so it can be updated eventually. * The table is marked in the oob area with an ident pattern and a version * number which indicates which of both tables is more up to date. * * The table uses 2 bits per block * 11b: block is good * 00b: block is factory marked bad * 01b, 10b: block is marked bad due to wear * * The memory bad block table uses the following scheme: * 00b: block is good * 01b: block is marked bad due to wear * 10b: block is reserved (to protect the bbt area) * 11b: block is factory marked bad * * Multichip devices like DOC store the bad block info per floor. * * Following assumptions are made: * - bbts start at a page boundary, if autolocated on a block boundary * - the space necessary for a bbt in FLASH does not exceed a block boundary * when who what ----- --- ---- 070807 tht codings derived from nand_base & brcmnand_bbt implementations. */ #include #include #include #include #include #include #include #include #include "brcmnand_priv.h" #include #include #include #define PRINTK(...) do { } while (0) //#define PRINTK printk //#define DEBUG_BBT #define DEBUG(...) do { } while (0) extern int gClearBBT; extern int gdebug; //char brcmNandBBTMsg[1024]; /* brcmnand= * rescan: 1. Rescan for bad blocks, and update existing BBT * showbbt: 2. Print out the contents of the BBT on boot up. * * The following commands are implemented but should be removed for production builds. * Use userspace flash_eraseall instead. * These were intended for development debugging only. * erase: 7. Erase entire flash, except CFE, and rescan for bad blocks * eraseall: 8. Erase entire flash, and rescan for bad blocks * clearbbt: 9. Erase BBT and rescan for bad blocks. (DANGEROUS, may lose Mfg's BIs). */ #define NANDCMD_RESCAN 1 #define NANDCMD_SHOWBBT 2 #define NANDCMD_ERASE 7 #define NANDCMD_ERASEALL 8 #define NANDCMD_CLEARBBT 9 int brcmnand_update_bbt(struct mtd_info *mtd, loff_t offs); /** * check_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search * @len: the length of buffer to search * @paglen: the pagelength * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block * tables and good / bad block identifiers. * If the SCAN_EMPTY option is set then check, if all bytes except the * pattern area contain 0xff * */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { int i, end = 0; uint8_t *p = buf; PRINTK("Check_pattern len=%d, pagelen=%d, td->offs-%d, pattern=%c%c%c%c\n", len, paglen, td->offs, td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); #ifdef DEBUG_BBT if (gdebug) { printk("oobbuf=\n"); print_oobbuf(&buf[paglen], len - paglen); } #endif end = paglen + td->offs; if (td->options & NAND_BBT_SCANEMPTY) { for (i = 0; i < end; i++) { if (p[i] != 0xff) { PRINTK("check_pattern 1: p[%d] == %02x - expect FF\n", i, p[i]); return -1; } } } p += end; /* Compare the pattern */ for (i = 0; i < td->len; i++) { if (p[i] != td->pattern[i]) { PRINTK("%s: expect @i=%d td->pat[%d]=%02x, found p[%d]=%02x\n", __FUNCTION__, i, i, td->pattern[i], td->offs + i, p[td->offs + i]); return -1; } } if (td->options & NAND_BBT_SCANEMPTY) { p += td->len; end += td->len; for (i = end; i < len; i++) { if (*p++ != 0xff) { PRINTK("check_pattern 2: p[%d] == %02x - expect FF\n", i, p[i]); return -1; } } } return 0; } /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block * tables and good / bad block identifiers. Same as check_pattern, but * no optional empty check * */ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td) { int i; uint8_t *p = buf; /* Compare the pattern */ for (i = 0; i < td->len; i++) { if (p[td->offs + i] != td->pattern[i]) { PRINTK("%s: expect @i=%d td->pat[%d]=%02x, found p[%d]=%02x\n", __FUNCTION__, i, i, td->pattern[i], td->offs + i, p[td->offs + i]); return -1; } } return 0; } /** * brcmnand_read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure * @buf: temporary buffer * @page: the starting page * @num: the number of bbt descriptors to read * @bits: number of bits per block * @offs: offset in the memory table * @reserved_block_code: Pattern to identify reserved blocks * * Read the bad block table starting from page. * */ static int brcmnand_read_bbt(struct mtd_info *mtd, uint8_t *buf, int64_t page, int num, int bits, int offs, int reserved_block_code) { int res, i, j, act = 0; struct brcmnand_chip *this = mtd->priv; size_t retlen, len, totlen; loff_t from; uint8_t msk = (uint8_t)((1 << bits) - 1); totlen = (num * bits) >> 3; from = ((loff_t)page) << this->page_shift; /* * Clear ECC registers */ this->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); this->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); while (totlen) { len = min(totlen, (size_t)(1 << this->bbt_erase_shift)); PRINTK("%s: calling read_ecc len=%d, bits=%d, num=%d, totallen=%d\n", __FUNCTION__, len, bits, num, totlen); res = mtd_read(mtd, from, len, &retlen, buf); if (res < 0) { if (retlen != len) { printk(KERN_INFO "brcmnand_bbt: Error reading bad block table\n"); return res; } printk(KERN_ERR "%s: ECC error %d while reading bad block table\n", __FUNCTION__, res); /* THT 11/10/09: If read fails, we should ignore the data, so return w/o analyzing it */ return res; } /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; for (j = 0; j < 8; j += bits, act += 2) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); mtd->ecc_stats.bbtblocks++; continue; } /* Leave it for now, if its matured we can move this * message to MTD_DEBUG_LEVEL0 */ printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n", ((offs << 2) + (act >> 1)) << this->bbt_erase_shift); /* Factory marked bad or worn out ? */ if (tmp == 0) this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); else this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); mtd->ecc_stats.badblocks++; } } totlen -= len; from += (loff_t)len; } return 0; } /** * brcmnand_read_abs_bbt - [GENERIC] Read the bad block table starting at a given page * @mtd: MTD device structure * @buf: temporary buffer * @td: descriptor for the bad block table * @chip: read the table for a specific chip, -1 read all chips. * Applies only if NAND_BBT_PERCHIP option is set * * Read the bad block table for all chips starting at a given page * We assume that the bbt bits are in consecutive order. */ static int brcmnand_read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) { struct brcmnand_chip *this = mtd->priv; int res = 0, i; int bits; PRINTK("-->%s, numchips=%d, chip=%d\n", __FUNCTION__, this->ctrl->numchips, chip); bits = td->options & NAND_BBT_NRBITS_MSK; if (td->options & NAND_BBT_PERCHIP) { int offs = 0; for (i = 0; i < this->ctrl->numchips; i++) { if (chip == -1 || chip == i) res = brcmnand_read_bbt(mtd, buf, td->pages[i], this->chipSize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); if (res) { PRINTK("<-- brcmnand_read_abs_bbt ret = %d\n", res); return res; } offs += this->chipSize >> (this->bbt_erase_shift + 2); } } else { PRINTK("%s: read BBT at %llx\n", __FUNCTION__, td->pages[0]); res = brcmnand_read_bbt(mtd, buf, td->pages[0], (uint32_t)(this->mtdSize >> this->bbt_erase_shift), bits, 0, td->reserved_block_code); if (res) { PRINTK("<-- brcmnand_read_abs_bbt 2 ret = %d\n", res); return res; } } PRINTK("<-- brcmnand_read_abs_bbt ret 0\n"); return 0; } /* * Scan read raw data from flash */ static int brcmnand_scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; int ret; ops.mode = MTD_OPS_RAW; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.oobbuf = &buf[mtd->writesize]; ops.datbuf = buf; ops.len = len; ret = mtd_read_oob(mtd, offs, &ops); PRINTK("%s: Reading BBT Sig @%0llx, OOB=\n", __FUNCTION__, offs); #ifdef DEBUG_BBT if (gdebug) print_oobbuf(ops.oobbuf, mtd->oobsize); #endif return ret; } /* * Scan write data with oob to flash */ static int brcmnand_scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len, uint8_t *buf, uint8_t *oob) { struct mtd_oob_ops ops; struct brcmnand_chip *this = mtd->priv; int ret; ops.mode = MTD_OPS_PLACE_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.datbuf = buf; ops.oobbuf = oob; ops.len = len; PRINTK("%s: Writing BBT Sig @%0llx, OOB=\n", __FUNCTION__, offs); if (gdebug) print_oobbuf(oob, mtd->oobsize); ret = this->write_oob(mtd, offs, &ops); //gdebug = 0; return ret; } /** * brcmnand_read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * @mtd: MTD device structure * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror * * Read the bad block table(s) for all chips starting at a given page * We assume that the bbt bits are in consecutive order. * */ static int brcmnand_read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) { struct brcmnand_chip *this = mtd->priv; PRINTK("--> %s\n", __FUNCTION__); /* Read the primary version, if available */ if (td->options & NAND_BBT_VERSION) { PRINTK("read primary version\n"); brcmnand_scan_read_raw(mtd, buf, td->pages[0] << this->page_shift, mtd->writesize); td->version[0] = buf[mtd->writesize + td->veroffs]; printk(KERN_DEBUG "Bad block table at page %x, version 0x%02X\n", td->pages[0], td->version[0]); PRINTK("Main bad block table at page %llx, version 0x%02X\n", td->pages[0], td->version[0]); } /* Read the mirror version, if available */ if (md && (md->options & NAND_BBT_VERSION)) { PRINTK("read mirror version\n"); brcmnand_scan_read_raw(mtd, buf, md->pages[0] << this->page_shift, mtd->writesize); md->version[0] = buf[mtd->writesize + md->veroffs]; printk(KERN_DEBUG "Bad block table at page %x, version 0x%02X\n", md->pages[0], md->version[0]); PRINTK( "Mirror bad block table at page %x, version 0x%02X\n", md->pages[0], md->version[0]); } PRINTK("<-- %s\n", __FUNCTION__); return 1; } /* * Scan a given block full */ static int brcmnand_scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, size_t readlen, int scanlen, int len) { int ret, j; ret = brcmnand_scan_read_raw(mtd, buf, offs, readlen); if (ret) return ret; for (j = 0; j < len; j++, buf += scanlen) { if (check_pattern(buf, scanlen, mtd->writesize, bd)) return 1; } return 0; } /* * Scan a given block partially * @offs: Offset of start of block * @len: Number of pages to scan * For MLC we need to read backwards from the end of the block */ static int brcmnand_scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int len) { struct mtd_oob_ops ops; int j, ret; int dir; struct brcmnand_chip *this = mtd->priv; /* * THT 8/23/2010 Changed to use low level test. * Apparently new Micron chips are SLC, but behaves like an MLC flash (requires BCH-4). * The old high level test would look for the BI indicator at the wrong page. * * if (!MTD_IS_MLC(mtd)) { // SLC: First and 2nd page * dir = 1; * } */ if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page dir = 1; }else { // MLC: Read last page (and next to last page). int pagesPerBlock = mtd->erasesize / mtd->writesize; dir = -1; offs += (loff_t)((pagesPerBlock - 1 ) * mtd->writesize); } ops.len = mtd->oobsize; ops.ooblen = mtd->oobsize; ops.oobbuf = buf; ops.ooboffs = 0; ops.datbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; for (j = 0; j < len; j++) { ret = mtd_read_oob(mtd, offs, &ops); if (gdebug && ret != 0) printk("########## %s: read_oob returns %d\n", __FUNCTION__, ret); if (ret == -EBADMSG || ret == -EIO || ret == -ETIMEDOUT) { // Uncorrectable errors uint32_t acc0; // Disable ECC acc0 = brcmnand_disable_read_ecc(this->ctrl->CS[this->csi]); // Re-read the OOB ret = mtd_read_oob(mtd, offs, &ops); // Enable ECC back brcmnand_restore_ecc(this->ctrl->CS[this->csi], acc0); } if (ret) { PRINTK("%s: read_oob returns error %d\n", __FUNCTION__, ret); return ret; } if (check_short_pattern(buf, bd)) return 1; offs += ((int64_t)dir * mtd->writesize); } return 0; } /** * brcmnand_create_bbt - [GENERIC] Create a bad block table by scanning the device * @mtd: MTD device structure * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * @chip: create the table for a specific chip, -1 read all chips. * Applies only if NAND_BBT_PERCHIP option is set * * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ static int brcmnand_create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct brcmnand_chip *this = mtd->priv; int i, len, scanlen; uint64_t numblocks, startblock; loff_t from; size_t readlen; PRINTK("-->brcmnand_create_bbt, bbt_erase_shift=%d, this->page_shift=%d\n", this->bbt_erase_shift, this->page_shift); printk(KERN_INFO "Scanning device for bad blocks, options=%08x\n", bd->options); if (bd->options & NAND_BBT_SCANALLPAGES) len = 1 << (this->bbt_erase_shift - this->page_shift); else { // Also for MLC if (bd->options & NAND_BBT_SCAN2NDPAGE) { if (this->options & NAND_SCAN_BI_3RD_PAGE) { len = 3; // For Hynix MLC chips }else { len = 2; } }else len = 1; } if (!(bd->options & NAND_BBT_SCANEMPTY)) { /* We need only read few bytes from the OOB area */ scanlen = 0; readlen = bd->len; } else { /* Full page content should be read */ scanlen = mtd->writesize + mtd->oobsize; readlen = len * mtd->writesize; } if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here, see i+=2 * below as it makes shifting and masking less painful */ numblocks = device_size(mtd) >> (this->bbt_erase_shift - 1); startblock = 0ULL; from = 0LL; } else { if (chip >= this->ctrl->numchips) { printk(KERN_WARNING "brcmnand_create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->ctrl->numchips); return -EINVAL; } numblocks = this->chipSize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; numblocks += startblock; from = startblock << (this->bbt_erase_shift - 1); } //gdebug=4; if (gdebug > 3) { PRINTK("Starting for loop: from=%0llx bd->options=%08x, startblock=%d numblocks=%d\n", from, bd->options, mtd64_ll_low(startblock), mtd64_ll_low(numblocks)); } for (i = startblock; i < numblocks; ) { int ret; if (bd->options & NAND_BBT_SCANALLPAGES) ret = brcmnand_scan_block_full(mtd, bd, from, buf, readlen, scanlen, len); else ret = brcmnand_scan_block_fast(mtd, bd, from, buf, len); /* * THT 8/24/10: Fall through and mark it as bad if -EBADMSG. * We want to mark it as bad if we can't read it, but we also * don't want to mark the block as bad due to a timeout for example */ if (ret < 0 && ret != (-EBADMSG) && ret != (-EIO)) { PRINTK("$$$$$$$$$$$$$$$$$$$$ brcmnand_scan_block_{fast/full} returns %d\n", ret); // THT 8/24/10: Go to next block instead of returning // return ret; } // -EBADMSG, -EIO and +1 (marked as bad) go here: else if (ret) { this->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int)from); PRINTK("$$$$$$$$$$$$$$$ Bad eraseblock %d at 0x%08x, ret=%d\n", i >> 1, (unsigned int)from, ret); mtd->ecc_stats.badblocks++; } i += 2; from += (loff_t)(1 << this->bbt_erase_shift); } //gdebug=0; return 0; } /** * brcmnand_search_bbt - [GENERIC] scan the device for a specific bad block table * @mtd: MTD device structure * @buf: temporary buffer * @td: descriptor for the bad block table * * Read the bad block table by searching for a given ident pattern. * Search is preformed either from the beginning up or from the end of * the device downwards. The search starts always at the start of a * block. * If the option NAND_BBT_PERCHIP is given, each chip is searched * for a bbt, which contains the bad block information of this chip. * This is necessary to provide support for certain DOC devices. * * The bbt ident pattern resides in the oob area of the first page * in a block. */ static int brcmnand_search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) { struct brcmnand_chip *this = mtd->priv; int i, chips; uint32_t bits, startblock, block; int dir; int scanlen = mtd->writesize + mtd->oobsize; int bbtblocks; int blocktopage = this->bbt_erase_shift - this->page_shift; int ret = 0; PRINTK("-->%s, CS=%d numchips=%d, mtdSize=%llx, mtd->size=%llx\n", __FUNCTION__, this->ctrl->CS[this->csi], this->ctrl->numchips, this->mtdSize, mtd->size); /* Search direction top -> down ? */ if (td->options & NAND_BBT_LASTBLOCK) { startblock = (uint32_t)(this->mtdSize >> this->bbt_erase_shift) - 1; dir = -1; } else { startblock = 0; dir = 1; } /* Do we have a bbt per chip ? */ if (td->options & NAND_BBT_PERCHIP) { chips = this->ctrl->numchips; bbtblocks = this->chipSize >> this->bbt_erase_shift; startblock &= bbtblocks - 1; } else { chips = 1; bbtblocks = (uint32_t)(this->mtdSize >> this->bbt_erase_shift); } /* Number of bits for each erase block in the bbt */ bits = td->options & NAND_BBT_NRBITS_MSK; PRINTK("%s: startblock=%d, dir=%d, chips=%d\n", __FUNCTION__, (int)startblock, dir, chips); for (i = 0; i < chips; i++) { /* Reset version information */ td->version[i] = 0; td->pages[i] = BBT_NULL_PAGE; /* Scan the maximum number of blocks */ for (block = 0; block < td->maxblocks; block++) { int64_t actblock = startblock + dir * block; loff_t offs = (uint64_t)actblock << this->bbt_erase_shift; /* Read first page */ ret = brcmnand_scan_read_raw(mtd, buf, offs, mtd->writesize); /* Here if the read routine returns -77 then the BBT data is invalid, ignore it */ // Ignore BBT if not there. if (ret) continue; PRINTK("Checking Sig %c%c%c%c against OOB\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); /* If scan-auto mode, fish out the useful data from the ECC stuffs */ if (td->options & BRCMNAND_BBT_AUTO_PLACE) { u_char abuf[16]; struct mtd_oob_ops ops; memset(abuf, 0, 16); ops.mode = MTD_OPS_AUTO_OOB; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; ops.oobbuf = abuf; ops.datbuf = buf; ops.len = mtd->writesize; this->oob_poi = &buf[mtd->writesize]; (void)brcmnand_transfer_oob(this, abuf, &ops, td->len + 1); //printk("BCH-8-16 scan: \n"); //print_oobbuf(abuf, td->len+1); /* Look for pattern at the beginning of OOB auto-buffer */ if (!check_pattern(abuf, mtd->oobsize, 0, td)) { PRINTK("%s: Found BBT at offset %0llx\n", __FUNCTION__, offs); td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { td->version[i] = abuf[td->veroffs]; } break; } }else if (!check_pattern(buf, scanlen, mtd->writesize, td)) { PRINTK("%s: Found BBT at offset %0llx\n", __FUNCTION__, offs); td->pages[i] = actblock << blocktopage; if (td->options & NAND_BBT_VERSION) { td->version[i] = buf[mtd->writesize + td->veroffs]; } break; } } startblock += this->chipSize >> this->bbt_erase_shift; } /* Check, if we found a bbt for each requested chip */ for (i = 0; i < chips; i++) { if (td->pages[i] == BBT_NULL_PAGE) { printk(KERN_WARNING "Bad block table %c%c%c%c not found for chip on CS%d\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], this->ctrl->CS[this->csi]); PRINTK( "**************** Bad block table %c%c%c%c not found for chip on CS%d\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], this->ctrl->CS[this->csi]); }else { printk(KERN_DEBUG "Bad block table %c%c%c%c found at page %08lx, version 0x%02X for chip on CS%d\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], (unsigned long)td->pages[i], td->version[i], this->ctrl->CS[this->csi]); PRINTK( "############# Bad block table %c%c%c%c found at page %08lx, version 0x%02X for chip on CS%d\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], (unsigned long)td->pages[i], td->version[i], this->ctrl->CS[this->csi]); } } return 0; } /** * brcmnand_search_read_bbts - [GENERIC] scan the device for bad block table(s) * @mtd: MTD device structure * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror * * Search and read the bad block table(s) */ static int brcmnand_search_read_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) { PRINTK("-->%s\n", __FUNCTION__); /* Search the primary table */ brcmnand_search_bbt(mtd, buf, td); /* Search the mirror table */ if (md) brcmnand_search_bbt(mtd, buf, md); /* Force result check */ return 1; } /** * brcmnand_write_bbt - [GENERIC] (Re)write the bad block table * * @mtd: MTD device structure * @buf: temporary buffer * @td: descriptor for the bad block table * @md: descriptor for the bad block table mirror * @chipsel: selector for a specific chip, -1 for all * * (Re)write the bad block table * THT: 1/16/07: TO DO: Currently, if writing to the block failed, we punt. * TBD: Use skip block mechanism, and skip over real bad blocks, so we would either start at the 1MB offset from bottom * and go down, or start from the bottom and go up, skipping over bad blocks until we reach the 1MB partition reserved * for BBT. * */ static int brcmnand_write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) { struct brcmnand_chip *this = mtd->priv; struct erase_info einfo; int i, j, res, chip = 0, skip = 0, dir = 0; uint32_t bits, offs, sft, sftmsk, bbtoffs; int64_t startblock = 0ULL, numblocks, page = 0ULL, i64; int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; loff_t to; struct mtd_oob_ops ops; int save_gdebug = gdebug; //gdebug=4; DEBUG(MTD_DEBUG_LEVEL3, "-->%s\n", __FUNCTION__); PRINTK("-->%s, chipsel=%d\n", __FUNCTION__, chipsel); ops.ooblen = mtd->oobsize; ops.ooboffs = 0; ops.datbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; if (!rcode) rcode = 0xff; /* Write bad block table per chip rather than per device ? */ if (td->options & NAND_BBT_PERCHIP) { numblocks = (this->chipSize >> this->bbt_erase_shift); /* Full device write or specific chip ? */ if (chipsel == -1) { nrchips = this->ctrl->numchips; } else { nrchips = chipsel + 1; chip = chipsel; } } else { numblocks = (this->mtdSize >> this->bbt_erase_shift); nrchips = 1; } PRINTK("%s Creating %c%c%c%c numblocks=%d, nrchips=%d, td->pages[0]=%llx\n", __FUNCTION__, td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3], (int)numblocks, nrchips, td->pages[0]); /* Loop through the chips */ for (; chip < nrchips; chip++) { /* There was already a version of the table, reuse the page * This applies for absolute placement too, as we have the * page nr. in td->pages. */ if (td->pages[chip] != BBT_NULL_PAGE) { page = td->pages[chip]; PRINTK("There is already a version of the table, go ahead and write it\n"); goto write; } /* Automatic placement of the bad block table */ /* Search direction top -> down ? */ if (td->options & NAND_BBT_LASTBLOCK) { startblock = numblocks * (chip + 1) - 1; dir = -1; } else { startblock = chip * numblocks; dir = 1; } skip = 0; write_retry: PRINTK("%s: write_retry: startblock=%0llx, dir=%d, td->maxblocks=%d, skip=%d\n", __FUNCTION__, startblock, dir, td->maxblocks, skip); for (i = skip; i < td->maxblocks; i++) { uint64_t block = startblock + (int64_t)(dir * i); // THT One byte contains 4 set of 2-bits, so divide block by 4 to index the BBT byte uint32_t blockindex = (uint32_t)(block >> 2); /* Check, if the block is bad */ PRINTK("%s: Checking BBT: i=%d, block=%0llx, BBT=%08x\n", __FUNCTION__, i, block, this->bbt[blockindex]); // THT: bbt[blockindex] is the byte we are looking for, now get the 2 bits that // is the BBT for the block (Shift (0,1,2,3) *2 positions depending on the block modulo 4) switch ((this->bbt[blockindex] >> (2 * (block & 0x03))) & 0x03) { case 0x01: case 0x03: continue; } page = block << (this->bbt_erase_shift - this->page_shift); PRINTK("%s: Checking BBT2: page=%llx, md->pages[chip]=%llx\n", __FUNCTION__, page, md->pages[chip]); /* Check, if the block is used by the mirror table */ if (!md || md->pages[chip] != page) goto write; } printk(KERN_ERR "No space left to write bad block table %c%c%c%c\n", td->pattern[0], td->pattern[1], td->pattern[2], td->pattern[3]); brcmnand_post_mortem_dump(mtd, page << this->page_shift); return -ENOSPC; write: /* Set up shift count and masks for the flash table */ bits = td->options & NAND_BBT_NRBITS_MSK; PRINTK("%s: bits=%d\n", __FUNCTION__, bits); msk[2] = ~rcode; switch (bits) { case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[3] = 0x01; break; case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[3] = 0x03; break; case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[3] = 0x0f; break; case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[3] = 0xff; break; default: return -EINVAL; } bbtoffs = chip * ((uint32_t)(numblocks >> 2)); to = (uint64_t)page << this->page_shift; /* Must we save the block contents ? */ if (td->options & NAND_BBT_SAVECONTENT) { /* Make it block aligned */ PRINTK("%s: NAND_BBT_SAVECONTENT\n", __FUNCTION__); //to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); to = to & ( ~((1 << this->bbt_erase_shift) - 1)); len = 1 << this->bbt_erase_shift; res = mtd_read(mtd, to, len, &retlen, buf); if (res < 0) { if (retlen != len) { printk(KERN_INFO "nand_bbt: Error " "reading block for writing " "the bad block table\n"); return res; } printk(KERN_WARNING "nand_bbt: ECC error " "while reading block for writing " "bad block table\n"); } /* Read oob data */ ops.len = (len >> this->page_shift) * mtd->oobsize; ops.oobbuf = &buf[len]; res = mtd_read_oob(mtd, to + mtd->writesize, &ops); if (res < 0 || ops.retlen != ops.len) goto outerr; /* Calc the byte offset in the buffer */ pageoffs = page - (to >> this->page_shift); // offs is offset from start of buffer, so it is OK to be 32bit. offs = pageoffs << this->page_shift; /* Preset the bbt area with 0xff */ memset(&buf[offs], 0xff, (size_t)(numblocks >> sft)); ooboffs = len + (pageoffs * mtd->oobsize); } else { PRINTK("%s: Not NAND_BBT_SAVECONTENT\n", __FUNCTION__); /* Calc length */ len = (size_t)(numblocks >> sft); /* Make it page aligned ! */ len = (len + (mtd->writesize - 1)) & ~(mtd->writesize - 1); /* Preset the buffer with 0xff */ memset(buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); offs = 0; ooboffs = len; /* Auto-place for BCH-8 on 16B OOB? */ if (td->options & BRCMNAND_BBT_AUTO_PLACE) { u_char abuf[8]; struct mtd_oob_ops ops; memcpy(abuf, td->pattern, td->len); // Write the version number (1 byte) if (td->options & NAND_BBT_VERSION) { abuf[td->veroffs] = td->version[0]; } ops.datbuf = NULL; ops.len = 0; ops.mode = MTD_OPS_AUTO_OOB; ops.ooboffs = 0; ops.ooblen = td->len + 1; /* 5 bytes */ ops.oobbuf = abuf; /* Source oobbuf */ this->oob_poi = &buf[ooboffs]; /* Destination oobbuf */ /* Copy abuf into OOB free bytes */ (void)brcmnand_fill_oob(this, abuf, &ops); }else { /* IN-PLACE OOB format */ /* Pattern is located in oob area of first page */ memcpy(&buf[ooboffs + td->offs], td->pattern, td->len); // Write the version number (1 byte) if (td->options & NAND_BBT_VERSION) { buf[ooboffs + td->veroffs] = td->version[0]; } } } /* walk through the memory table */ /* * THT: Right now we are safe, but when numblocks exceed 32bit, * then we need to look at these codes again, * as we may need to break the BBT into 2 or more tables that a uint32_t can index. */ for (i64 = 0ULL; i64 < numblocks; ) { uint8_t dat; uint32_t irs2 = (uint32_t)(i64 >> 2); // Ihdex into BBT /* * Make sure that the cast above for b64i is not lossy */ if (mtd64_ll_high(i64 >> 2)) { printk(KERN_ERR "FIXME: %s: integer index to BBT overflow %0llx\n", __FUNCTION__, i64 >> 2); } dat = this->bbt[bbtoffs + irs2]; for (j = 0; j < 4; j++, i64++) { uint32_t sftcnt = (uint32_t)((i64 << (3 - sft)) & sftmsk); /* Do not store the reserved bbt blocks ! */ buf[offs + (uint32_t)(i64 >> sft)] &= ~(msk[dat & 0x03] << sftcnt); dat >>= 2; } } memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; einfo.addr = to; einfo.len = 1ULL << this->bbt_erase_shift; res = this->erase_bbt(mtd, &einfo, 1, 1); // Do not look up BBT if (res < 0) { printk(KERN_ERR "brcmnand_bbt: Error during block erase at %0llx: %d\n", to, res); skip++; goto write_retry; } //gdebug = 4; res = brcmnand_scan_write_bbt(mtd, to, len, buf, &buf[len]); //gdebug = 0; if (res < 0) { // THT: If writing reports a bad block, we will skip it, and retry. Eventually may // run out of td->maxblocks printk(KERN_INFO "write_bbt returns flash status error at %0llx, skipping and retrying...\n", to); skip++; goto write_retry; } printk(KERN_DEBUG "Bad block table written to 0x%08x, version " "0x%02X\n", (unsigned int)to, td->version[chip]); /* Mark it as used */ td->pages[chip] = page; } gdebug = save_gdebug; return 0; outerr: gdebug = save_gdebug; printk(KERN_WARNING "brcmnand_bbt: Error while writing bad block table %d\n", res); return res; } /** * brcmnand_memory_bbt - [GENERIC] create a memory based bad block table * @mtd: MTD device structure * @bd: descriptor for the good/bad block search pattern * * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ static inline int brcmnand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct brcmnand_chip *this = mtd->priv; bd->options &= ~NAND_BBT_SCANEMPTY; return brcmnand_create_bbt(mtd, this->ctrl->buffers->databuf, bd, -1); } /** * brcmnand_check_create - [GENERIC] create and write bbt(s) if necessary * @mtd: MTD device structure * @buf: temporary buffer * @bd: descriptor for the good/bad block search pattern * * The function checks the results of the previous call to brcmnand_read_bbt * and creates / updates the bbt(s) if necessary * Creation is necessary if no bbt was found for the chip/device * Update is necessary if one of the tables is missing or the * version nr. of one table is less than the other */ static int brcmnand_check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) { int i, chips, writeops, chipsel, res; struct brcmnand_chip *this = mtd->priv; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; struct nand_bbt_descr *rd, *rd2; PRINTK("-->%s, td=%p, md=%p\n", __FUNCTION__, td, md); /* Do we have a bbt per chip ? */ if (td->options & NAND_BBT_PERCHIP) chips = this->ctrl->numchips; else chips = 1; for (i = 0; i < chips; i++) { writeops = 0; rd = NULL; rd2 = NULL; /* Per chip or per device ? */ chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; /* * THT: Reset version to 0 if 0xff */ if ((td->options & NAND_BBT_VERSION) && (td->version[i] == 0xff) && td->pages[i] != BBT_NULL_PAGE) td->version[i] = 0; if ((md->options & NAND_BBT_VERSION) && (md->version[i] == 0xff) && md->pages[i] != BBT_NULL_PAGE) md->version[i] = 0; /* Mirrored table available ? */ if (md) { if (td->pages[i] == BBT_NULL_PAGE && md->pages[i] == BBT_NULL_PAGE) { writeops = 0x03; goto create; } if (td->pages[i] == BBT_NULL_PAGE) { rd = md; td->version[i] = md->version[i]; writeops = 1; goto writecheck; } if (md->pages[i] == BBT_NULL_PAGE) { rd = td; md->version[i] = td->version[i]; writeops = 2; goto writecheck; } if (td->version[i] == md->version[i]) { rd = td; if (!(td->options & NAND_BBT_VERSION)) rd2 = md; goto writecheck; } if (((int8_t)(td->version[i] - md->version[i])) > 0) { rd = td; md->version[i] = td->version[i]; writeops = 2; } else { rd = md; td->version[i] = md->version[i]; writeops = 1; } goto writecheck; } else { if (td->pages[i] == BBT_NULL_PAGE) { writeops = 0x01; goto create; } rd = td; goto writecheck; } create: /* Create the bad block table by scanning the device ? */ if (!(td->options & NAND_BBT_CREATE)) continue; /* Create the table in memory by scanning the chip(s) */ brcmnand_create_bbt(mtd, buf, bd, chipsel); td->version[i] = 1; if (md) md->version[i] = 1; writecheck: res = 0; PRINTK("%s: writeops=%d, rd=%p, rd2=%p\n", __FUNCTION__, writeops, rd, rd2); /* read back first ? */ if (rd) { PRINTK("%s: Read rd\n", __FUNCTION__); res = brcmnand_read_abs_bbt(mtd, buf, rd, chipsel); } /* If they weren't versioned, read both. */ if (rd2) { if (res != 0) { int bbtlen = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); /* Clear the in-memory BBT first */ PRINTK("%s: Discarding previously read BBT %c%c%c%c, res=%d\n", __FUNCTION__, rd->pattern[0], rd->pattern[1], rd->pattern[2], rd->pattern[3], res); memset(this->bbt, 0, bbtlen); } PRINTK("%s: Read rd2\n", __FUNCTION__); res = brcmnand_read_abs_bbt(mtd, buf, rd2, chipsel); if (res != 0) { PRINTK("%s: Read BBT %c%c%c%c returns res=%d, discarding\n", __FUNCTION__, rd2->pattern[0], rd2->pattern[1], rd2->pattern[2], rd2->pattern[3], res); } } /* Write the bad block table to the device ? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { res = brcmnand_write_bbt(mtd, buf, td, md, chipsel); if (res < 0) return res; } /* Write the mirror bad block table to the device ? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { res = brcmnand_write_bbt(mtd, buf, md, td, chipsel); if (res < 0) return res; } } return 0; } /** * mark_bbt_regions - [GENERIC] mark the bad block table regions * @mtd: MTD device structure * @td: bad block table descriptor * * The bad block table regions are marked as "bad" to prevent * accidental erasures / writes. The regions are identified by * the mark 0x02. */ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct brcmnand_chip *this = mtd->priv; int i, j, update; uint32_t chips, block, nrblocks; uint8_t oldval, newval; /* Do we have a bbt per chip ? */ if (td->options & NAND_BBT_PERCHIP) { chips = this->ctrl->numchips; nrblocks = (int)(this->chipSize >> this->bbt_erase_shift); } else { chips = 1; nrblocks = (uint32_t)(this->mtdSize >> this->bbt_erase_shift); } for (i = 0; i < chips; i++) { if ((td->options & NAND_BBT_ABSPAGE) || !(td->options & NAND_BBT_WRITE)) { if (td->pages[i] == BBT_NULL_PAGE) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); block <<= 1; oldval = this->bbt[(block >> 3)]; newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; if ((oldval != newval) && td->reserved_block_code) brcmnand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); continue; } update = 0; if (td->options & NAND_BBT_LASTBLOCK) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; block <<= 1; for (j = 0; j < td->maxblocks; j++) { oldval = this->bbt[(block >> 3)]; newval = oldval | (0x2 << (block & 0x06)); this->bbt[(block >> 3)] = newval; if (oldval != newval) update = 1; block += 2; } /* If we want reserved blocks to be recorded to flash, and some new ones have been marked, then we need to update the stored bbts. This should only happen once. */ if (update && td->reserved_block_code) brcmnand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); } } /** * brcmnand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) * @mtd: MTD device structure * @bd: descriptor for the good/bad block search pattern * * The function checks, if a bad block table(s) is/are already * available. If not it scans the device for manufacturer * marked good / bad blocks and writes the bad block table(s) to * the selected place. * * The bad block table memory is allocated here. It must be freed * by calling the nand_free_bbt function. * */ int brcmnand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct brcmnand_chip *this = mtd->priv; int res = 0; uint32_t len; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; PRINTK("-->%s: chip=%p, td->options=%08x, md->options=%08x\n", __FUNCTION__, this, td->options, md->options); len = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); /* Allocate memory (2bit per block) */ PRINTK("brcmnand_scan_bbt: Allocating %d byte for BBT. mtd->size=%lld, eraseshift=%d\n", len, this->mtdSize, this->bbt_erase_shift); this->bbt = (uint8_t*)kmalloc(len, GFP_KERNEL); if (!this->bbt) { printk(KERN_ERR "brcmnand_scan_bbt: Out of memory, bbt_erase_shift=%d, len=%d\n", this->bbt_erase_shift, len); return -ENOMEM; } /* Clear the memory bad block table */ memset(this->bbt, 0x00, len); /* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ if (!td) { if ((res = brcmnand_memory_bbt(mtd, bd))) { printk(KERN_ERR "brcmnand_bbt: Can't scan flash and build the RAM-based BBT\n"); kfree(this->bbt); this->bbt = NULL; } return res; } /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); PRINTK("%s: len before OOB = %08x\n", __FUNCTION__, len); len += (len >> this->page_shift) * (mtd->oobsize); PRINTK("%s: Inc OOB - Allocating %08x byte buffer, oobsize=%d\n", __FUNCTION__, len, mtd->oobsize); buf = kmalloc(len, GFP_KERNEL); if (!buf) { printk(KERN_ERR "%s: Out of memory 2, bbt_erase_shift=%d, len=%dx\n", __FUNCTION__, this->bbt_erase_shift, len ); kfree(this->bbt); this->bbt = NULL; return -ENOMEM; } /* Is the bbt at a given page ? */ if (td->options & NAND_BBT_ABSPAGE) { res = brcmnand_read_abs_bbts(mtd, buf, td, md); } else { /* Search the bad block table using a pattern in oob */ res = brcmnand_search_read_bbts(mtd, buf, td, md); } if (res) { res = brcmnand_check_create(mtd, buf, bd); } /* Prevent the bbt regions from erasing / writing */ mark_bbt_region(mtd, td); if (md) mark_bbt_region(mtd, md); kfree(buf); return res; } EXPORT_SYMBOL(brcmnand_scan_bbt); /** * brcmnand_update_bbt - [NAND Interface] update bad block table(s) * @mtd: MTD device structure * @offs: the offset of the newly marked block * * The function updates the bad block table(s) */ int brcmnand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct brcmnand_chip *this = mtd->priv; int len, res = 0, writeops = 0; int chip, chipsel; uint8_t *buf; struct nand_bbt_descr *td = this->bbt_td; struct nand_bbt_descr *md = this->bbt_md; DEBUG(MTD_DEBUG_LEVEL3, "-->%s offs=%0llx\n", __FUNCTION__, offs); PRINTK("-->%s offs=%0llx\n", __FUNCTION__, offs); if (!this->bbt || !td) return -EINVAL; len = (uint32_t)(this->mtdSize >> (this->bbt_erase_shift + 2)); /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); len += (len >> this->page_shift) * mtd->oobsize; buf = kmalloc(len, GFP_ATOMIC); if (!buf) { printk(KERN_ERR "brcmnand_update_bbt: Out of memory\n"); return -ENOMEM; } writeops = md != NULL ? 0x03 : 0x01; /* Do we have a bbt per chip ? */ if (td->options & NAND_BBT_PERCHIP) { chip = (int)(offs >> this->chip_shift); chipsel = chip; } else { chip = 0; chipsel = -1; } (td->version[chip])++; // THT Roll over if (td->version[chip] == 0xff) td->version[chip] = 1; if (md) (md->version[chip])++; if (md->version[chip] == 0xff) md->version[chip] = 1; /* Write the bad block table to the device ? */ if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { res = brcmnand_write_bbt(mtd, buf, td, md, chipsel); if (res < 0) goto out; } /* Write the mirror bad block table to the device ? */ if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { res = brcmnand_write_bbt(mtd, buf, md, td, chipsel); } out: kfree(buf); return res; } /* Define some generic bad / good block scan pattern which are used * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr smallpage_memorybased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern }; static struct nand_bbt_descr largepage_memorybased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 0, .len = 2, .pattern = scan_ff_pattern }; /* static struct nand_bbt_descr mlc_4kpage_memorybased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 0, .len = 1, .pattern = scan_ff_pattern }; */ static struct nand_bbt_descr smallpage_flashbased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern }; static struct nand_bbt_descr largepage_flashbased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 0, .len = 2, .pattern = scan_ff_pattern }; /* 2K & 4K page MLC NAND use same pattern */ static struct nand_bbt_descr bch4_flashbased = { .options = NAND_BBT_SCAN2NDPAGE, .offs = 0, .len = 1, .pattern = scan_ff_pattern }; #if 0 static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; static struct nand_bbt_descr agand_flashbased = { .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, .offs = 0x20, .len = 6, .pattern = scan_agand_pattern }; #endif /* Generic flash bbt decriptors */ static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; /* * THT: We only have 1 chip per device */ static struct nand_bbt_descr bbt_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE #if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 | NAND_BBT_PERCHIP #endif | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 9, /* THT: Changed from 8 */ .len = 4, .veroffs = 13, /* THT: Changed from 12 */ .maxblocks = 4, /* THT: Will update later, based on 1MB partition reserved for BBT */ .pattern = bbt_pattern }; static struct nand_bbt_descr bbt_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE #if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 | NAND_BBT_PERCHIP #endif | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 9, /* THT: Changed from 8 */ .len = 4, .veroffs = 13, /* THT: Changed from 12 */ .maxblocks = 4, .pattern = mirror_pattern }; /* SLC flash using BCH-4 ECC, SM & Large page use same descriptor template */ static struct nand_bbt_descr bbt_slc_bch4_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE #if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 | NAND_BBT_PERCHIP #endif | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 1, /* THT: Changed from 8 */ .len = 4, .veroffs = 6, /* THT: Changed from 12 */ .maxblocks = 8, .pattern = bbt_pattern }; static struct nand_bbt_descr bbt_slc_bch4_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE #if 0 //CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3 | NAND_BBT_PERCHIP #endif | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 1, .len = 4, .veroffs = 6, .maxblocks = 8, .pattern = mirror_pattern }; /* Also used for bch-8 & bch-12 with 27B OOB */ static struct nand_bbt_descr bbt_bch4_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 1, .len = 4, .veroffs = 5, /* THT: Changed from 12 */ .maxblocks = 8, /* THT: Will update later, based on 4MB partition reserved for BBT */ .pattern = bbt_pattern }; static struct nand_bbt_descr bbt_bch4_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION, .offs = 1, /* THT: Changed from 8 */ .len = 4, .veroffs = 5, /* THT: Changed from 12 */ .maxblocks = 8, .pattern = mirror_pattern }; /* BCH-8 with only 16B OOB, uses auto-place for the (small) OOB */ static struct nand_bbt_descr bbt_bch8_16_main_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | BRCMNAND_BBT_AUTO_PLACE, .offs = 0, /* Signature is at offset 0 in auto-place format */ .len = 4, .veroffs = 4, /* Version just follows the signature in auto-place format */ .maxblocks = 8, /* THT: Will update later, based on 4MB partition reserved for BBT */ .pattern = bbt_pattern }; static struct nand_bbt_descr bbt_bch8_16_mirror_descr = { .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_2BIT | NAND_BBT_VERSION | BRCMNAND_BBT_AUTO_PLACE, .offs = 0, .len = 4, .veroffs = 4, .maxblocks = 8, .pattern = mirror_pattern }; static int brcmnand_displayBBT(struct mtd_info* mtd) { struct brcmnand_chip *this = mtd->priv; loff_t bOffset, bOffsetStart, bOffsetEnd; int res; int bbtSize = brcmnand_get_bbt_size(mtd); bOffsetStart = 0; bOffsetEnd = (loff_t)(this->mtdSize - bbtSize); // Skip BBT itself printk(KERN_INFO "----- Contents of BBT -----\n"); for (bOffset = bOffsetStart; bOffset < bOffsetEnd; bOffset = bOffset + mtd->erasesize) { res = this->isbad_bbt(mtd, bOffset, 1); if (res) { printk(KERN_INFO "Bad block at %0llx\n", bOffset); } } printk(KERN_INFO "----- END Contents of BBT -----\n"); return 0; } #if 1 // Remove this block in production builds. /* * Process brcmnand= kernel command arg, BEFORE building/reading BBT table. * Currently, the only accepted command is CLEARBBT, which in itself is a dangerous activity. * The user is assumed to know what he is doing. */ static void brcmnand_preprocessKernelArg(struct mtd_info *mtd) { struct brcmnand_chip *this = mtd->priv; int ret, needBBT; uint64_t bOffset, bOffsetStart = 0, bOffsetEnd = 0; int bbtSize = brcmnand_get_bbt_size(mtd); //int page; PRINTK("%s: gClearBBT=%d, size=%016llx, erasesize=%08x\n", __FUNCTION__, gClearBBT, device_size(mtd), mtd->erasesize); switch (gClearBBT) { case NANDCMD_CLEARBBT: // Force rescan of BBT (DANGEROUS, may lose Mfg's BIs). bOffsetStart = this->mtdSize - bbtSize; bOffsetEnd = this->mtdSize - mtd->erasesize; printk("%s: gClearBBT=clearbbt, start=%0llx, end=%0llx\n", __FUNCTION__, bOffsetStart, bOffsetEnd); break; case NANDCMD_SHOWBBT: return; case NANDCMD_RESCAN: return; case NANDCMD_ERASEALL: return; case NANDCMD_ERASE: return; default: BUG_ON("Invalid brcmnand flag"); break; } // switch printk("Erasing flash from %016llx to %016llx\n", bOffsetStart, bOffsetEnd); /* * Clear ECC registers */ this->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0); this->ctrl_write(BCHP_NAND_ECC_UNC_ADDR, 0); for (bOffset = bOffsetStart; bOffset <= bOffsetEnd; bOffset += mtd->erasesize) { //unsigned long pAddr = this->pbase + bOffset; //int i; //int skipBadBlock = 0; /* * Skip erasing bad blocks. If you accidentally/intentionally mark a block as bad, * and want to clear it, use BBS to clear it * The exception are the blocks in the BBT area, which are reserved * Here during pre-processing, there is no BBT, so we cannot assume its existence. */ PRINTK("brcmnand flag=%d: Erasing block at %0llx\n", gClearBBT, bOffset); this->ctrl_writeAddr(this, bOffset, 0); this->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); // Wait until flash is ready ret = this->write_is_complete(mtd, &needBBT); if (needBBT) { printk(KERN_WARNING "%s: Erase failure, marking bad block @%016llx\n", __FUNCTION__, bOffset); ret = this->block_markbad(mtd, bOffset); } } return; } #else #define brcmnand_preprocessKernelArg(mtd) #endif /* * Process brcmnand= kernel command arg, AFTER building/reading BBT table */ static void brcmnand_postprocessKernelArg(struct mtd_info *mtd) { struct brcmnand_chip *this = mtd->priv; int ret = 0, needBBT; //uint64_t bOffset, bOffsetStart=0, bOffsetEnd=0; uint64_t bOffset, bOffsetStart = 0, bOffsetEnd = 0; int bbtSize = brcmnand_get_bbt_size(mtd); PRINTK("%s: gClearBBT=%d, size=%016llx, erasesize=%08x\n", __FUNCTION__, gClearBBT, device_size(mtd), mtd->erasesize); switch (gClearBBT) { case NANDCMD_SHOWBBT: brcmnand_displayBBT(mtd); return; case NANDCMD_CLEARBBT: // already done during pre-processing brcmnand_displayBBT(mtd); return; case NANDCMD_RESCAN: printk("rescanning .... \n"); /* FALLTHROUGH */ case NANDCMD_ERASEALL: /* FALLTHROUGH */ case NANDCMD_ERASE: // Force erase of entire flash (except BBT), and rescan of BBT: bOffsetStart = 0LL; bOffsetEnd = this->mtdSize - bbtSize; // BBT partition is 1MB //printk("%s: gClearBBT=erase|eraseall, start=%08x, end=%08x\n", __FUNCTION__, bOffsetStart, bOffsetEnd); break; default: BUG_ON("Invalid clear brcmnand flag"); break; } // switch // printk("Erasing flash from %08x to %08x\n", bOffsetStart, bOffsetEnd); for (bOffset = bOffsetStart; bOffset < bOffsetEnd; bOffset = bOffset + mtd->erasesize) { #if CONFIG_MTD_BRCMNAND_VERSION < CONFIG_MTD_BRCMNAND_VERS_1_0 unsigned long pAddr = this->pbase + bOffset; #else uint64_t pAddr = bOffset + this->pbase; #endif int i; int isBadBlock = 0; int inCFE = 0; int cs = this->ctrl->CS[this->csi]; if (0 == cs) { if (this->xor_disable) { inCFE = pAddr < 0x00300000; }else { // XOR enabled inCFE = (pAddr >= 0x1fc00000 && pAddr < 0x1ff00000); } } /* Skip reserved area, 7MB starting at 0x1f80_0000. * Reserved area is owned by the bootloader. Linux owns the rootfs and the BBT area */ if (gClearBBT == NANDCMD_ERASE && inCFE) continue; /* Already included in BBT? */ if (mtd_block_isbad(mtd, bOffset)) { isBadBlock = 1; continue; } /* * Finding bad blocks besides the ones already in the BBT. * If you accidentally/intentionally mark a block as bad, * and want to clear it, use BBS to clear it, as Linux does not offer a way to do it. * The exception are the blocks in the BBT area, which are reserved */ else { unsigned char oobbuf[NAND_MAX_OOBSIZE]; //int autoplace = 0; //int raw = 1; //struct nand_oobinfo oobsel; int numpages; /* THT: This __can__ be a 36bit integer (NAND controller address space is 48bit wide, minus * page size of 2*12, therefore 36bit max */ uint64_t blockPage = bOffset >> this->page_shift; int dir; uint64_t page; /* How many pages should we scan */ if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) { if (this->options & NAND_SCAN_BI_3RD_PAGE) { numpages = 3; }else { numpages = 2; } } else { numpages = 1; } if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page dir = 1; page = blockPage; // first page of block }else { // MLC: Read last page int pagesPerBlock = mtd->erasesize / mtd->writesize; dir = -1; page = blockPage + pagesPerBlock - 1; // last page of block } for (i = 0; i < numpages; i++, page += i * dir) { int res; struct mtd_oob_ops ops; uint64_t offs = page << this->page_shift; ops.len = mtd->oobsize; ops.ooblen = mtd->oobsize; ops.oobbuf = oobbuf; ops.ooboffs = 0; ops.datbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; res = mtd_read_oob(mtd, offs, &ops); if (gdebug && res != 0) printk("########## %s: read_oob returns %d\n", __FUNCTION__, res); if (res == -EBADMSG || res == -EIO || res == -ETIMEDOUT) { // Uncorrectable errors uint32_t acc0; // Disable ECC acc0 = brcmnand_disable_read_ecc(this->ctrl->CS[this->csi]); // Re-read the OOB res = mtd_read_oob(mtd, offs, &ops); // Enable ECC back brcmnand_restore_ecc(this->ctrl->CS[this->csi], acc0); // res should be zero here } if (!res) { if (check_short_pattern(oobbuf, this->badblock_pattern)) { isBadBlock = 1; if (NANDCMD_RESCAN == gClearBBT) printk(KERN_INFO "Found bad block at offset %0llx\n", offs); PRINTK("Found bad block at offset %0llx\n", offs); break; } }else { // We don't want to mark a block as bad otherwise printk(KERN_DEBUG "brcmnand_read_pageoob returns %d for page %0llx\n", res, page); } } } switch (gClearBBT) { case NANDCMD_ERASE: /* FALLTHROUGH */ //gdebug=4; case NANDCMD_ERASEALL: if (isBadBlock) { printk(KERN_INFO "Skipping Bad Block at %0llx\n", bOffset); continue; } //printk("brcmnand flag=%d: Erasing block at %08x\n", gClearBBT, bOffset); this->ctrl_writeAddr(this, bOffset, 0); this->ctrl_write(BCHP_NAND_CMD_START, OP_BLOCK_ERASE); // Wait until flash is ready ret = this->write_is_complete(mtd, &needBBT); if (needBBT) { printk(KERN_INFO "%s: Marking bad block @%0llx\n", __FUNCTION__, bOffset); ret = this->block_markbad(mtd, bOffset); } break; case NANDCMD_RESCAN: if (isBadBlock) { printk(KERN_INFO "%s: Marking bad block @%0llx\n", __FUNCTION__, bOffset); ret = this->block_markbad(mtd, bOffset); } break; default: printk(KERN_INFO "Invalid brcmnand argument in %s\n", __FUNCTION__); BUG(); } } brcmnand_displayBBT(mtd); return; } /** * brcmnand_isbad_bbt - [NAND Interface] Check if a block is bad * @mtd: MTD device structure * @offs: offset in the device * @allowbbt: allow access to bad block table region * * Each byte in the BBT contains 4 entries, 2 bits each per block. * So the entry for the block b is: * bbt[b >> 2] & (0x3 << ((b & 0x3) << 1))) * */ static int brcmnand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct brcmnand_chip *this = mtd->priv; uint32_t block; // Used as an index, so 32bit. uint8_t res; //printk( "--> %s: bbt info for offs 0x%08x: \n", __FUNCTION__, __ll_low(offs)); /* * THT 03/20/07: * Get block number * 2. It's more convenient to do it in the following way * but is actually the same thing as in the comment above */ block = (uint32_t)(offs >> (this->bbt_erase_shift - 1)); res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; DEBUG(MTD_DEBUG_LEVEL3, "brcmnand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", (unsigned int)offs, block >> 1, res); //if (res) PRINTK("%s: res=%x, allowbbt=%d at block %08x\n", __FUNCTION__, res, allowbbt, (unsigned int) offs); switch ((int)res) { case 0x00: // Good block //printk("<-- %s\n", __FUNCTION__); return 0; case 0x01: // Marked bad due to wear return 1; case 0x02: // Reserved blocks return allowbbt ? 0 : 1; case 0x03: return 1; // Factory marked bad } return 1; } /** * brcmnand_isbad_raw - [NAND Interface] Check if a block is bad in the absence of BBT * @mtd: MTD device structure * @offs: offset in the device * * Each byte in the BBT contains 4 entries, 2 bits each per block. * So the entry for the block b is: * bbt[b >> 2] & (0x3 << ((b & 0x3) << 1))) * */ int brcmnand_isbad_raw(struct mtd_info *mtd, loff_t offs) { struct brcmnand_chip *this = mtd->priv; //uint32_t block; // Used as an index, so 32bit. uint8_t isBadBlock = 0; int i; unsigned char oobbuf[NAND_MAX_OOBSIZE]; int numpages; /* THT: This __can__ be a 36bit integer (NAND controller address space is 48bit wide, minus * page size of 2*12, therefore 36bit max */ uint64_t blockPage = offs >> this->page_shift; int dir; uint64_t page; printk("-->%s(offs=%llx\n", __FUNCTION__, offs); /* How many pages should we scan */ if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) { if (this->options & NAND_SCAN_BI_3RD_PAGE) { numpages = 3; }else { numpages = 2; } } else { numpages = 1; } PRINTK("%s: 20\n", __FUNCTION__); if (!NAND_IS_MLC(this)) { // SLC: First and 2nd page dir = 1; page = blockPage; // first page of block }else { // MLC: Read last page int pagesPerBlock = mtd->erasesize / mtd->writesize; dir = -1; page = blockPage + pagesPerBlock - 1; // last page of block } PRINTK("%s: 30\n", __FUNCTION__); for (i = 0; i < numpages; i++, page += i * dir) { int res; //int retlen = 0; PRINTK("%s: 50 calling read_page_oob=%p, offset=%llx\n", __FUNCTION__, this->read_page_oob, page << this->page_shift); //gdebug=4; res = this->read_page_oob(mtd, oobbuf, page); //gdebug = 0; if (!res) { if (check_short_pattern(oobbuf, this->badblock_pattern)) { isBadBlock = 1; break; } }else { printk(KERN_DEBUG "brcmnand_read_pageoob returns %d for page %0llx\n", res, page); } } return isBadBlock; } static struct nand_bbt_descr* brcmnand_bbt_desc_init(struct nand_bbt_descr* orig) { struct nand_bbt_descr* td; /* Potential memory leak here when used as a module, this is never freed */ td = kmalloc(sizeof(struct nand_bbt_descr), GFP_KERNEL); if (!td) { printk(KERN_ERR "%s: Cannot allocate memory for BBT descriptor\n", __FUNCTION__); return NULL; } *td = *orig; return td; } /** * brcmnand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure * * This function selects the default bad block table * support for the device and calls the brcmnand_scan_bbt function * */ int brcmnand_default_bbt(struct mtd_info *mtd) { struct brcmnand_chip *this = mtd->priv; int res; printk("-->%s\n", __FUNCTION__); /* Default for AG-AND. We must use a flash based * bad block table as the devices have factory marked * _good_ blocks. Erasing those blocks leads to loss * of the good / bad information, so we _must_ store * this information in a good / bad table during * startup */ #if 0 if (this->options & NAND_IS_AND) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_mirror_descr); } this->options |= NAND_BBT_USE_FLASH; return brcmnand_scan_bbt(mtd, &agand_flashbased); } #endif /* Is a flash based bad block table requested ? */ if (this->options & NAND_BBT_USE_FLASH) { if (this->ecclevel == BRCMNAND_ECC_HAMMING) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_mirror_descr); } if (!this->badblock_pattern) { this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; } printk("%s: bbt_td = bbt_main_descr\n", __FUNCTION__); } #if 1 /* Nowadays, both SLC and MLC can have 4KB page, and more than 16 OOB size */ else if (NAND_IS_MLC(this)) { // MLC if (this->ecclevel == BRCMNAND_ECC_BCH_8 && this->eccOobSize == 16) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch8_16_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch8_16_mirror_descr); } if (!this->badblock_pattern) { // 2K & 4K MLC NAND use same pattern this->badblock_pattern = &bch4_flashbased; } printk("%s: bbt_td = bbt_bch8_16_main_descr\n", __FUNCTION__); }else { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch4_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch4_mirror_descr); } if (!this->badblock_pattern) { // 2K & 4K MLC NAND use same pattern this->badblock_pattern = &bch4_flashbased; } } printk("%s: bbt_td = bbt_bch4_main_descr\n", __FUNCTION__); } #endif else { /* SLC flashes using BCH-4 or higher ECC */ /* Small & Large SLC NAND use the same template */ if (this->ecclevel == BRCMNAND_ECC_BCH_4) { if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_slc_bch4_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_slc_bch4_mirror_descr); } if (!this->badblock_pattern) { this->badblock_pattern = (mtd->writesize > 512) ? &bch4_flashbased : &smallpage_flashbased; } printk("%s: bbt_td = bbt_slc_bch4_main_descr\n", __FUNCTION__); } /* Special case for BCH-8 with only 16B OOB */ else if (this->ecclevel == BRCMNAND_ECC_BCH_8 && this->eccOobSize == 16) { if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_bch8_16_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_bch8_16_mirror_descr); } if (!this->badblock_pattern) { // 2K & 4K MLC NAND use same pattern this->badblock_pattern = &bch4_flashbased; } printk("%s: bbt_td = bbt_bch8_16_main_descr\n", __FUNCTION__); }else if (this->ecclevel >= BRCMNAND_ECC_BCH_8 && this->ecclevel < BRCMNAND_ECC_HAMMING && this->eccOobSize > 16) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = brcmnand_bbt_desc_init(&bbt_slc_bch4_main_descr); this->bbt_md = brcmnand_bbt_desc_init(&bbt_slc_bch4_mirror_descr); } if (!this->badblock_pattern) { this->badblock_pattern = &bch4_flashbased; } printk("%s: bbt_td = bbt_slc_bch4_main_descr\n", __FUNCTION__); } /* TBD: Use Internal ECC */ else { printk(KERN_ERR "***** %s: Unsupported ECC level %d\n", __FUNCTION__, this->ecclevel); BUG(); } } } else { /* MLC memory based not supported */ this->bbt_td = NULL; this->bbt_md = NULL; if (!this->badblock_pattern) { this->badblock_pattern = (mtd->writesize > 512) ? &largepage_memorybased : &smallpage_memorybased; } } /* * BBT partition occupies 1 MB at the end of the useable flash, so adjust maxblocks accordingly. * Only applies to flash with 512MB or less, since we don't have the extra reserved space at the * end of the flash (1FF0_0000 - 1FFF_FFFF). */ if (device_size(mtd) <= ( 512ULL << 20)) { this->bbt_td->maxblocks = this->bbt_md->maxblocks = (1 << 20) / this->blockSize; } /* * THT: 8/18/08: For MLC flashes with block size of 512KB, allocate 8 blocks or 4MB, * (this is possible because this region is outside of the CFE allocated space of 1MB at 1FF0_0000). */ else if (this->blockSize == (512 * 1024)) { this->bbt_td->maxblocks = this->bbt_md->maxblocks = max(this->bbt_td->maxblocks, (int)((4 << 20) / this->blockSize)); } /* Reserve at least 8 blocks */ else if (this->blockSize >= (1 << 20)) { this->bbt_td->maxblocks = this->bbt_md->maxblocks = max(this->bbt_td->maxblocks, 8); } this->bbtSize = this->bbt_td->maxblocks * this->blockSize; PRINTK("%s: gClearBBT = %d\n", __FUNCTION__, gClearBBT); if (gClearBBT) { (void)brcmnand_preprocessKernelArg(mtd); } res = brcmnand_scan_bbt(mtd, this->badblock_pattern); /* * Now that we have scanned the BBT, allow BBT-lookup */ this->isbad_bbt = brcmnand_isbad_bbt; if (gClearBBT) { (void)brcmnand_postprocessKernelArg(mtd); } return res; } EXPORT_SYMBOL(brcmnand_default_bbt); #endif //defined(CONFIG_BCM_KF_MTD_BCMNAND)