diff options
| author | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2007-11-24 21:26:56 +0100 | 
|---|---|---|
| committer | Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> | 2007-11-25 23:28:52 +0100 | 
| commit | 59829cc189378c142c13d2aa8d9a897d8bef3961 (patch) | |
| tree | 9db7b0647e82c7d3edbd33caa89a71eb4e123d0c /drivers/mtd/nand/nand_bbt.c | |
| parent | 318c0b90431f2648552e5ade78833f42652ce859 (diff) | |
| download | olio-uboot-2014.01-59829cc189378c142c13d2aa8d9a897d8bef3961.tar.xz olio-uboot-2014.01-59829cc189378c142c13d2aa8d9a897d8bef3961.zip | |
drivers/mtd : move mtd drivers to drivers/mtd
Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Diffstat (limited to 'drivers/mtd/nand/nand_bbt.c')
| -rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 1052 | 
1 files changed, 1052 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c new file mode 100644 index 000000000..19a9bc2a5 --- /dev/null +++ b/drivers/mtd/nand/nand_bbt.c @@ -0,0 +1,1052 @@ +/* + *  drivers/mtd/nand_bbt.c + * + *  Overview: + *   Bad block table support for the NAND driver + * + *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) + * + * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Description: + * + * 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 neccecary for a bbt in FLASH does not exceed a block boundary + * + */ + +#include <common.h> + +#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) + +#include <malloc.h> +#include <linux/mtd/compat.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +#include <asm/errno.h> + +/** + * 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; +	uint8_t *p = buf; + +	end = paglen + td->offs; +	if (td->options & NAND_BBT_SCANEMPTY) { +		for (i = 0; i < end; i++) { +			if (p[i] != 0xff) +				return -1; +		} +	} +	p += end; + +	/* Compare the pattern */ +	for (i = 0; i < td->len; i++) { +		if (p[i] != td->pattern[i]) +			return -1; +	} + +	p += td->len; +	end += td->len; +	if (td->options & NAND_BBT_SCANEMPTY) { +		for (i = end; i < len; i++) { +			if (*p++ != 0xff) +				return -1; +		} +	} +	return 0; +} + +/** + * 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 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; + +	while (totlen) { +		len = min (totlen, (size_t) (1 << this->bbt_erase_shift)); +		res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); +		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); +					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); +			} +		} +		totlen -= len; +		from += len; +	} +	return 0; +} + +/** + * 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 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; + +	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 = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); +			if (res) +				return res; +			offs += this->chipsize >> (this->bbt_erase_shift + 2); +		} +	} else { +		res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); +		if (res) +			return res; +	} +	return 0; +} + +/** + * 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 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; + +	/* Read the primary version, if available */ +	if (td->options & NAND_BBT_VERSION) { +		nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); +		td->version[0] = buf[mtd->oobblock + 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)) { +		nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); +		md->version[0] = buf[mtd->oobblock + md->veroffs]; +		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); +	} + +	return 1; +} + +/** + * 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 + */ +static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +{ +	struct nand_chip *this = mtd->priv; +	int i, j, numblocks, len, scanlen; +	int startblock; +	loff_t from; +	size_t readlen, ooblen; + +	if (bd->options & NAND_BBT_SCANALLPAGES) +		len = 1 << (this->bbt_erase_shift - this->page_shift); +	else { +		if (bd->options & NAND_BBT_SCAN2NDPAGE) +			len = 2; +		else +			len = 1; +	} +	scanlen	= mtd->oobblock + mtd->oobsize; +	readlen = len * mtd->oobblock; +	ooblen = len * mtd->oobsize; + +	if (chip == -1) { +		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it +		 * makes shifting and masking less painful */ +		numblocks = mtd->size >> (this->bbt_erase_shift - 1); +		startblock = 0; +		from = 0; +	} else { +		if (chip >= this->numchips) { +			printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", +				chip + 1, this->numchips); +			return; +		} +		numblocks = this->chipsize >> (this->bbt_erase_shift - 1); +		startblock = chip * numblocks; +		numblocks += startblock; +		from = startblock << (this->bbt_erase_shift - 1); +	} + +	for (i = startblock; i < numblocks;) { +		nand_read_raw (mtd, buf, from, readlen, ooblen); +		for (j = 0; j < len; j++) { +			if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { +				this->bbt[i >> 3] |= 0x03 << (i & 0x6); +				break; +			} +		} +		i += 2; +		from += (1 << this->bbt_erase_shift); +	} +} + +/** + * 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 neccecary 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 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->oobblock + mtd->oobsize; +	int bbtblocks; + +	/* Search direction top -> down ? */ +	if (td->options & NAND_BBT_LASTBLOCK) { +		startblock = (mtd->size >> 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 = mtd->size >> 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; +			/* Read first page */ +			nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); +			if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { +				td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); +				if (td->options & NAND_BBT_VERSION) { +					td->version[i] = buf[mtd->oobblock + 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; +} + +/** + * 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 search_read_bbts (struct mtd_info *mtd, uint8_t *buf, +	struct nand_bbt_descr *td, struct nand_bbt_descr *md) +{ +	/* Search the primary table */ +	search_bbt (mtd, buf, td); + +	/* Search the mirror table */ +	if (md) +		search_bbt (mtd, buf, md); + +	/* Force result check */ +	return 1; +} + + +/** + * 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 + * +*/ +static int 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 nand_oobinfo oobinfo; +	struct erase_info einfo; +	int i, j, res, chip = 0; +	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; +	int nrchips, bbtoffs, pageoffs; +	uint8_t msk[4]; +	uint8_t rcode = td->reserved_block_code; +	size_t retlen, len = 0; +	loff_t to; + +	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 = (int) (mtd->size >> this->bbt_erase_shift); +		nrchips = 1; +	} + +	/* 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]; +			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; +		} + +		for (i = 0; i < td->maxblocks; i++) { +			int block = startblock + dir * i; +			/* Check, if the block is bad */ +			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; +		switch (bits) { +		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; +		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; +		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; +		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; +		default: return -EINVAL; +		} + +		bbtoffs = chip * (numblocks >> 2); + +		to = ((loff_t) page) << this->page_shift; + +		memcpy (&oobinfo, this->autooob, sizeof(oobinfo)); +		oobinfo.useecc = MTD_NANDECC_PLACEONLY; + +		/* Must we save the block contents ? */ +		if (td->options & NAND_BBT_SAVECONTENT) { +			/* Make it block aligned */ +			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); +			len = 1 << this->bbt_erase_shift; +			res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); +			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"); +			} +			/* Calc the byte offset in the buffer */ +			pageoffs = page - (int)(to >> this->page_shift); +			offs = pageoffs << this->page_shift; +			/* Preset the bbt area with 0xff */ +			memset (&buf[offs], 0xff, (size_t)(numblocks >> sft)); +			/* Preset the bbt's oob area with 0xff */ +			memset (&buf[len + pageoffs * mtd->oobsize], 0xff, +				((len >> this->page_shift) - pageoffs) * mtd->oobsize); +			if (td->options & NAND_BBT_VERSION) { +				buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip]; +			} +		} else { +			/* Calc length */ +			len = (size_t) (numblocks >> sft); +			/* Make it page aligned ! */ +			len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); +			/* Preset the buffer with 0xff */ +			memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); +			offs = 0; +			/* Pattern is located in oob area of first page */ +			memcpy (&buf[len + td->offs], td->pattern, td->len); +			if (td->options & NAND_BBT_VERSION) { +				buf[len + td->veroffs] = td->version[chip]; +			} +		} + +		/* 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 = (unsigned long) to; +		einfo.len = 1 << this->bbt_erase_shift; +		res = nand_erase_nand (mtd, &einfo, 1); +		if (res < 0) { +			printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); +			return res; +		} + +		res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); +		if (res < 0) { +			printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); +			return res; +		} +		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; +} + +/** + * 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 int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ +	struct nand_chip *this = mtd->priv; + +	/* Ensure that we only scan for the pattern and nothing else */ +	bd->options = 0; +	create_bbt (mtd, this->data_buf, bd, -1); +	return 0; +} + +/** + * check_create - [GENERIC] create and write bbt(s) if neccecary + * @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 read_bbt + * and creates / updates the bbt(s) if neccecary + * Creation is neccecary if no bbt was found for the chip/device + * Update is neccecary if one of the tables is missing or the + * version nr. of one table is less than the other +*/ +static int 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; + +	/* Do we have a bbt per chip ? */ +	if (td->options & NAND_BBT_PERCHIP) +		chips = this->numchips; +	else +		chips = 1; + +	for (i = 0; i < chips; i++) { +		writeops = 0; +		rd = NULL; +		rd2 = NULL; +		/* Per chip or per device ? */ +		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1; +		/* Mirrored table avilable ? */ +		if (md) { +			if (td->pages[i] == -1 && md->pages[i] == -1) { +				writeops = 0x03; +				goto create; +			} + +			if (td->pages[i] == -1) { +				rd = md; +				td->version[i] = md->version[i]; +				writeops = 1; +				goto writecheck; +			} + +			if (md->pages[i] == -1) { +				rd = td; +				md->version[i] = td->version[i]; +				writeops = 2; +				goto writecheck; +			} + +			if (td->version[i] == md->version[i]) { +				rd = td; +				if (!(td->options & NAND_BBT_VERSION)) +					rd2 = md; +				goto writecheck; +			} + +			if (((int8_t) (td->version[i] - md->version[i])) > 0) { +				rd = td; +				md->version[i] = td->version[i]; +				writeops = 2; +			} else { +				rd = md; +				td->version[i] = md->version[i]; +				writeops = 1; +			} + +			goto writecheck; + +		} else { +			if (td->pages[i] == -1) { +				writeops = 0x01; +				goto create; +			} +			rd = td; +			goto writecheck; +		} +create: +		/* Create the bad block table by scanning the device ? */ +		if (!(td->options & NAND_BBT_CREATE)) +			continue; + +		/* Create the table in memory by scanning the chip(s) */ +		create_bbt (mtd, buf, bd, chipsel); + +		td->version[i] = 1; +		if (md) +			md->version[i] = 1; +writecheck: +		/* read back first ? */ +		if (rd) +			read_abs_bbt (mtd, buf, rd, chipsel); +		/* If they weren't versioned, read both. */ +		if (rd2) +			read_abs_bbt (mtd, buf, rd2, chipsel); + +		/* Write the bad block table to the device ? */ +		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { +			res = write_bbt (mtd, buf, td, md, chipsel); +			if (res < 0) +				return res; +		} + +		/* Write the mirror bad block table to the device ? */ +		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { +			res = write_bbt (mtd, buf, md, td, chipsel); +			if (res < 0) +				return res; +		} +	} +	return 0; +} + +/** + * mark_bbt_regions - [GENERIC] mark the bad block table regions + * @mtd:	MTD device structure + * @td:		bad block table descriptor + * + * The bad block table regions are marked as "bad" to prevent + * accidental erasures / writes. The regions are identified by + * the mark 0x02. +*/ +static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td) +{ +	struct nand_chip *this = mtd->priv; +	int i, j, chips, block, nrblocks, update; +	uint8_t oldval, newval; + +	/* Do we have a bbt per chip ? */ +	if (td->options & NAND_BBT_PERCHIP) { +		chips = this->numchips; +		nrblocks = (int)(this->chipsize >> this->bbt_erase_shift); +	} else { +		chips = 1; +		nrblocks = (int)(mtd->size >> this->bbt_erase_shift); +	} + +	for (i = 0; i < chips; i++) { +		if ((td->options & NAND_BBT_ABSPAGE) || +		    !(td->options & NAND_BBT_WRITE)) { +		    	if (td->pages[i] == -1) continue; +			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); +			block <<= 1; +			oldval = this->bbt[(block >> 3)]; +			newval = oldval | (0x2 << (block & 0x06)); +			this->bbt[(block >> 3)] = newval; +			if ((oldval != newval) && td->reserved_block_code) +				nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1)); +			continue; +		} +		update = 0; +		if (td->options & NAND_BBT_LASTBLOCK) +			block = ((i + 1) * nrblocks) - td->maxblocks; +		else +			block = i * nrblocks; +		block <<= 1; +		for (j = 0; j < td->maxblocks; j++) { +			oldval = this->bbt[(block >> 3)]; +			newval = oldval | (0x2 << (block & 0x06)); +			this->bbt[(block >> 3)] = newval; +			if (oldval != newval) update = 1; +			block += 2; +		} +		/* If we want reserved blocks to be recorded to flash, and some +		   new ones have been marked, then we need to update the stored +		   bbts.  This should only happen once. */ +		if (update && td->reserved_block_code) +			nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1)); +	} +} + +/** + * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) + * @mtd:	MTD device structure + * @bd:		descriptor for the good/bad block search pattern + * + * The function checks, if a bad block table(s) is/are already + * available. If not it scans the device for manufacturer + * marked good / bad blocks and writes the bad block table(s) to + * the selected place. + * + * The bad block table memory is allocated here. It must be freed + * by calling the nand_free_bbt function. + * +*/ +int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +{ +	struct nand_chip *this = mtd->priv; +	int len, res = 0; +	uint8_t *buf; +	struct nand_bbt_descr *td = this->bbt_td; +	struct nand_bbt_descr *md = this->bbt_md; + +	len = mtd->size >> (this->bbt_erase_shift + 2); +	/* Allocate memory (2bit per block) */ +	this->bbt = kmalloc (len, GFP_KERNEL); +	if (!this->bbt) { +		printk (KERN_ERR "nand_scan_bbt: Out of memory\n"); +		return -ENOMEM; +	} +	/* Clear the memory bad block table */ +	memset (this->bbt, 0x00, len); + +	/* If no primary table decriptor is given, scan the device +	 * to build a memory based bad block table +	 */ +	if (!td) +		return nand_memory_bbt(mtd, bd); + +	/* Allocate a temporary buffer for one eraseblock incl. oob */ +	len = (1 << this->bbt_erase_shift); +	len += (len >> this->page_shift) * mtd->oobsize; +	buf = kmalloc (len, GFP_KERNEL); +	if (!buf) { +		printk (KERN_ERR "nand_bbt: Out of memory\n"); +		kfree (this->bbt); +		this->bbt = NULL; +		return -ENOMEM; +	} + +	/* Is the bbt at a given page ? */ +	if (td->options & NAND_BBT_ABSPAGE) { +		res = read_abs_bbts (mtd, buf, td, md); +	} else { +		/* Search the bad block table using a pattern in oob */ +		res = search_read_bbts (mtd, buf, td, md); +	} + +	if (res) +		res = check_create (mtd, buf, bd); + +	/* Prevent the bbt regions from erasing / writing */ +	mark_bbt_region (mtd, td); +	if (md) +		mark_bbt_region (mtd, md); + +	kfree (buf); +	return res; +} + + +/** + * nand_update_bbt - [NAND Interface] update bad block table(s) + * @mtd:	MTD device structure + * @offs:	the offset of the newly marked block + * + * The function updates the bad block table(s) +*/ +int nand_update_bbt (struct mtd_info *mtd, loff_t offs) +{ +	struct nand_chip *this = mtd->priv; +	int len, res = 0, writeops = 0; +	int chip, chipsel; +	uint8_t *buf; +	struct nand_bbt_descr *td = this->bbt_td; +	struct nand_bbt_descr *md = this->bbt_md; + +	if (!this->bbt || !td) +		return -EINVAL; + +	len = mtd->size >> (this->bbt_erase_shift + 2); +	/* Allocate a temporary buffer for one eraseblock incl. oob */ +	len = (1 << this->bbt_erase_shift); +	len += (len >> this->page_shift) * mtd->oobsize; +	buf = kmalloc (len, GFP_KERNEL); +	if (!buf) { +		printk (KERN_ERR "nand_update_bbt: Out of memory\n"); +		return -ENOMEM; +	} + +	writeops = md != NULL ? 0x03 : 0x01; + +	/* Do we have a bbt per chip ? */ +	if (td->options & NAND_BBT_PERCHIP) { +		chip = (int) (offs >> this->chip_shift); +		chipsel = chip; +	} else { +		chip = 0; +		chipsel = -1; +	} + +	td->version[chip]++; +	if (md) +		md->version[chip]++; + +	/* Write the bad block table to the device ? */ +	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) { +		res = write_bbt (mtd, buf, td, md, chipsel); +		if (res < 0) +			goto out; +	} +	/* Write the mirror bad block table to the device ? */ +	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) { +		res = write_bbt (mtd, buf, md, td, chipsel); +	} + +out: +	kfree (buf); +	return res; +} + +/* Define some generic bad / good block scan pattern which are used + * while scanning a device for factory marked good / bad blocks + * + * The memory based patterns just + */ +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr smallpage_memorybased = { +	.options = 0, +	.offs = 5, +	.len = 1, +	.pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_memorybased = { +	.options = 0, +	.offs = 0, +	.len = 2, +	.pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr smallpage_flashbased = { +	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, +	.offs = 5, +	.len = 1, +	.pattern = scan_ff_pattern +}; + +static struct nand_bbt_descr largepage_flashbased = { +	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, +	.offs = 0, +	.len = 2, +	.pattern = scan_ff_pattern +}; + +static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 }; + +static struct nand_bbt_descr agand_flashbased = { +	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, +	.offs = 0x20, +	.len = 6, +	.pattern = scan_agand_pattern +}; + +/* Generic flash bbt decriptors +*/ +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, +	.offs =	8, +	.len = 4, +	.veroffs = 12, +	.maxblocks = 4, +	.pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr = { +	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE +		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, +	.offs =	8, +	.len = 4, +	.veroffs = 12, +	.maxblocks = 4, +	.pattern = mirror_pattern +}; + +/** + * 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; + +	/* 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) { +		/* 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->oobblock > 512) ? +				&largepage_flashbased : &smallpage_flashbased; +		} +	} else { +		this->bbt_td = NULL; +		this->bbt_md = NULL; +		if (!this->badblock_pattern) { +			this->badblock_pattern = (mtd->oobblock > 512) ? +				&largepage_memorybased : &smallpage_memorybased; +		} +	} +	return nand_scan_bbt (mtd, this->badblock_pattern); +} + +/** + * 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 + * + */ +int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) +{ +	struct nand_chip *this = mtd->priv; +	int block; +	uint8_t	res; + +	/* Get block number * 2 */ +	block = (int) (offs >> (this->bbt_erase_shift - 1)); +	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + +	DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", +		(unsigned int)offs, res, block >> 1); + +	switch ((int)res) { +	case 0x00:	return 0; +	case 0x01:	return 1; +	case 0x02:	return allowbbt ? 0 : 1; +	} +	return 1; +} + +#endif |