diff options
| author | Haavard Skinnemoen <haavard.skinnemoen@atmel.com> | 2008-12-17 16:53:07 +0100 | 
|---|---|---|
| committer | Haavard Skinnemoen <haavard.skinnemoen@atmel.com> | 2008-12-17 16:53:07 +0100 | 
| commit | cb5473205206c7f14cbb1e747f28ec75b48826e2 (patch) | |
| tree | 8f4808d60917100b18a10b05230f7638a0a9bbcc /drivers/mtd/nand/nand_base.c | |
| parent | baf449fc5ff96f071bb0e3789fd3265f6d4fd9a0 (diff) | |
| parent | 92c78a3bbcb2ce508b4bf1c4a1e0940406a024bb (diff) | |
| download | olio-uboot-2014.01-cb5473205206c7f14cbb1e747f28ec75b48826e2.tar.xz olio-uboot-2014.01-cb5473205206c7f14cbb1e747f28ec75b48826e2.zip | |
Merge branch 'fixes' into cleanups
Conflicts:
	board/atmel/atngw100/atngw100.c
	board/atmel/atstk1000/atstk1000.c
	cpu/at32ap/at32ap700x/gpio.c
	include/asm-avr32/arch-at32ap700x/clk.h
	include/configs/atngw100.h
	include/configs/atstk1002.h
	include/configs/atstk1003.h
	include/configs/atstk1004.h
	include/configs/atstk1006.h
	include/configs/favr-32-ezkit.h
	include/configs/hammerhead.h
	include/configs/mimc200.h
Diffstat (limited to 'drivers/mtd/nand/nand_base.c')
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 141 | 
1 files changed, 130 insertions, 11 deletions
| diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0913bb874..ba05b762e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -7,7 +7,7 @@   *   Basic support for AG-AND chips is provided.   *   *	Additional technical information is available on - *	http://www.linux-mtd.infradead.org/tech/nand.html + *	http://www.linux-mtd.infradead.org/doc/nand.html   *   *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)   *		  2002-2006 Thomas Gleixner (tglx@linutronix.de) @@ -24,6 +24,7 @@   *	if we have HW ecc support.   *	The AG-AND chips have nice features for speed improvement,   *	which are not supported yet. Read / program 4 pages in one go. + *	BBT table is not serialized, has to be fixed   *   * 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 @@ -128,7 +129,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,  static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);  /* - * For devices which display every fart in the system on a seperate LED. Is + * For devices which display every fart in the system on a separate LED. Is   * compiled away when LED support is disabled.   */  /* XXX U-BOOT XXX */ @@ -412,6 +413,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  		/* We write two bytes, so we dont have to mess with 16 bit  		 * access  		 */ +		nand_get_device(chip, mtd, FL_WRITING);  		ofs += mtd->oobsize;  		chip->ops.len = chip->ops.ooblen = 2;  		chip->ops.datbuf = NULL; @@ -419,9 +421,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)  		chip->ops.ooboffs = chip->badblockpos & ~0x01;  		ret = nand_do_write_oob(mtd, ofs, &chip->ops); +		nand_release_device(mtd);  	}  	if (!ret)  		mtd->ecc_stats.badblocks++; +  	return ret;  } @@ -492,7 +496,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);  void nand_wait_ready(struct mtd_info *mtd)  {  	struct nand_chip *chip = mtd->priv; -	u32 timeo = (CFG_HZ * 20) / 1000; +	u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;  	reset_timer(); @@ -831,9 +835,9 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)  	int state = this->state;  	if (state == FL_ERASING) -		timeo = (CFG_HZ * 400) / 1000; +		timeo = (CONFIG_SYS_HZ * 400) / 1000;  	else -		timeo = (CFG_HZ * 20) / 1000; +		timeo = (CONFIG_SYS_HZ * 20) / 1000;  	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))  		this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1); @@ -911,7 +915,88 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,  		int stat;  		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); -		if (stat == -1) +		if (stat < 0) +			mtd->ecc_stats.failed++; +		else +			mtd->ecc_stats.corrected += stat; +	} +	return 0; +} + +/** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @dataofs	offset of requested data within the page + * @readlen	data length + * @buf:	buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ +	int start_step, end_step, num_steps; +	uint32_t *eccpos = chip->ecc.layout->eccpos; +	uint8_t *p; +	int data_col_addr, i, gaps = 0; +	int datafrag_len, eccfrag_len, aligned_len, aligned_pos; +	int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + +	/* Column address wihin the page aligned to ECC size (256bytes). */ +	start_step = data_offs / chip->ecc.size; +	end_step = (data_offs + readlen - 1) / chip->ecc.size; +	num_steps = end_step - start_step + 1; + +	/* Data size aligned to ECC ecc.size*/ +	datafrag_len = num_steps * chip->ecc.size; +	eccfrag_len = num_steps * chip->ecc.bytes; + +	data_col_addr = start_step * chip->ecc.size; +	/* If we read not a page aligned data */ +	if (data_col_addr != 0) +		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + +	p = bufpoi + data_col_addr; +	chip->read_buf(mtd, p, datafrag_len); + +	/* Calculate  ECC */ +	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) +		chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + +	/* The performance is faster if to position offsets +	   according to ecc.pos. Let make sure here that +	   there are no gaps in ecc positions */ +	for (i = 0; i < eccfrag_len - 1; i++) { +		if (eccpos[i + start_step * chip->ecc.bytes] + 1 != +			eccpos[i + start_step * chip->ecc.bytes + 1]) { +			gaps = 1; +			break; +		} +	} +	if (gaps) { +		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); +		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); +	} else { +		/* send the command to read the particular ecc bytes */ +		/* take care about buswidth alignment in read_buf */ +		aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); +		aligned_len = eccfrag_len; +		if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) +			aligned_len++; +		if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) +			aligned_len++; + +		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); +		chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); +	} + +	for (i = 0; i < eccfrag_len; i++) +		chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + +	p = bufpoi + data_col_addr; +	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { +		int stat; + +		stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); +		if (stat < 0)  			mtd->ecc_stats.failed++;  		else  			mtd->ecc_stats.corrected += stat; @@ -996,7 +1081,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,  		chip->read_buf(mtd, oob, eccbytes);  		stat = chip->ecc.correct(mtd, p, oob, NULL); -		if (stat == -1) +		if (stat < 0)  			mtd->ecc_stats.failed++;  		else  			mtd->ecc_stats.corrected += stat; @@ -1116,6 +1201,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  			/* Now read the page into the buffer */  			if (unlikely(ops->mode == MTD_OOB_RAW))  				ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); +			else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) +				ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);  			else  				ret = chip->ecc.read_page(mtd, chip, bufpoi);  			if (ret < 0) @@ -1123,7 +1210,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,  			/* Transfer not aligned data */  			if (!aligned) { -				chip->pagebuf = realpage; +				if (!NAND_SUBPAGE_READ(chip) && !oob) +					chip->pagebuf = realpage;  				memcpy(buf, chip->buffers->databuf + col, bytes);  			} @@ -2193,13 +2281,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,   erase_exit:  	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; -	/* Do call back function */ -	if (!ret) -		mtd_erase_callback(instr);  	/* Deselect and wake up anyone waiting on the device */  	nand_release_device(mtd); +	/* Do call back function */ +	if (!ret) +		mtd_erase_callback(instr); +  	/*  	 * If BBT requires refresh and erase was successful, rewrite any  	 * selected bad block tables @@ -2356,10 +2445,17 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  {  	struct nand_flash_dev *type = NULL;  	int i, dev_id, maf_idx; +	int tmp_id, tmp_manf;  	/* Select the device */  	chip->select_chip(mtd, 0); +	/* +	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) +	 * after power-up +	 */ +	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); +  	/* Send the command for reading device ID */  	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); @@ -2367,6 +2463,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  	*maf_id = chip->read_byte(mtd);  	dev_id = chip->read_byte(mtd); +	/* Try again to make sure, as some systems the bus-hold or other +	 * interface concerns can cause random data which looks like a +	 * possibly credible NAND flash to appear. If the two results do +	 * not match, ignore the device completely. +	 */ + +	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); + +	/* Read manufacturer and device IDs */ + +	tmp_manf = chip->read_byte(mtd); +	tmp_id = chip->read_byte(mtd); + +	if (tmp_manf != *maf_id || tmp_id != dev_id) { +		printk(KERN_INFO "%s: second ID read did not match " +		       "%02x,%02x against %02x,%02x\n", __func__, +		       *maf_id, dev_id, tmp_manf, tmp_id); +		return ERR_PTR(-ENODEV); +	} +  	/* Lookup the flash id */  	for (i = 0; nand_flash_ids[i].name != NULL; i++) {  		if (dev_id == nand_flash_ids[i].id) { @@ -2510,6 +2626,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)  	/* Check for a chip array */  	for (i = 1; i < maxchips; i++) {  		chip->select_chip(mtd, i); +		/* See comment in nand_get_flash_type for reset */ +		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);  		/* Send the command for reading device ID */  		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);  		/* Read manufacturer and device IDs */ @@ -2630,6 +2748,7 @@ int nand_scan_tail(struct mtd_info *mtd)  		chip->ecc.calculate = nand_calculate_ecc;  		chip->ecc.correct = nand_correct_data;  		chip->ecc.read_page = nand_read_page_swecc; +		chip->ecc.read_subpage = nand_read_subpage;  		chip->ecc.write_page = nand_write_page_swecc;  		chip->ecc.read_oob = nand_read_oob_std;  		chip->ecc.write_oob = nand_write_oob_std; |