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

📄 nand_cet.c

📁 This is about correction error table in nand controller
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 NAND Correctable Error Table (cet) Support
---------------------------------------------
In case of a single bit correctable error, the block in which correctable error
occured is refreshed (i.e., read->erase->write the entire block). Following a
refresh a success value is returned by nand_read() i.e., the error is 
hidden from the file system. The Correctable Error Table (CET) keeps a history
(bit-vector) of per page correctable errors. If a correctable error happens 
on the same page twice, an error is returned to the file system.

The CET starts from the opposite end of BBT with 1-bit per page. The CET is 
initialized to all 1's. On the first correctable error the bit corresponding
to a page is reset. On an erase, all the bits of the corresponding block are
set. The CET can span across multiple blocks therefore a signature 'CET#' 
where # is the block number is kept in the OOB area of the first page of a 
CET block. Also, the total correctable error count is kept in the second
page OOB of the first CET block. 

There is an in-memory correctable error table during runtime which is flushed
to the flash every 10 mins (CET_SYNC_FREQ). 

 */

#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/vmalloc.h>
#include <linux/workqueue.h>
#include "nand_priv.h"

#ifdef CONFIG_MTD_NAND_CORRECTABLE_ERR_HANDLING

#define PRINTK(...)
#define BBT_PARTITION	(1<<20)
#define BBT_MAX_BLKS	4
#define CET_START_BLK(x, y) __ll_low(__ll_RightShift((x), (y)->bbt_erase_shift) - (BBT_PARTITION/(y)->blockSize));

#define CET_GOOD_BLK	0x00
#define CET_BAD_WEAR 	0x01
#define CET_BBT_USE	0x02
#define CET_BAD_FACTORY	0x03

#define CET_SYNC_FREQ	(10*60*HZ)	

static char cet_pattern[] = {'C', 'E', 'T', 0};
static struct nand_cet_descr cet_descr = {
	.offs = 9,
	.len = 4,
	.pattern = cet_pattern
};
static void sync_cet(void *);
static int search_cet_blks(struct mtd_info *, struct nand_cet_descr *, char);
extern char gClearCET;

/*
 * Private: Read OOB area in RAW mode
 */
static inline int nand_cet_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs)
{
	struct mtd_oob_ops ops;

	ops.mode = MTD_OOB_RAW;
	ops.len = mtd->oobsize;
	ops.ooblen = mtd->oobsize;
	ops.datbuf = NULL;
	ops.oobbuf = buf;
	ops.ooboffs = 0;

	return mtd->read_oob(mtd, offs, &ops);
}

/* 
 * Private: Write to the OOB area only
 */
static inline int nand_cet_write_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs)
{
	struct mtd_oob_ops ops;

	ops.mode = MTD_OOB_RAW;
	ops.len = mtd->oobsize;
	ops.ooblen = mtd->oobsize;
	ops.datbuf = NULL;
	ops.oobbuf = buf;
	ops.ooboffs = 0;

	return mtd->write_oob(mtd, offs, &ops);
}

/*
 * Private: write one page of data and OOB to flash 
 */
static int nand_cet_write(struct mtd_info *mtd, loff_t offs, size_t len, 
		uint8_t *buf, uint8_t *oob) 
{
	struct mtd_oob_ops ops;
	int ret;

	ops.mode = MTD_OOB_PLACE;
	ops.ooboffs = 0;
	ops.ooblen = mtd->oobsize;
	ops.datbuf = buf;
	ops.oobbuf = oob;
	ops.len = len;
	ret = mtd->write_oob(mtd, offs, &ops);

	return ret;
}

/*
 * bitcount - MIT Hackmem count implementation which is O(1)
 * http://infolab.stanford.edu/~manku/bitcount/bitcount.html
 * Counts the number of 1s in a given unsigned int n 
 */
static inline int bitcount(uint32_t n) 
{
	uint32_t tmp;
	tmp = n - ((n >> 1) & 033333333333)
		- ((n >> 2) & 011111111111);
	return ((tmp + (tmp >> 3)) & 030707070707) % 63;
}

/* 
 * Private debug function: Print OOBs 
 */
