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

📄 nand_bbt.c

📁 This is for bad block management in nand controller
💻 C
📖 第 1 页 / 共 4 页
字号:
#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 + -