⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nand_bbt.c

📁 This is for bad block management in nand controller
💻 C
📖 第 1 页 / 共 4 页
字号:
/*

    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 + -