static void cet_printpg_oob(struct mtd_info *mtd, struct nand_cet_descr *cet, int count) 
{
	uint8_t oobbuf[mtd->oobsize];
	loff_t offs;
	int i, gdebug = 0;
	struct nand_chip *this = (struct nand_chip *) mtd->priv;

	offs = __ll_LeftShift32(cet->startblk, this->bbt_erase_shift);
	if (gdebug) {
		printk(KERN_INFO "%s: %x\n", __FUNCTION__, (unsigned int) offs);
	}
	for (i = 0; i < count; i++) {
		memset(oobbuf, 0, mtd->oobsize);
		if (nand_cet_read_oob(mtd, oobbuf, offs)) {
			return;
		}
		print_oobbuf((const char *) oobbuf, mtd->oobsize);
		offs = offs + cet->sign*this->pageSize;
	}
	return;
}

/*
 * Private debug function: Prints first OOB area of all blocks <block#, page0>
 */ 
static void cet_printblk_oob(struct mtd_info *mtd, struct nand_cet_descr *cet) 
{
	uint8_t *oobbuf;
	loff_t offs;
	int i;
	struct nand_chip *this = (struct nand_chip *) mtd->priv;

	if((oobbuf = (uint8_t *) vmalloc(sizeof(uint8_t)*mtd->oobsize)) == NULL) {
		printk(KERN_ERR "nandCET: %s vmalloc failed\n", __FUNCTION__);
		return;
	}
	for (i = 0; i < this->bbt_td->maxblocks; i++) {
		memset(oobbuf, 0, mtd->oobsize);
		offs = __ll_LeftShift32(cet->startblk+((cet->sign)*i), this->bbt_erase_shift);
		if (nand_cet_read_oob(mtd, oobbuf, offs)) {
			vfree(oobbuf);
			return;
		}
		print_oobbuf((const char *) oobbuf, mtd->oobsize);
	}
	vfree(oobbuf);
	return;
}

/*
 * Private debug function: erase all blocks belonging to CET 
 * Use for testing purposes only
 */
static void cet_eraseall(struct mtd_info *mtd, struct nand_cet_descr *cet) 
{
	int i, ret;
	loff_t from;
	struct erase_info einfo;
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	int gdebug = 0;

	for (i = 0; i < cet->numblks; i++) {
		if (cet->memtbl[i].blk != -1) {
			from = __ll_LeftShift32(cet->memtbl[i].blk, this->bbt_erase_shift);
			if (unlikely(gdebug)) {
				printk(KERN_INFO "DEBUG -> Erasing blk %x\n", cet->memtbl[i].blk);
			}
			memset(&einfo, 0, sizeof(einfo));
			einfo.mtd = mtd;
			einfo.addr = __ll_low(from);
			einfo.len = mtd->erasesize;
			ret = this->erase_bbt(mtd, &einfo, 1, 1);
			if (unlikely(ret < 0)) {
				printk(KERN_ERR "nandCET: %s Error erasing block %llx\n", __FUNCTION__, einfo.addr);
			}
		}
	}

	return;
}

/*
 * Private: Check if a block is factory marked bad block
 * Derived from nand_isbad_bbt()
 * Return values: 
 * 0x00 Good block
 * 0x01 Marked bad due to wear
 * 0x02 Reserved for BBT
 * 0x03 Factory marked bad
 */
static inline int check_badblk(struct mtd_info *mtd, loff_t offs) 
{
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	int blk;
	int res;

	blk = __ll_low(__ll_RightShift(offs, this->bbt_erase_shift-1));
	res = (this->bbt[blk >> 3] >> blk & 0x06) & 0x03;

	return res;
}

/*
 * Check for CET pattern in the OOB buffer
 * return the blk number present in the CET
 */
static inline int found_cet_pattern(struct nand_chip *this, uint8_t *buf)
{
	struct nand_cet_descr *cet = this->cet;
	int i;

	for (i = 0; i < cet->len-1; i++) {
		if (buf[cet->offs + i] != cet_pattern[i]) {
			return -1;
		}
	}
	return (int) buf[cet->offs + cet->len-1];
}

/*
 * Check for BBT/Mirror BBT pattern
 * Similar to the implementation in nand_bbt.c
 */
static inline int found_bbt_pattern(uint8_t *buf, struct nand_bbt_descr *bd) 
{
	int i;

	for (i = 0; i < bd->len; i++) {
		if (buf[bd->offs+i] != bd->pattern[i]) {
			return 0;
		}
	}
	return 1;
}

/*
 * Check OOB area to test if the block is erased 
 */
static inline int cet_iserased(struct mtd_info *mtd, uint8_t *oobbuf) 
{
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	struct nand_ecclayout *oobinfo = this->ecclayout;
	int i;

	for (i = 0; i < oobinfo->eccbytes; i++) {
		if (oobbuf[oobinfo->eccpos[i]] != 0xff) {
			return 0;
		}
	}
	return 1;
}

/*
 * Process kernel command line showcet
 * If the CET is loaded, display which blocks of flash the CET is in
 */
static inline void cmdline_showcet(struct mtd_info *mtd, struct nand_cet_descr *cet)
{
	int i;
	loff_t offs;
	uint8_t oobbuf[mtd->oobsize];
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	
	if (cet->flags == NAND_CET_DISABLED) {
		printk(KERN_INFO "nandCET: Disabled\n");
		return;
	}
	printk(KERN_INFO "nandCET: Correctable error count is 0x%x\n", cet->cerr_count);
	if (cet->flags == NAND_CET_LAZY) {
		printk(KERN_INFO "nandCET: Deferred until next correctable error\n");
		return;
	}
	printk(KERN_INFO "nandCET: Displaying first OOB area of all CET blocks ...\n");
	for (i = 0; i < cet->numblks; i++) {
		if (cet->memtbl[i].blk == -1) 
			continue;
		offs = __ll_LeftShift32(cet->memtbl[i].blk, this->bbt_erase_shift);
		printk(KERN_INFO "nandCET: Block[%d] @ %x\n", i, (unsigned int) offs);
		if (nand_cet_read_oob(mtd, oobbuf, offs)) {
			return;
		}
		print_oobbuf((const char *) oobbuf, mtd->oobsize);
	}
	return;
}

/*
 * Reset CET to all 0xffs 
 */
static inline int cmdline_resetcet(struct mtd_info *mtd, struct nand_cet_descr *cet)
{
	int i;

	cet_eraseall(mtd, cet);
	for (i = 0; i < cet->numblks; i++) {
		cet->memtbl[i].isdirty = 0;
		cet->memtbl[i].blk = -1;
		cet->memtbl[i].bitvec = NULL;
	}
	printk(KERN_INFO "nandCET: Recreating ... \n");

	return search_cet_blks(mtd, cet, 0);
}

/*
 * Create a CET pattern in the OOB area. 
 */
static int create_cet_blks(struct mtd_info *mtd, struct nand_cet_descr *cet)
{
	int i, j, ret, gdebug = 0;
	loff_t from;
	struct nand_bbt_descr *td, *md;
	struct erase_info einfo;
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	uint8_t oobbuf[mtd->oobsize];
	char *oobptr, count = 0;

	td = this->bbt_td;
	md = this->bbt_md;
	if (unlikely(gdebug)) {
		printk(KERN_INFO "nandCET: Inside %s\n", __FUNCTION__);
	}
	for (i = 0; i < td->maxblocks; i++) {
		from = __ll_LeftShift32(cet->startblk+i*cet->sign, this->bbt_erase_shift);
		/* Skip if bad block */
		ret = check_badblk(mtd, from);
		if (ret == CET_BAD_FACTORY || ret == CET_BAD_WEAR) {
			continue;
		}
		memset(oobbuf, 0, mtd->oobsize);
		if (nand_cet_read_oob(mtd, oobbuf, from)) {
			printk(KERN_INFO "nandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__);
			return -1;
		}
		/* If BBT/MBT block found  we have no space left */
		if (found_bbt_pattern(oobbuf, td) || found_bbt_pattern(oobbuf, md)) {
			printk(KERN_INFO "nandCET: %s blk %x is BBT\n", __FUNCTION__, cet->startblk + i*cet->sign);
			return -1;
		}
		//if (!cet_iserased(mtd, oobbuf)) {
		if (unlikely(gdebug)) {
			printk(KERN_INFO "nandCET: block %x is erased\n", cet->startblk+i*cet->sign);
		}
		/* Erase */
		memset(&einfo, 0, sizeof(einfo));
		einfo.mtd = mtd;
		einfo.addr = __ll_low(from);
		einfo.len = mtd->erasesize;
		ret = this->erase_bbt(mtd, &einfo, 1, 1);
		if (unlikely(ret < 0)) {
			printk(KERN_ERR "nandCET: %s Error erasing block %x\n", __FUNCTION__, cet->startblk+i*cet->sign);
			return -1;
		}
		//} 
		/* Write 'CET#' pattern to the OOB area */
		memset(oobbuf, 0xff, mtd->oobsize);
		if (unlikely(gdebug)) {
			printk(KERN_INFO "nandCET: writing CET %d to OOB area\n", (int)count);
		}
		oobptr = (char *) oobbuf;
		for (j = 0; j < cet->len-1; j++) {
			oobptr[cet->offs + j] = cet->pattern[j];
		}
		oobptr[cet->offs + j] = count;
		if (nand_cet_write_oob(mtd, oobbuf, from)) {
			printk(KERN_ERR "nandCET: %s Error writing to OOB# %x\n", __FUNCTION__, (unsigned int)from);
			return -1;
		}
		/* If this is the first CET block, init the correctable erase count to 0 */
		if (count == 0) {
			memset(oobbuf, 0xff, mtd->oobsize);
			oobptr = (char *) oobbuf;
			*((uint32_t *) (oobptr + cet->offs)) = 0x00000000;
			from += this->pageSize;
			if (unlikely(gdebug)) {
				printk(KERN_INFO "DEBUG -> 0: from = %x\n", (unsigned int) from);
				printk(KERN_INFO "nandCET: Writing cer_count to page %x\n", (unsigned int) from);
			}
			if (nand_cet_write_oob(mtd, oobbuf, from)) {
				printk(KERN_INFO "nandCET: %s Error writing to OOB# %x\n", __FUNCTION__, (unsigned int)from);
				return -1;
			}
		}
		count++;
		if (((int)count) == cet->numblks) {
			return 0;
		}
	}
	return -1;
}

/*
 * Search for CET blocks
 * force => 1 Force creation of tables, do not defer for later
 */
static int search_cet_blks(struct mtd_info *mtd, struct nand_cet_descr *cet, char force)
{
	int i, count = 0, ret;
	loff_t from;
	struct nand_bbt_descr *td, *md;
	uint8_t oobbuf[mtd->oobsize];
	struct nand_chip *this = (struct nand_chip *) mtd->priv;
	int gdebug = 0;

	td = this->bbt_td;
	md = this->bbt_md;
	if (unlikely(gdebug)) {
		printk(KERN_INFO "DEBUG -> Inside search_cet_blks\n");
	}
	for (i = 0; i < td->maxblocks; i++) {
		from = __ll_LeftShift32(cet->startblk+i*cet->sign, this->bbt_erase_shift);
		/* Skip if bad block */
		ret = check_badblk(mtd, from);
		if (ret == CET_BAD_FACTORY || ret == CET_BAD_WEAR) {
			continue;
		}
		/* Read the OOB area of the first page of the block */
		memset(oobbuf, 0, mtd->oobsize);
		if (nand_cet_read_oob(mtd, oobbuf, from)) {
			printk(KERN_INFO "nandCET: %s %d Error reading OOB\n", __FUNCTION__, __LINE__);
			cet->flags = NAND_CET_DISABLED;
			return -1;
		}
		if (unlikely(gdebug)) {
			print_oobbuf(oobbuf, mtd->oobsize);
		}
		/* Return -1 if BBT/MBT block => no space left for CET */
		if (found_bbt_pattern(oobbuf, td) || found_bbt_pattern(oobbuf, md)) {
			printk(KERN_INFO "nandCET: %s blk %x is BBT\n", __FUNCTION__, cet->startblk + i*cet->sign);
			cet->flags = NAND_CET_DISABLED;
			return -1;
		}
		/* Check for CET pattern */
		ret = found_cet_pattern(this, oobbuf);
		if (unlikely(gdebug)) {
			print_oobbuf((const char *) oobbuf, mtd->oobsize);
		}
		if (ret < 0 || ret >= cet->numblks) {
			/* No CET pattern found due to
			   1. first time being booted => normal so create
			   2. Did not find CET pattern when we're supposed to
			      error => recreate, in either case we call create_cet_blks();
			   3. Found an incorrect > cet->numblks count => error => recreate
			 */
			printk(KERN_INFO "nandCET: Did not find CET, recreating\n");
			if (create_cet_blks(mtd, cet) < 0) {
				cet->flags = NAND_CET_DISABLED;
				return ret;
			}
			cet->flags = NAND_CET_LAZY;
			return 0;
		}
		/* Found CET pattern */
		if (unlikely(gdebug)) {
			printk(KERN_INFO "nandCET: Found CET block#%d\n", count);
		}
		/* If this is the first block do some extra stuff ... */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -