diff options
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 680 | 
1 files changed, 679 insertions, 1 deletions
| diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index cc609b1b5..c6aa5db33 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -5,6 +5,9 @@   *   * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas   * + * Add Programmable Multibit ECC support for various AT91 SoC + *     (C) Copyright 2012 ATMEL, Hong Xu + *   * See file CREDITS for list of people who contributed to this   * project.   * @@ -30,6 +33,7 @@  #include <asm/arch/at91_pio.h>  #include <nand.h> +#include <watchdog.h>  #ifdef CONFIG_ATMEL_NAND_HWECC @@ -41,6 +45,674 @@  #include "atmel_nand_ecc.h"	/* Hardware ECC registers */ +#ifdef CONFIG_ATMEL_NAND_HW_PMECC + +struct atmel_nand_host { +	struct pmecc_regs __iomem *pmecc; +	struct pmecc_errloc_regs __iomem *pmerrloc; +	void __iomem		*pmecc_rom_base; + +	u8		pmecc_corr_cap; +	u16		pmecc_sector_size; +	u32		pmecc_index_table_offset; + +	int		pmecc_bytes_per_sector; +	int		pmecc_sector_number; +	int		pmecc_degree;	/* Degree of remainders */ +	int		pmecc_cw_len;	/* Length of codeword */ + +	/* lookup table for alpha_to and index_of */ +	void __iomem	*pmecc_alpha_to; +	void __iomem	*pmecc_index_of; + +	/* data for pmecc computation */ +	int16_t	pmecc_smu[(CONFIG_PMECC_CAP + 2) * (2 * CONFIG_PMECC_CAP + 1)]; +	int16_t	pmecc_partial_syn[2 * CONFIG_PMECC_CAP + 1]; +	int16_t	pmecc_si[2 * CONFIG_PMECC_CAP + 1]; +	int16_t	pmecc_lmu[CONFIG_PMECC_CAP + 1]; /* polynomal order */ +	int	pmecc_mu[CONFIG_PMECC_CAP + 1]; +	int	pmecc_dmu[CONFIG_PMECC_CAP + 1]; +	int	pmecc_delta[CONFIG_PMECC_CAP + 1]; +}; + +static struct atmel_nand_host pmecc_host; +static struct nand_ecclayout atmel_pmecc_oobinfo; + +/* + * Return number of ecc bytes per sector according to sector size and + * correction capability + * + * Following table shows what at91 PMECC supported: + * Correction Capability	Sector_512_bytes	Sector_1024_bytes + * =====================	================	================= + *                2-bits                 4-bytes                  4-bytes + *                4-bits                 7-bytes                  7-bytes + *                8-bits                13-bytes                 14-bytes + *               12-bits                20-bytes                 21-bytes + *               24-bits                39-bytes                 42-bytes + */ +static int pmecc_get_ecc_bytes(int cap, int sector_size) +{ +	int m = 12 + sector_size / 512; +	return (m * cap + 7) / 8; +} + +static void pmecc_config_ecc_layout(struct nand_ecclayout *layout, +	int oobsize, int ecc_len) +{ +	int i; + +	layout->eccbytes = ecc_len; + +	/* ECC will occupy the last ecc_len bytes continuously */ +	for (i = 0; i < ecc_len; i++) +		layout->eccpos[i] = oobsize - ecc_len + i; + +	layout->oobfree[0].offset = 2; +	layout->oobfree[0].length = +		oobsize - ecc_len - layout->oobfree[0].offset; +} + +static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) +{ +	int table_size; + +	table_size = host->pmecc_sector_size == 512 ? +		PMECC_INDEX_TABLE_SIZE_512 : PMECC_INDEX_TABLE_SIZE_1024; + +	/* the ALPHA lookup table is right behind the INDEX lookup table. */ +	return host->pmecc_rom_base + host->pmecc_index_table_offset + +			table_size * sizeof(int16_t); +} + +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i; +	uint32_t value; + +	/* Fill odd syndromes */ +	for (i = 0; i < host->pmecc_corr_cap; i++) { +		value = readl(&host->pmecc->rem_port[sector].rem[i / 2]); +		if (i & 1) +			value >>= 16; +		value &= 0xffff; +		host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value; +	} +} + +static void pmecc_substitute(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int16_t __iomem *alpha_to = host->pmecc_alpha_to; +	int16_t __iomem *index_of = host->pmecc_index_of; +	int16_t *partial_syn = host->pmecc_partial_syn; +	const int cap = host->pmecc_corr_cap; +	int16_t *si; +	int i, j; + +	/* si[] is a table that holds the current syndrome value, +	 * an element of that table belongs to the field +	 */ +	si = host->pmecc_si; + +	memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1)); + +	/* Computation 2t syndromes based on S(x) */ +	/* Odd syndromes */ +	for (i = 1; i < 2 * cap; i += 2) { +		for (j = 0; j < host->pmecc_degree; j++) { +			if (partial_syn[i] & (0x1 << j)) +				si[i] = readw(alpha_to + i * j) ^ si[i]; +		} +	} +	/* Even syndrome = (Odd syndrome) ** 2 */ +	for (i = 2, j = 1; j <= cap; i = ++j << 1) { +		if (si[j] == 0) { +			si[i] = 0; +		} else { +			int16_t tmp; + +			tmp = readw(index_of + si[j]); +			tmp = (tmp * 2) % host->pmecc_cw_len; +			si[i] = readw(alpha_to + tmp); +		} +	} +} + +/* + * This function defines a Berlekamp iterative procedure for + * finding the value of the error location polynomial. + * The input is si[], initialize by pmecc_substitute(). + * The output is smu[][]. + * + * This function is written according to chip datasheet Chapter: + * Find the Error Location Polynomial Sigma(x) of Section: + * Programmable Multibit ECC Control (PMECC). + */ +static void pmecc_get_sigma(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; + +	int16_t *lmu = host->pmecc_lmu; +	int16_t *si = host->pmecc_si; +	int *mu = host->pmecc_mu; +	int *dmu = host->pmecc_dmu;	/* Discrepancy */ +	int *delta = host->pmecc_delta; /* Delta order */ +	int cw_len = host->pmecc_cw_len; +	const int16_t cap = host->pmecc_corr_cap; +	const int num = 2 * cap + 1; +	int16_t __iomem	*index_of = host->pmecc_index_of; +	int16_t __iomem	*alpha_to = host->pmecc_alpha_to; +	int i, j, k; +	uint32_t dmu_0_count, tmp; +	int16_t *smu = host->pmecc_smu; + +	/* index of largest delta */ +	int ro; +	int largest; +	int diff; + +	/* Init the Sigma(x) */ +	memset(smu, 0, sizeof(int16_t) * ARRAY_SIZE(smu)); + +	dmu_0_count = 0; + +	/* First Row */ + +	/* Mu */ +	mu[0] = -1; + +	smu[0] = 1; + +	/* discrepancy set to 1 */ +	dmu[0] = 1; +	/* polynom order set to 0 */ +	lmu[0] = 0; +	/* delta[0] = (mu[0] * 2 - lmu[0]) >> 1; */ +	delta[0] = -1; + +	/* Second Row */ + +	/* Mu */ +	mu[1] = 0; +	/* Sigma(x) set to 1 */ +	smu[num] = 1; + +	/* discrepancy set to S1 */ +	dmu[1] = si[1]; + +	/* polynom order set to 0 */ +	lmu[1] = 0; + +	/* delta[1] = (mu[1] * 2 - lmu[1]) >> 1; */ +	delta[1] = 0; + +	for (i = 1; i <= cap; i++) { +		mu[i + 1] = i << 1; +		/* Begin Computing Sigma (Mu+1) and L(mu) */ +		/* check if discrepancy is set to 0 */ +		if (dmu[i] == 0) { +			dmu_0_count++; + +			tmp = ((cap - (lmu[i] >> 1) - 1) / 2); +			if ((cap - (lmu[i] >> 1) - 1) & 0x1) +				tmp += 2; +			else +				tmp += 1; + +			if (dmu_0_count == tmp) { +				for (j = 0; j <= (lmu[i] >> 1) + 1; j++) +					smu[(cap + 1) * num + j] = +							smu[i * num + j]; + +				lmu[cap + 1] = lmu[i]; +				return; +			} + +			/* copy polynom */ +			for (j = 0; j <= lmu[i] >> 1; j++) +				smu[(i + 1) * num + j] = smu[i * num + j]; + +			/* copy previous polynom order to the next */ +			lmu[i + 1] = lmu[i]; +		} else { +			ro = 0; +			largest = -1; +			/* find largest delta with dmu != 0 */ +			for (j = 0; j < i; j++) { +				if ((dmu[j]) && (delta[j] > largest)) { +					largest = delta[j]; +					ro = j; +				} +			} + +			/* compute difference */ +			diff = (mu[i] - mu[ro]); + +			/* Compute degree of the new smu polynomial */ +			if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff)) +				lmu[i + 1] = lmu[i]; +			else +				lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2; + +			/* Init smu[i+1] with 0 */ +			for (k = 0; k < num; k++) +				smu[(i + 1) * num + k] = 0; + +			/* Compute smu[i+1] */ +			for (k = 0; k <= lmu[ro] >> 1; k++) { +				int16_t a, b, c; + +				if (!(smu[ro * num + k] && dmu[i])) +					continue; +				a = readw(index_of + dmu[i]); +				b = readw(index_of + dmu[ro]); +				c = readw(index_of + smu[ro * num + k]); +				tmp = a + (cw_len - b) + c; +				a = readw(alpha_to + tmp % cw_len); +				smu[(i + 1) * num + (k + diff)] = a; +			} + +			for (k = 0; k <= lmu[i] >> 1; k++) +				smu[(i + 1) * num + k] ^= smu[i * num + k]; +		} + +		/* End Computing Sigma (Mu+1) and L(mu) */ +		/* In either case compute delta */ +		delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1; + +		/* Do not compute discrepancy for the last iteration */ +		if (i >= cap) +			continue; + +		for (k = 0; k <= (lmu[i + 1] >> 1); k++) { +			tmp = 2 * (i - 1); +			if (k == 0) { +				dmu[i + 1] = si[tmp + 3]; +			} else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) { +				int16_t a, b, c; +				a = readw(index_of + +						smu[(i + 1) * num + k]); +				b = si[2 * (i - 1) + 3 - k]; +				c = readw(index_of + b); +				tmp = a + c; +				tmp %= cw_len; +				dmu[i + 1] = readw(alpha_to + tmp) ^ +					dmu[i + 1]; +			} +		} +	} +} + +static int pmecc_err_location(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	const int cap = host->pmecc_corr_cap; +	const int num = 2 * cap + 1; +	int sector_size = host->pmecc_sector_size; +	int err_nbr = 0;	/* number of error */ +	int roots_nbr;		/* number of roots */ +	int i; +	uint32_t val; +	int16_t *smu = host->pmecc_smu; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	writel(PMERRLOC_DISABLE, &host->pmerrloc->eldis); + +	for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) { +		writel(smu[(cap + 1) * num + i], &host->pmerrloc->sigma[i]); +		err_nbr++; +	} + +	val = PMERRLOC_ELCFG_NUM_ERRORS(err_nbr - 1); +	if (sector_size == 1024) +		val |= PMERRLOC_ELCFG_SECTOR_1024; + +	writel(val, &host->pmerrloc->elcfg); +	writel(sector_size * 8 + host->pmecc_degree * cap, +			&host->pmerrloc->elen); + +	while (--timeout) { +		if (readl(&host->pmerrloc->elisr) & PMERRLOC_CALC_DONE) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to calculate PMECC error location\n"); +		return -1; +	} + +	roots_nbr = (readl(&host->pmerrloc->elisr) & PMERRLOC_ERR_NUM_MASK) +			>> 8; +	/* Number of roots == degree of smu hence <= cap */ +	if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1) +		return err_nbr - 1; + +	/* Number of roots does not match the degree of smu +	 * unable to correct error */ +	return -1; +} + +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, +		int sector_num, int extra_bytes, int err_nbr) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i = 0; +	int byte_pos, bit_pos, sector_size, pos; +	uint32_t tmp; +	uint8_t err_byte; + +	sector_size = host->pmecc_sector_size; + +	while (err_nbr) { +		tmp = readl(&host->pmerrloc->el[i]) - 1; +		byte_pos = tmp / 8; +		bit_pos  = tmp % 8; + +		if (byte_pos >= (sector_size + extra_bytes)) +			BUG();	/* should never happen */ + +		if (byte_pos < sector_size) { +			err_byte = *(buf + byte_pos); +			*(buf + byte_pos) ^= (1 << bit_pos); + +			pos = sector_num * host->pmecc_sector_size + byte_pos; +			printk(KERN_INFO "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", +				pos, bit_pos, err_byte, *(buf + byte_pos)); +		} else { +			/* Bit flip in OOB area */ +			tmp = sector_num * host->pmecc_bytes_per_sector +					+ (byte_pos - sector_size); +			err_byte = ecc[tmp]; +			ecc[tmp] ^= (1 << bit_pos); + +			pos = tmp + nand_chip->ecc.layout->eccpos[0]; +			printk(KERN_INFO "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", +				pos, bit_pos, err_byte, ecc[tmp]); +		} + +		i++; +		err_nbr--; +	} + +	return; +} + +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf, +	u8 *ecc) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	int i, err_nbr, eccbytes; +	uint8_t *buf_pos; + +	eccbytes = nand_chip->ecc.bytes; +	for (i = 0; i < eccbytes; i++) +		if (ecc[i] != 0xff) +			goto normal_check; +	/* Erased page, return OK */ +	return 0; + +normal_check: +	for (i = 0; i < host->pmecc_sector_number; i++) { +		err_nbr = 0; +		if (pmecc_stat & 0x1) { +			buf_pos = buf + i * host->pmecc_sector_size; + +			pmecc_gen_syndrome(mtd, i); +			pmecc_substitute(mtd); +			pmecc_get_sigma(mtd); + +			err_nbr = pmecc_err_location(mtd); +			if (err_nbr == -1) { +				printk(KERN_ERR "PMECC: Too many errors\n"); +				mtd->ecc_stats.failed++; +				return -EIO; +			} else { +				pmecc_correct_data(mtd, buf_pos, ecc, i, +					host->pmecc_bytes_per_sector, err_nbr); +				mtd->ecc_stats.corrected += err_nbr; +			} +		} +		pmecc_stat >>= 1; +	} + +	return 0; +} + +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, +	struct nand_chip *chip, uint8_t *buf, int page) +{ +	struct atmel_nand_host *host = chip->priv; +	int eccsize = chip->ecc.size; +	uint8_t *oob = chip->oob_poi; +	uint32_t *eccpos = chip->ecc.layout->eccpos; +	uint32_t stat; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); +	pmecc_writel(host->pmecc, cfg, ((pmecc_readl(host->pmecc, cfg)) +		& ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + +	chip->read_buf(mtd, buf, eccsize); +	chip->read_buf(mtd, oob, mtd->oobsize); + +	while (--timeout) { +		if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to read PMECC page\n"); +		return -1; +	} + +	stat = pmecc_readl(host->pmecc, isr); +	if (stat != 0) +		if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0) +			return -EIO; + +	return 0; +} + +static void atmel_nand_pmecc_write_page(struct mtd_info *mtd, +		struct nand_chip *chip, const uint8_t *buf) +{ +	struct atmel_nand_host *host = chip->priv; +	uint32_t *eccpos = chip->ecc.layout->eccpos; +	int i, j; +	int timeout = PMECC_MAX_TIMEOUT_US; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + +	pmecc_writel(host->pmecc, cfg, (pmecc_readl(host->pmecc, cfg) | +		PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DATA); + +	chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + +	while (--timeout) { +		if (!(pmecc_readl(host->pmecc, sr) & PMECC_SR_BUSY)) +			break; +		WATCHDOG_RESET(); +		udelay(1); +	} + +	if (!timeout) { +		printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); +		return; +	} + +	for (i = 0; i < host->pmecc_sector_number; i++) { +		for (j = 0; j < host->pmecc_bytes_per_sector; j++) { +			int pos; + +			pos = i * host->pmecc_bytes_per_sector + j; +			chip->oob_poi[eccpos[pos]] = +				readb(&host->pmecc->ecc_port[i].ecc[j]); +		} +	} +	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); +} + +static void atmel_pmecc_core_init(struct mtd_info *mtd) +{ +	struct nand_chip *nand_chip = mtd->priv; +	struct atmel_nand_host *host = nand_chip->priv; +	uint32_t val = 0; +	struct nand_ecclayout *ecc_layout; + +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_RST); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_DISABLE); + +	switch (host->pmecc_corr_cap) { +	case 2: +		val = PMECC_CFG_BCH_ERR2; +		break; +	case 4: +		val = PMECC_CFG_BCH_ERR4; +		break; +	case 8: +		val = PMECC_CFG_BCH_ERR8; +		break; +	case 12: +		val = PMECC_CFG_BCH_ERR12; +		break; +	case 24: +		val = PMECC_CFG_BCH_ERR24; +		break; +	} + +	if (host->pmecc_sector_size == 512) +		val |= PMECC_CFG_SECTOR512; +	else if (host->pmecc_sector_size == 1024) +		val |= PMECC_CFG_SECTOR1024; + +	switch (host->pmecc_sector_number) { +	case 1: +		val |= PMECC_CFG_PAGE_1SECTOR; +		break; +	case 2: +		val |= PMECC_CFG_PAGE_2SECTORS; +		break; +	case 4: +		val |= PMECC_CFG_PAGE_4SECTORS; +		break; +	case 8: +		val |= PMECC_CFG_PAGE_8SECTORS; +		break; +	} + +	val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE +		| PMECC_CFG_AUTO_DISABLE); +	pmecc_writel(host->pmecc, cfg, val); + +	ecc_layout = nand_chip->ecc.layout; +	pmecc_writel(host->pmecc, sarea, mtd->oobsize - 1); +	pmecc_writel(host->pmecc, saddr, ecc_layout->eccpos[0]); +	pmecc_writel(host->pmecc, eaddr, +			ecc_layout->eccpos[ecc_layout->eccbytes - 1]); +	/* See datasheet about PMECC Clock Control Register */ +	pmecc_writel(host->pmecc, clk, PMECC_CLK_133MHZ); +	pmecc_writel(host->pmecc, idr, 0xff); +	pmecc_writel(host->pmecc, ctrl, PMECC_CTRL_ENABLE); +} + +static int atmel_pmecc_nand_init_params(struct nand_chip *nand, +		struct mtd_info *mtd) +{ +	struct atmel_nand_host *host; +	int cap, sector_size; + +	host = nand->priv = &pmecc_host; + +	nand->ecc.mode = NAND_ECC_HW; +	nand->ecc.calculate = NULL; +	nand->ecc.correct = NULL; +	nand->ecc.hwctl = NULL; + +	cap = host->pmecc_corr_cap = CONFIG_PMECC_CAP; +	sector_size = host->pmecc_sector_size = CONFIG_PMECC_SECTOR_SIZE; +	host->pmecc_index_table_offset = CONFIG_PMECC_INDEX_TABLE_OFFSET; + +	printk(KERN_INFO "Initialize PMECC params, cap: %d, sector: %d\n", +		 cap, sector_size); + +	host->pmecc = (struct pmecc_regs __iomem *) ATMEL_BASE_PMECC; +	host->pmerrloc = (struct pmecc_errloc_regs __iomem *) +			ATMEL_BASE_PMERRLOC; +	host->pmecc_rom_base = (void __iomem *) ATMEL_BASE_ROM; + +	/* ECC is calculated for the whole page (1 step) */ +	nand->ecc.size = mtd->writesize; + +	/* set ECC page size and oob layout */ +	switch (mtd->writesize) { +	case 2048: +	case 4096: +		host->pmecc_degree = PMECC_GF_DIMENSION_13; +		host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; +		host->pmecc_sector_number = mtd->writesize / sector_size; +		host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( +			cap, sector_size); +		host->pmecc_alpha_to = pmecc_get_alpha_to(host); +		host->pmecc_index_of = host->pmecc_rom_base + +			host->pmecc_index_table_offset; + +		nand->ecc.steps = 1; +		nand->ecc.bytes = host->pmecc_bytes_per_sector * +				       host->pmecc_sector_number; +		if (nand->ecc.bytes > mtd->oobsize - 2) { +			printk(KERN_ERR "No room for ECC bytes\n"); +			return -EINVAL; +		} +		pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, +					mtd->oobsize, +					nand->ecc.bytes); +		nand->ecc.layout = &atmel_pmecc_oobinfo; +		break; +	case 512: +	case 1024: +		/* TODO */ +		printk(KERN_ERR "Unsupported page size for PMECC, use Software ECC\n"); +	default: +		/* page size not handled by HW ECC */ +		/* switching back to soft ECC */ +		nand->ecc.mode = NAND_ECC_SOFT; +		nand->ecc.read_page = NULL; +		nand->ecc.postpad = 0; +		nand->ecc.prepad = 0; +		nand->ecc.bytes = 0; +		return 0; +	} + +	nand->ecc.read_page = atmel_nand_pmecc_read_page; +	nand->ecc.write_page = atmel_nand_pmecc_write_page; + +	atmel_pmecc_core_init(mtd); + +	return 0; +} + +#else +  /* oob layout for large page size   * bad block info is on bytes 0 and 1   * the bytes have to be consecutives to avoid @@ -285,7 +957,9 @@ int atmel_hwecc_nand_init_param(struct nand_chip *nand, struct mtd_info *mtd)  	return 0;  } -#endif +#endif /* CONFIG_ATMEL_NAND_HW_PMECC */ + +#endif /* CONFIG_ATMEL_NAND_HWECC */  static void at91_nand_hwcontrol(struct mtd_info *mtd,  					 int cmd, unsigned int ctrl) @@ -350,7 +1024,11 @@ int atmel_nand_chip_init(int devnum, ulong base_addr)  		return ret;  #ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC +	ret = atmel_pmecc_nand_init_params(nand, mtd); +#else  	ret = atmel_hwecc_nand_init_param(nand, mtd); +#endif  	if (ret)  		return ret;  #endif |