📄 nand_bbt.c
字号:
#endif
switch (gClearBBT) {
case NANDCMD_CLEARBBT: // Force rescan of BBT (DANGEROUS, may lose Mfg's BIs).
#ifdef MTD_LARGE
if (mtd64_is_lteq(MTD_SIZE(mtd), (512<<20))) {
bOffsetStart = mtd64_sub32(MTD_SIZE(mtd), (1<<20)); // BBT0 partition is 1MB
}
else {
bOffsetStart = mtd64_sub32(MTD_SIZE(mtd), (4<<20)); // BBT1 partition is 4MB
}
#else
bOffsetStart = mtd->size - (1<<20); // BBT partition is 1MB
#endif
bOffsetEnd = __ll_sub(this->mtdSize, mtd->erasesize);
PRINTK("%s: gClearBBT=clearbbt, start=%s, end=%s\n", __FUNCTION__,
__ll_sprintf(NandBBTMsg,bOffsetStart), __ll_sprintf(NandBBTMsg,bOffsetEnd));
break;
case NANDCMD_SHOWBBT:
return;
case NANDCMD_RESCAN:
return;
case NANDCMD_ERASEALL:
return;
case NANDCMD_ERASE:
return;
default:
BUG_ON("Invalid nand flag");
break;
} // switch
#ifdef MTD_LARGE
printk("Erasing flash from %016llx to %016llx\n", bOffsetStart, bOffsetEnd);
#else
printk("Erasing flash from %08x to %08x\n", bOffsetStart, bOffsetEnd);
#endif
/*
* 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, thre is no BBT, so we cannot assume its existence.
*/
#if 0
unsigned char oobbuf[64];
int autoplace = 0;
int raw = 1;
struct nand_oobinfo oobsel;
int numpages;
memcpy (&oobsel, this->autooob, sizeof(oobsel));
oobsel.useecc = MTD_NANDECC_PLACEONLY;
// We will erase everything, including mfg's bad blocks.
// The user is assumed to know what he is doing.
/* How many pages should we scan */
if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) {
numpages = 2;
} else {
numpages = this->blockSize/this->pageSize;
}
for (i=0; i<numpages; i++) {
int pageOffset = bOffset + i*mtd->oobblock;
int res;
int retlen = 0;
res = nand_read_pageoob(mtd, pageOffset, oobbuf, &retlen, &oobsel, autoplace, raw);
if (!res) {
if (check_short_pattern (oobbuf, this->badblock_pattern)) {
skipBadBlock = 1;
break;
}
}
else {
printk("nand_read_pageoob returns %d for page %08x\n", res, pageOffset);
}
}
if (skipBadBlock) {
printk("Skipping Bad Block at %08x\n", bOffset);
continue;
}
#endif
PRINTK("nand flag=%d: Erasing block at %s\n",
gClearBBT, __ll_sprintf(NandBBTMsg, 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) {
#ifdef MTD_LARGE
printk(KERN_WARNING "%s: Erase failure, marking bad block @%016llx\n", __FUNCTION__, bOffset);
#else
printk(KERN_WARNING "%s: Erase failure, marking bad block @%08x\n", __FUNCTION__, bOffset);
#endif
ret = this->block_markbad(mtd, bOffset);
}
}
return;
}
#else
#define nand_preprocessKernelArg(mtd)
#endif
/*
* Process nand= kernel command arg, AFTER building/reading BBT table
*/
static void nand_postprocessKernelArg(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
#ifdef MTD_LARGE
int ret, needBBT;
//uint64_t bOffset, bOffsetStart=0, bOffsetEnd=0;
UL_OFF_T bOffset, bOffsetStart = 0, bOffsetEnd = 0;
#else
int bOffset, ret, needBBT, bOffsetStart=0, bOffsetEnd=0;
#endif
//int page;
#ifdef MTD_LARGE
PRINTK("%s: gClearBBT=%d, size=%016llx, erasesize=%08x\n", __FUNCTION__, gClearBBT, MTD_SIZE(mtd), mtd->erasesize);
#else
PRINTK("%s: gClearBBT=%d, size=%08x, erasesize=%08x\n", __FUNCTION__, gClearBBT, mtd->size, mtd->erasesize);
#endif
switch (gClearBBT) {
case NANDCMD_SHOWBBT:
nand_displayBBT(mtd);
return;
case NANDCMD_CLEARBBT: // already done during pre-processing
nand_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 = __ll_isub(this->mtdSize, 1<<20); // BBT partition is 1MB
//printk("%s: gClearBBT=erase|eraseall, start=%08x, end=%08x\n", __FUNCTION__, bOffsetStart, bOffsetEnd);
break;
default:
BUG_ON("Invalid clear nand flag");
break;
} // switch
// printk("Erasing flash from %08x to %08x\n", bOffsetStart, bOffsetEnd);
for (bOffset=bOffsetStart; __ll_is_less(bOffset, bOffsetEnd);
bOffset = __ll_add32(bOffset, mtd->erasesize))
{
#if CONFIG_MTD_NAND_VERSION < CONFIG_MTD_NAND_VERS_1_0
unsigned long pAddr = this->pbase + bOffset;
#else
L_OFF_T pAddr = __ll_add32(bOffset, this->pbase);
#endif
int i;
int isBadBlock = 0;
/* Skip reserved area, 7MB starting at 0x1f80_0000.
* Reserved area is owned by the bootloader. Linux owns the rootfs and the BBT area
*/
#if CONFIG_MTD_NAND_VERSION < CONFIG_MTD_NAND_VERS_1_0
if (gClearBBT == NANDCMD_ERASE && pAddr >= 0x1fc00000 && pAddr < 0x1ff00000)
continue;
#else
if (gClearBBT == NANDCMD_ERASE &&
!__ll_is_less(pAddr, __ll_constructor(0,0x1fc00000)) &&
__ll_is_less(pAddr , __ll_constructor(0,0x1ff00000)))
continue;
#endif
/* 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[64];
//int autoplace = 0;
//int raw = 1;
//struct nand_oobinfo oobsel;
int numpages;
int blockPage = __ll_RightShift(bOffset, this->page_shift);
int dir;
int page;
/* How many pages should we scan */
if (this->badblock_pattern->options & NAND_BBT_SCAN2NDPAGE) {
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;
//int retlen = 0;
res = this->read_page_oob(mtd, oobbuf, page);
if (!res) {
if (check_short_pattern (oobbuf, this->badblock_pattern)) {
isBadBlock = 1;
if (NANDCMD_RESCAN == gClearBBT)
printk(KERN_INFO "Found bad block at offset %08x\n", __ll_low(page));
break;
}
}
else {
printk(KERN_DEBUG "nand_read_pageoob returns %d for page %08x\n", res, __ll_low(page));
}
}
}
switch (gClearBBT) {
case NANDCMD_ERASE:
/* FALLTHROUGH */
//gdebug=4;
case NANDCMD_ERASEALL:
if (isBadBlock) {
printk(KERN_INFO "Skipping Bad Block at %08x\n", __ll_low(bOffset));
continue;
}
//printk("nand 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 @%08x\n", __FUNCTION__, __ll_low(bOffset));
ret = this->block_markbad(mtd, bOffset);
}
break;
case NANDCMD_RESCAN:
if (isBadBlock) {
printk(KERN_INFO "%s: Marking bad block @%08x\n", __FUNCTION__, __ll_low(bOffset));
ret = this->block_markbad(mtd, bOffset);
}
break;
default:
printk(KERN_INFO "Invalid nand argument in %s\n", __FUNCTION__);
BUG();
}
}
nand_displayBBT(mtd);
return;
}
/**
* nand_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 nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
{
struct nand_chip *this = mtd->priv;
int block;
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 = __ll_low(__ll_RightShift(offs, (this->bbt_erase_shift - 1)));
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG (MTD_DEBUG_LEVEL3, "nand_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;
}
/**
* nand_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 nand_scan_bbt function
*
*/
int nand_default_bbt (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
int res;
/* 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 (this->options & NAND_IS_AND) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
this->options |= NAND_USE_FLASH_BBT;
return nand_scan_bbt (mtd, &agand_flashbased);
}
/* Is a flash based bad block table requested ? */
if (this->options & NAND_USE_FLASH_BBT) {
if (!NAND_IS_MLC(this)) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
}
}
else { // MLC
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_mlc_main_descr;
this->bbt_md = &bbt_mlc_mirror_descr;
}
if (!this->badblock_pattern) {
// 2K & 4K MLC NAND use same pattern
this->badblock_pattern = &mlc_flashbased;
}
}
} 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;
}
}
this->isbad_bbt = nand_isbad_bbt;
/*
* 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 (this->blockSize <= (128 << 10) || !__ll_is_greater(MTD_SIZE(mtd), 512 << 20))
this->bbt_td->maxblocks = this->bbt_md->maxblocks = (1<<20) / this->blockSize;
/*
* THT: 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 {
this->bbt_td->maxblocks = this->bbt_md->maxblocks =
max(this->bbt_td->maxblocks, (int)((4<<20) / this->blockSize));
}
PRINTK("%s: gClearBBT = %d\n", __FUNCTION__, gClearBBT);
if (gClearBBT) {
(void) nand_preprocessKernelArg(mtd);
}
res = nand_scan_bbt (mtd, this->badblock_pattern);
if (gClearBBT) {
(void) nand_postprocessKernelArg(mtd);
}
return res;
}
EXPORT_SYMBOL (nand_scan_bbt);
EXPORT_SYMBOL (nand_default_bbt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -