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

📄 nand_bbt.c

📁 This is for bad block management in nand controller
💻 C
📖 第 1 页 / 共 4 页
字号:
static int nand_create_bbt(struct mtd_info *mtd, uint8_t *buf,
	struct nand_bbt_descr *bd, int chip)
{
	struct nand_chip *this = mtd->priv;
	int i, len, scanlen;
	uint64_t numblocks, startblock;
	
	loff_t from;
	size_t readlen;


PRINTK("-->nand_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)
			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 */
#ifdef MTD_LARGE
		numblocks = mtd64_rshft32(MTD_SIZE(mtd), (this->bbt_erase_shift - 1));
#else
		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
#endif
		startblock = 0UL;
		from = 0LL;
	} else {
		if (chip >= this->numchips) {
			printk (KERN_WARNING "nand_create_bbt(): chipnr (%d) > available chips (%d)\n",
				chip + 1, this->numchips);
			return -EINVAL;
		}
		numblocks = this->chipSize >> (this->bbt_erase_shift - 1);
		startblock = chip * numblocks;
		numblocks += startblock;
		from = __ll_LeftShift32(startblock, (this->bbt_erase_shift - 1));
	}
if (gdebug > 3) { char msg[20];
PRINTK("Starting for loop: from=%s bd->options=%08x, startblock=%d numblocks=%d\n", 
__ll_sprintf(msg, from), bd->options, __ll_low(startblock), __ll_low(numblocks));
}
	for (i = startblock; i < numblocks;) {
		int ret;

		if (bd->options & NAND_BBT_SCANALLPAGES)
			ret = nand_scan_block_full(mtd, bd, from, buf, readlen,
					      scanlen, len);
		else
			ret = nand_scan_block_fast(mtd, bd, from, buf, len);

		if (ret < 0)
			return ret;

		if (ret) {
			this->bbt[i >> 3] |= 0x03 << (i & 0x6);
			printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
			       i >> 1, (unsigned int)from);
			mtd->ecc_stats.badblocks++;
		}

		i += 2;
		from += (1 << this->bbt_erase_shift);
	}
	return 0;
}

/**
 * nand_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 nand_search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
	struct nand_chip *this = mtd->priv;
	int i, chips;
	int bits, startblock, block, dir;
	int scanlen = mtd->writesize + mtd->oobsize;
	int bbtblocks;
	int blocktopage = this->bbt_erase_shift - this->page_shift;

	/* Search direction top -> down ? */
	if (td->options & NAND_BBT_LASTBLOCK) {
		startblock = __ll_low(__ll_RightShift(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->numchips;
		bbtblocks = this->chipSize >> this->bbt_erase_shift;
		startblock &= bbtblocks - 1;
	} else {
		chips = 1;
		bbtblocks = __ll_low(__ll_RightShift(this->mtdSize, this->bbt_erase_shift));

	}

	/* Number of bits for each erase block in the bbt */
	bits = td->options & NAND_BBT_NRBITS_MSK;

	for (i = 0; i < chips; i++) {
		/* Reset version information */
		td->version[i] = 0;
		td->pages[i] = -1;
		/* Scan the maximum number of blocks */
		for (block = 0; block < td->maxblocks; block++) {

			int actblock = startblock + dir * block;
			loff_t offs = __ll_LeftShift32(actblock, this->bbt_erase_shift);

			/* Read first page */
			nand_scan_read_raw(mtd, buf, offs, mtd->writesize);
			if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
				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] == -1)
			printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
		else
			printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
			       td->version[i]);
	}
	return 0;
}

/**
 * nand_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 nand_search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
	struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
	/* Search the primary table */
	nand_search_bbt (mtd, buf, td);

	/* Search the mirror table */
	if (md)
		nand_search_bbt (mtd, buf, md);

	/* Force result check */
	return 1;
}


/**
 * nand_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
 *  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 nand_write_bbt(struct mtd_info *mtd, uint8_t *buf,
		     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
		     int chipsel)
{
	struct nand_chip *this = mtd->priv;
	struct erase_info einfo;
	int i, j, res, chip = 0, skip;
	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
	int nrchips, bbtoffs, 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;


DEBUG(MTD_DEBUG_LEVEL3, "-->%s\n", __FUNCTION__);
	ops.ooblen = mtd->oobsize;
	ops.ooboffs = 0;
	ops.datbuf = NULL;
	ops.mode = MTD_OOB_PLACE;

	if (!rcode)
		rcode = 0xff;
	/* Write bad block table per chip rather than per device ? */
	if (td->options & NAND_BBT_PERCHIP) {
		numblocks = (int) (this->chipSize >> this->bbt_erase_shift);
		/* Full device write or specific chip ? */
		if (chipsel == -1) {
			nrchips = this->numchips;
		} else {
			nrchips = chipsel + 1;
			chip = chipsel;
		}
	} else {
		numblocks = __ll_low(__ll_RightShift(this->mtdSize, this->bbt_erase_shift));
		nrchips = 1;
	}
	
PRINTK("numblocks=%d, nrchips=%d\n", numblocks, nrchips);

	/* 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] != -1) {
			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("startblock=%08x, dir=%d, td->maxblocks=%d, skip=%d\n", startblock, dir, td->maxblocks, skip);

		for (i = skip; i < td->maxblocks; i++) {
			int block = startblock + dir * i;



			/* Check, if the block is bad */

PRINTK("%s: Checking BBT: i=%d, block=%08x, BBT=%08x\n", 
__FUNCTION__, i, block, this->bbt[block >> 2]);
			
			switch ((this->bbt[block >> 2] >>
				 (2 * (block & 0x03))) & 0x03) {
			case 0x01:
			case 0x03:
				continue;
			}
			page = block << (this->bbt_erase_shift - this->page_shift);
			/* 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\n");
		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);
		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 * (numblocks >> 2);

		to = __ll_LeftShift32(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 = __ll_and32(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 - __ll_low(__ll_RightShift(to, this->page_shift));
			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;
			/* Pattern is located in oob area of first page */
			memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
		}

		/* walk through the memory table */
		for (i = 0; i < numblocks; ) {
			uint8_t dat;
			dat = this->bbt[bbtoffs + (i >> 2)];
			for (j = 0; j < 4; j++ , i++) {
				int sftcnt = (i << (3 - sft)) & sftmsk;
				/* Do not store the reserved bbt blocks ! */
				buf[offs + (i >> sft)] &=
					~(msk[dat & 0x03] << sftcnt);
				dat >>= 2;
			}
		}
		

		memset (&einfo, 0, sizeof (einfo));
		einfo.mtd = mtd;
		einfo.addr = to;
		
		einfo.len = 1 << this->bbt_erase_shift;
		res = this->erase_bbt (mtd, &einfo, 1, 1); // Do not look up BBT
		if (res < 0) {
			printk (KERN_ERR "nand_bbt: Error during block erase: %d\n", res);
			goto outerr;
		}

//gdebug = 4;
		res = nand_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
			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;
	}
	return 0;

 outerr:
	printk(KERN_WARNING
	       "nand_bbt: Error while writing bad block table %d\n", res);
	return res;
}

/**
 * nand_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 nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
	struct nand_chip *this = mtd->priv;

	bd->options &= ~NAND_BBT_SCANEMPTY;
	return nand_create_bbt (mtd, this->buffers->databuf, bd, -1);
}

/**
 * nand_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 nand_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 nand_check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
{
	int i, chips, writeops, chipsel, res;
	struct nand_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("-->nand_check_create\n");
	/* Do we have a bbt per chip ? */

⌨️ 快捷键说明

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