📄 nand_bbt.c
字号:
/*
Description:
NAND driver for Samsung K9F1G08U0A chip with this NAND controller.
The main difference between this controller and OneNAND is that this
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 nand_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
*
*/
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/compatmac.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include "nand_priv.h"
#ifdef MTD_LARGE
#include <linux/mtd/mtd64.h>
#endif
#define PRINTK(...)
//#define PRINTK printk
extern int gClearBBT;
extern int gdebug;
char NandBBTMsg[1024];
/* nand=
* 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 nand_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\n", len, paglen, td->offs);
if (gdebug > 3) { printk("oobbuf=\n"); print_oobbuf(&buf[paglen], len-paglen); }
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;
}
/**
* nand_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 nand_read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
int bits, int offs, int reserved_block_code)
{
int res, i, j, act = 0;
struct nand_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 "nand_bbt: Error reading bad block table\n");
return res;
}
printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
}
/* 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 += len;
}
return 0;
}
/**
* nand_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 nand_read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
struct nand_chip *this = mtd->priv;
int res = 0, i;
int bits;
PRINTK("-->nand_read_abs_bbt\n");
bits = td->options & NAND_BBT_NRBITS_MSK;
if (td->options & NAND_BBT_PERCHIP) {
int offs = 0;
for (i = 0; i < this->numchips; i++) {
if (chip == -1 || chip == i)
res = nand_read_bbt (mtd, buf, td->pages[i], this->chipSize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
if (res) {
PRINTK("<-- nand_read_abs_bbt ret = %d\n", res);
return res;
}
offs += this->chipSize >> (this->bbt_erase_shift + 2);
}
} else {
res = nand_read_bbt (mtd, buf, td->pages[0],
__ll_low(__ll_RightShift(this->mtdSize, this->bbt_erase_shift)), bits, 0, td->reserved_block_code);
if (res) {
PRINTK("<-- nand_read_abs_bbt 2 ret = %d\n", res);
return res;
}
}
PRINTK("<-- nand_read_abs_bbt ret 0\n");
return 0;
}
/*
* Scan read raw data from flash
*/
static int nand_scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.oobbuf = buf;
ops.datbuf = buf;
ops.len = len;
return mtd->read_oob(mtd, offs, &ops);
}
/*
* Scan write data with oob to flash
*/
static int nand_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 nand_chip *this = mtd->priv;
int ret;
ops.mode = MTD_OOB_PLACE;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.datbuf = buf;
ops.oobbuf = oob;
ops.len = len;
PRINTK("%s: Writing BBT Sig @%s, OOB=\n", __FUNCTION__, __ll_sprintf(NandBBTMsg,offs));
if (gdebug) print_oobbuf(oob, mtd->oobsize);
ret = this->write_oob(mtd, offs, &ops);
//gdebug = 0;
return ret;
}
/**
* nand_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 nand_read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
struct nand_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");
nand_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 %d, 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");
nand_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 %d, version 0x%02X\n",
md->pages[0], md->version[0]);
}
PRINTK("<-- %s\n", __FUNCTION__);
return 1;
}
/*
* Scan a given block full
*/
static int nand_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 = nand_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 nand_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;
if (!MTD_IS_MLC(mtd)) { // 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 += (pagesPerBlock -1 ) * mtd->writesize;
}
ops.len = mtd->oobsize;
ops.ooblen = mtd->oobsize;
ops.oobbuf = buf;
ops.ooboffs = 0;
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
for (j=0; j < len; j++) {
/*
* Read the full oob until read_oob is fixed to
* handle single byte reads for 16 bit
* buswidth
*/
ret = mtd->read_oob(mtd, offs, &ops);
if (ret)
return ret;
if (check_short_pattern(buf, bd))
return 1;
offs += (dir * mtd->writesize);
}
return 0;
}
/**
* nand_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
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -