diff options
Diffstat (limited to 'drivers/mtd/nand/fsmc_nand.c')
| -rw-r--r-- | drivers/mtd/nand/fsmc_nand.c | 486 | 
1 files changed, 486 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c new file mode 100644 index 000000000..7a61d88cc --- /dev/null +++ b/drivers/mtd/nand/fsmc_nand.c @@ -0,0 +1,486 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * (C) Copyright 2012 + * Amit Virdi, ST Microelectronics, amit.virdi@st.com. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <nand.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/fsmc_nand.h> +#include <asm/arch/hardware.h> + +static u32 fsmc_version; +static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) +	CONFIG_SYS_FSMC_BASE; + +/* + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of + * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can + * correct 1 bit in 512 bytes + */ + +static struct nand_ecclayout fsmc_ecc4_lp_layout = { +	.eccbytes = 104, +	.eccpos = {  2,   3,   4,   5,   6,   7,   8, +		9,  10,  11,  12,  13,  14, +		18,  19,  20,  21,  22,  23,  24, +		25,  26,  27,  28,  29,  30, +		34,  35,  36,  37,  38,  39,  40, +		41,  42,  43,  44,  45,  46, +		50,  51,  52,  53,  54,  55,  56, +		57,  58,  59,  60,  61,  62, +		66,  67,  68,  69,  70,  71,  72, +		73,  74,  75,  76,  77,  78, +		82,  83,  84,  85,  86,  87,  88, +		89,  90,  91,  92,  93,  94, +		98,  99, 100, 101, 102, 103, 104, +		105, 106, 107, 108, 109, 110, +		114, 115, 116, 117, 118, 119, 120, +		121, 122, 123, 124, 125, 126 +	}, +	.oobfree = { +		{.offset = 15, .length = 3}, +		{.offset = 31, .length = 3}, +		{.offset = 47, .length = 3}, +		{.offset = 63, .length = 3}, +		{.offset = 79, .length = 3}, +		{.offset = 95, .length = 3}, +		{.offset = 111, .length = 3}, +		{.offset = 127, .length = 1} +	} +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { +	.eccbytes = 104, +	.eccpos = {  2,   3,   4,   5,   6,   7,   8, +		9,  10,  11,  12,  13,  14, +		18,  19,  20,  21,  22,  23,  24, +		25,  26,  27,  28,  29,  30, +		34,  35,  36,  37,  38,  39,  40, +		41,  42,  43,  44,  45,  46, +		50,  51,  52,  53,  54,  55,  56, +		57,  58,  59,  60,  61,  62, +		66,  67,  68,  69,  70,  71,  72, +		73,  74,  75,  76,  77,  78, +		82,  83,  84,  85,  86,  87,  88, +		89,  90,  91,  92,  93,  94, +		98,  99, 100, 101, 102, 103, 104, +		105, 106, 107, 108, 109, 110, +		114, 115, 116, 117, 118, 119, 120, +		121, 122, 123, 124, 125, 126 +	}, +	.oobfree = { +		{.offset = 15, .length = 3}, +		{.offset = 31, .length = 3}, +		{.offset = 47, .length = 3}, +		{.offset = 63, .length = 3}, +		{.offset = 79, .length = 3}, +		{.offset = 95, .length = 3}, +		{.offset = 111, .length = 3}, +		{.offset = 127, .length = 97} +	} +}; + +/* + * ECC placement definitions in oobfree type format + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data + * Managing the ecc bytes in the following way makes it easier for software to + * read ecc bytes consecutive to data bytes. This way is similar to + * oobfree structure maintained already in u-boot nand driver + */ +static struct fsmc_eccplace fsmc_eccpl_lp = { +	.eccplace = { +		{.offset = 2, .length = 13}, +		{.offset = 18, .length = 13}, +		{.offset = 34, .length = 13}, +		{.offset = 50, .length = 13}, +		{.offset = 66, .length = 13}, +		{.offset = 82, .length = 13}, +		{.offset = 98, .length = 13}, +		{.offset = 114, .length = 13} +	} +}; + +static struct nand_ecclayout fsmc_ecc4_sp_layout = { +	.eccbytes = 13, +	.eccpos = { 0,  1,  2,  3,  6,  7, 8, +		9, 10, 11, 12, 13, 14 +	}, +	.oobfree = { +		{.offset = 15, .length = 1}, +	} +}; + +static struct fsmc_eccplace fsmc_eccpl_sp = { +	.eccplace = { +		{.offset = 0, .length = 4}, +		{.offset = 6, .length = 9} +	} +}; + +static struct nand_ecclayout fsmc_ecc1_layout = { +	.eccbytes = 24, +	.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, +		66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, +	.oobfree = { +		{.offset = 8, .length = 8}, +		{.offset = 24, .length = 8}, +		{.offset = 40, .length = 8}, +		{.offset = 56, .length = 8}, +		{.offset = 72, .length = 8}, +		{.offset = 88, .length = 8}, +		{.offset = 104, .length = 8}, +		{.offset = 120, .length = 8} +	} +}; + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ +	int k, written_bits = 0; + +	for (k = 0; k < size; k++) { +		written_bits += hweight8(~buff[k]); +		if (written_bits > max_bits) +			break; +	} + +	return written_bits; +} + +static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ +	struct nand_chip *this = mtd->priv; +	ulong IO_ADDR_W; + +	if (ctrl & NAND_CTRL_CHANGE) { +		IO_ADDR_W = (ulong)this->IO_ADDR_W; + +		IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); +		if (ctrl & NAND_CLE) +			IO_ADDR_W |= CONFIG_SYS_NAND_CLE; +		if (ctrl & NAND_ALE) +			IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + +		if (ctrl & NAND_NCE) { +			writel(readl(&fsmc_regs_p->pc) | +					FSMC_ENABLE, &fsmc_regs_p->pc); +		} else { +			writel(readl(&fsmc_regs_p->pc) & +					~FSMC_ENABLE, &fsmc_regs_p->pc); +		} +		this->IO_ADDR_W = (void *)IO_ADDR_W; +	} + +	if (cmd != NAND_CMD_NONE) +		writeb(cmd, this->IO_ADDR_W); +} + +static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, +		u_char *read_ecc, u_char *calc_ecc) +{ +	/* The calculated ecc is actually the correction index in data */ +	u32 err_idx[8]; +	u32 num_err, i; +	u32 ecc1, ecc2, ecc3, ecc4; + +	num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; + +	if (likely(num_err == 0)) +		return 0; + +	if (unlikely(num_err > 8)) { +		/* +		 * This is a temporary erase check. A newly erased page read +		 * would result in an ecc error because the oob data is also +		 * erased to FF and the calculated ecc for an FF data is not +		 * FF..FF. +		 * This is a workaround to skip performing correction in case +		 * data is FF..FF +		 * +		 * Logic: +		 * For every page, each bit written as 0 is counted until these +		 * number of bits are greater than 8 (the maximum correction +		 * capability of FSMC for each 512 + 13 bytes) +		 */ + +		int bits_ecc = count_written_bits(read_ecc, 13, 8); +		int bits_data = count_written_bits(dat, 512, 8); + +		if ((bits_ecc + bits_data) <= 8) { +			if (bits_data) +				memset(dat, 0xff, 512); +			return bits_data + bits_ecc; +		} + +		return -EBADMSG; +	} + +	ecc1 = readl(&fsmc_regs_p->ecc1); +	ecc2 = readl(&fsmc_regs_p->ecc2); +	ecc3 = readl(&fsmc_regs_p->ecc3); +	ecc4 = readl(&fsmc_regs_p->sts); + +	err_idx[0] = (ecc1 >> 0) & 0x1FFF; +	err_idx[1] = (ecc1 >> 13) & 0x1FFF; +	err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); +	err_idx[3] = (ecc2 >> 7) & 0x1FFF; +	err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); +	err_idx[5] = (ecc3 >> 1) & 0x1FFF; +	err_idx[6] = (ecc3 >> 14) & 0x1FFF; +	err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + +	i = 0; +	while (i < num_err) { +		err_idx[i] ^= 3; + +		if (err_idx[i] < 512 * 8) +			__change_bit(err_idx[i], dat); + +		i++; +	} + +	return num_err; +} + +static int fsmc_read_hwecc(struct mtd_info *mtd, +			const u_char *data, u_char *ecc) +{ +	u_int ecc_tmp; +	int timeout = CONFIG_SYS_HZ; +	ulong start; + +	switch (fsmc_version) { +	case FSMC_VER8: +		start = get_timer(0); +		while (get_timer(start) < timeout) { +			/* +			 * Busy waiting for ecc computation +			 * to finish for 512 bytes +			 */ +			if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) +				break; +		} + +		ecc_tmp = readl(&fsmc_regs_p->ecc1); +		ecc[0] = (u_char) (ecc_tmp >> 0); +		ecc[1] = (u_char) (ecc_tmp >> 8); +		ecc[2] = (u_char) (ecc_tmp >> 16); +		ecc[3] = (u_char) (ecc_tmp >> 24); + +		ecc_tmp = readl(&fsmc_regs_p->ecc2); +		ecc[4] = (u_char) (ecc_tmp >> 0); +		ecc[5] = (u_char) (ecc_tmp >> 8); +		ecc[6] = (u_char) (ecc_tmp >> 16); +		ecc[7] = (u_char) (ecc_tmp >> 24); + +		ecc_tmp = readl(&fsmc_regs_p->ecc3); +		ecc[8] = (u_char) (ecc_tmp >> 0); +		ecc[9] = (u_char) (ecc_tmp >> 8); +		ecc[10] = (u_char) (ecc_tmp >> 16); +		ecc[11] = (u_char) (ecc_tmp >> 24); + +		ecc_tmp = readl(&fsmc_regs_p->sts); +		ecc[12] = (u_char) (ecc_tmp >> 16); +		break; + +	default: +		ecc_tmp = readl(&fsmc_regs_p->ecc1); +		ecc[0] = (u_char) (ecc_tmp >> 0); +		ecc[1] = (u_char) (ecc_tmp >> 8); +		ecc[2] = (u_char) (ecc_tmp >> 16); +		break; +	} + +	return 0; +} + +void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ +	writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, +			&fsmc_regs_p->pc); +	writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, +			&fsmc_regs_p->pc); +	writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, +			&fsmc_regs_p->pc); +} + +/* + * fsmc_read_page_hwecc + * @mtd:	mtd info structure + * @chip:	nand chip info structure + * @buf:	buffer to store read data + * @page:	page number to read + * + * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(upto a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, +				 uint8_t *buf, int page) +{ +	struct fsmc_eccplace *fsmc_eccpl; +	int i, j, s, stat, eccsize = chip->ecc.size; +	int eccbytes = chip->ecc.bytes; +	int eccsteps = chip->ecc.steps; +	uint8_t *p = buf; +	uint8_t *ecc_calc = chip->buffers->ecccalc; +	uint8_t *ecc_code = chip->buffers->ecccode; +	int off, len, group = 0; +	uint8_t oob[13] __attribute__ ((aligned (2))); + +	/* Differentiate between small and large page ecc place definitions */ +	if (mtd->writesize == 512) +		fsmc_eccpl = &fsmc_eccpl_sp; +	else +		fsmc_eccpl = &fsmc_eccpl_lp; + +	for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + +		chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); +		chip->ecc.hwctl(mtd, NAND_ECC_READ); +		chip->read_buf(mtd, p, eccsize); + +		for (j = 0; j < eccbytes;) { +			off = fsmc_eccpl->eccplace[group].offset; +			len = fsmc_eccpl->eccplace[group].length; +			group++; + +			/* +			 * length is intentionally kept a higher multiple of 2 +			 * to read at least 13 bytes even in case of 16 bit NAND +			 * devices +			 */ +			if (chip->options & NAND_BUSWIDTH_16) +				len = roundup(len, 2); +			chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); +			chip->read_buf(mtd, oob + j, len); +			j += len; +		} + +		memcpy(&ecc_code[i], oob, 13); +		chip->ecc.calculate(mtd, p, &ecc_calc[i]); + +		stat = chip->ecc.correct(mtd, p, &ecc_code[i], +				&ecc_calc[i]); +		if (stat < 0) +			mtd->ecc_stats.failed++; +		else +			mtd->ecc_stats.corrected += stat; +	} + +	return 0; +} + +int fsmc_nand_init(struct nand_chip *nand) +{ +	static int chip_nr; +	struct mtd_info *mtd; +	int i; +	u32 peripid2 = readl(&fsmc_regs_p->peripid2); + +	fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & +		FSMC_REVISION_MSK; + +	writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); + +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) +	writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, +			&fsmc_regs_p->pc); +#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) +	writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, +			&fsmc_regs_p->pc); +#else +#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT +#endif +	writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, +			&fsmc_regs_p->pc); +	writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, +			&fsmc_regs_p->comm); +	writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, +			&fsmc_regs_p->attrib); + +	nand->options = 0; +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) +	nand->options |= NAND_BUSWIDTH_16; +#endif +	nand->ecc.mode = NAND_ECC_HW; +	nand->ecc.size = 512; +	nand->ecc.calculate = fsmc_read_hwecc; +	nand->ecc.hwctl = fsmc_enable_hwecc; +	nand->cmd_ctrl = fsmc_nand_hwcontrol; +	nand->IO_ADDR_R = nand->IO_ADDR_W = +		(void  __iomem *)CONFIG_SYS_NAND_BASE; +	nand->badblockbits = 7; + +	mtd = &nand_info[chip_nr++]; +	mtd->priv = nand; + +	switch (fsmc_version) { +	case FSMC_VER8: +		nand->ecc.bytes = 13; +		nand->ecc.correct = fsmc_bch8_correct_data; +		nand->ecc.read_page = fsmc_read_page_hwecc; +		if (mtd->writesize == 512) +			nand->ecc.layout = &fsmc_ecc4_sp_layout; +		else { +			if (mtd->oobsize == 224) +				nand->ecc.layout = &fsmc_ecc4_224_layout; +			else +				nand->ecc.layout = &fsmc_ecc4_lp_layout; +		} + +		break; +	default: +		nand->ecc.bytes = 3; +		nand->ecc.layout = &fsmc_ecc1_layout; +		nand->ecc.correct = nand_correct_data; +		break; +	} + +	/* Detect NAND chips */ +	if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) +		return -ENXIO; + +	if (nand_scan_tail(mtd)) +		return -ENXIO; + +	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) +		if (nand_register(i)) +			return -ENXIO; + +	return 0; +} |