diff options
Diffstat (limited to 'drivers/mtd/nand')
| -rw-r--r-- | drivers/mtd/nand/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 40 | ||||
| -rw-r--r-- | drivers/mtd/nand/fsl_ifc_nand.c | 99 | ||||
| -rw-r--r-- | drivers/mtd/nand/fsl_ifc_spl.c | 7 | ||||
| -rw-r--r-- | drivers/mtd/nand/omap_elm.c | 196 | ||||
| -rw-r--r-- | drivers/mtd/nand/omap_gpmc.c | 321 | 
6 files changed, 518 insertions, 146 deletions
| diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index eb1eafaf0..e145cd184 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o  obj-$(CONFIG_NAND_SPEAR) += spr_nand.o  obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o  obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o +obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o  obj-$(CONFIG_NAND_PLAT) += nand_plat.o  obj-$(CONFIG_NAND_DOCG4) += docg4.o diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index da83f06e4..16b7df0f7 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -412,7 +412,7 @@ static int pmecc_err_location(struct mtd_info *mtd)  	}  	if (!timeout) { -		printk(KERN_ERR "atmel_nand : Timeout to calculate PMECC error location\n"); +		dev_err(host->dev, "atmel_nand : Timeout to calculate PMECC error location\n");  		return -1;  	} @@ -452,7 +452,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,  			*(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", +			dev_dbg(host->dev, "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 */ @@ -462,7 +462,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,  			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", +			dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",  				pos, bit_pos, err_byte, ecc[tmp]);  		} @@ -500,7 +500,7 @@ normal_check:  			err_nbr = pmecc_err_location(mtd);  			if (err_nbr == -1) { -				printk(KERN_ERR "PMECC: Too many errors\n"); +				dev_err(host->dev, "PMECC: Too many errors\n");  				mtd->ecc_stats.failed++;  				return -EIO;  			} else { @@ -544,7 +544,7 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,  	}  	if (!timeout) { -		printk(KERN_ERR "atmel_nand : Timeout to read PMECC page\n"); +		dev_err(host->dev, "atmel_nand : Timeout to read PMECC page\n");  		return -1;  	} @@ -584,7 +584,7 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,  	}  	if (!timeout) { -		printk(KERN_ERR "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n"); +		dev_err(host->dev, "atmel_nand : Timeout to read PMECC status, fail to write PMECC in oob\n");  		goto out;  	} @@ -827,6 +827,7 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  	switch (mtd->writesize) {  	case 2048:  	case 4096: +	case 8192:  		host->pmecc_degree = (sector_size == 512) ?  			PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;  		host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; @@ -840,8 +841,15 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  		nand->ecc.steps = 1;  		nand->ecc.bytes = host->pmecc_bytes_per_sector *  				       host->pmecc_sector_number; + +		if (nand->ecc.bytes > MTD_MAX_ECCPOS_ENTRIES_LARGE) { +			dev_err(host->dev, "too large eccpos entries. max support ecc.bytes is %d\n", +					MTD_MAX_ECCPOS_ENTRIES_LARGE); +			return -EINVAL; +		} +  		if (nand->ecc.bytes > mtd->oobsize - 2) { -			printk(KERN_ERR "No room for ECC bytes\n"); +			dev_err(host->dev, "No room for ECC bytes\n");  			return -EINVAL;  		}  		pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, @@ -852,7 +860,7 @@ static int atmel_pmecc_nand_init_params(struct nand_chip *nand,  	case 512:  	case 1024:  		/* TODO */ -		printk(KERN_ERR "Unsupported page size for PMECC, use Software ECC\n"); +		dev_err(host->dev, "Unsupported page size for PMECC, use Software ECC\n");  	default:  		/* page size not handled by HW ECC */  		/* switching back to soft ECC */ @@ -1035,7 +1043,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,  		/* it doesn't seems to be a freshly  		 * erased block.  		 * We can't correct so many errors */ -		printk(KERN_WARNING "atmel_nand : multiple errors detected." +		dev_warn(host->dev, "atmel_nand : multiple errors detected."  				" Unable to correct.\n");  		return -EIO;  	} @@ -1045,12 +1053,12 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,  		/* there's nothing much to do here.  		 * the bit error is on the ECC itself.  		 */ -		printk(KERN_WARNING "atmel_nand : one bit error on ECC code." +		dev_warn(host->dev, "atmel_nand : one bit error on ECC code."  				" Nothing to correct\n");  		return 0;  	} -	printk(KERN_WARNING "atmel_nand : one bit error on data." +	dev_warn(host->dev, "atmel_nand : one bit error on data."  			" (word offset in the page :"  			" 0x%x bit offset : 0x%x)\n",  			ecc_word, ecc_bit); @@ -1062,7 +1070,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,  		/* 8 bits words */  		dat[ecc_word] ^= (1 << ecc_bit);  	} -	printk(KERN_WARNING "atmel_nand : error corrected\n"); +	dev_warn(host->dev, "atmel_nand : error corrected\n");  	return 1;  } @@ -1178,7 +1186,11 @@ int atmel_nand_chip_init(int devnum, ulong base_addr)  	mtd->priv = nand;  	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr; +#ifdef CONFIG_NAND_ECC_BCH +	nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else  	nand->ecc.mode = NAND_ECC_SOFT; +#endif  #ifdef CONFIG_SYS_NAND_DBW_16  	nand->options = NAND_BUSWIDTH_16;  #endif @@ -1186,7 +1198,7 @@ int atmel_nand_chip_init(int devnum, ulong base_addr)  #ifdef CONFIG_SYS_NAND_READY_PIN  	nand->dev_ready = at91_nand_ready;  #endif -	nand->chip_delay = 20; +	nand->chip_delay = 75;  	ret = nand_scan_ident(mtd, CONFIG_SYS_NAND_MAX_CHIPS, NULL);  	if (ret) @@ -1214,6 +1226,6 @@ void board_nand_init(void)  	int i;  	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)  		if (atmel_nand_chip_init(i, base_addr[i])) -			printk(KERN_ERR "atmel_nand: Fail to initialize #%d chip", +			dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip",  				i);  } diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 98a09c064..49b63af85 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -125,6 +125,69 @@ static struct nand_ecclayout oob_4096_ecc8 = {  	.oobfree = { {2, 6}, {136, 82} },  }; +/* 8192-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_8192_ecc4 = { +	.eccbytes = 128, +	.eccpos = { +		8, 9, 10, 11, 12, 13, 14, 15, +		16, 17, 18, 19, 20, 21, 22, 23, +		24, 25, 26, 27, 28, 29, 30, 31, +		32, 33, 34, 35, 36, 37, 38, 39, +		40, 41, 42, 43, 44, 45, 46, 47, +		48, 49, 50, 51, 52, 53, 54, 55, +		56, 57, 58, 59, 60, 61, 62, 63, +		64, 65, 66, 67, 68, 69, 70, 71, +		72, 73, 74, 75, 76, 77, 78, 79, +		80, 81, 82, 83, 84, 85, 86, 87, +		88, 89, 90, 91, 92, 93, 94, 95, +		96, 97, 98, 99, 100, 101, 102, 103, +		104, 105, 106, 107, 108, 109, 110, 111, +		112, 113, 114, 115, 116, 117, 118, 119, +		120, 121, 122, 123, 124, 125, 126, 127, +		128, 129, 130, 131, 132, 133, 134, 135, +	}, +	.oobfree = { {2, 6}, {136, 208} }, +}; + +/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_8192_ecc8 = { +	.eccbytes = 256, +	.eccpos = { +		8, 9, 10, 11, 12, 13, 14, 15, +		16, 17, 18, 19, 20, 21, 22, 23, +		24, 25, 26, 27, 28, 29, 30, 31, +		32, 33, 34, 35, 36, 37, 38, 39, +		40, 41, 42, 43, 44, 45, 46, 47, +		48, 49, 50, 51, 52, 53, 54, 55, +		56, 57, 58, 59, 60, 61, 62, 63, +		64, 65, 66, 67, 68, 69, 70, 71, +		72, 73, 74, 75, 76, 77, 78, 79, +		80, 81, 82, 83, 84, 85, 86, 87, +		88, 89, 90, 91, 92, 93, 94, 95, +		96, 97, 98, 99, 100, 101, 102, 103, +		104, 105, 106, 107, 108, 109, 110, 111, +		112, 113, 114, 115, 116, 117, 118, 119, +		120, 121, 122, 123, 124, 125, 126, 127, +		128, 129, 130, 131, 132, 133, 134, 135, +		136, 137, 138, 139, 140, 141, 142, 143, +		144, 145, 146, 147, 148, 149, 150, 151, +		152, 153, 154, 155, 156, 157, 158, 159, +		160, 161, 162, 163, 164, 165, 166, 167, +		168, 169, 170, 171, 172, 173, 174, 175, +		176, 177, 178, 179, 180, 181, 182, 183, +		184, 185, 186, 187, 188, 189, 190, 191, +		192, 193, 194, 195, 196, 197, 198, 199, +		200, 201, 202, 203, 204, 205, 206, 207, +		208, 209, 210, 211, 212, 213, 214, 215, +		216, 217, 218, 219, 220, 221, 222, 223, +		224, 225, 226, 227, 228, 229, 230, 231, +		232, 233, 234, 235, 236, 237, 238, 239, +		240, 241, 242, 243, 244, 245, 246, 247, +		248, 249, 250, 251, 252, 253, 254, 255, +		256, 257, 258, 259, 260, 261, 262, 263, +	}, +	.oobfree = { {2, 6}, {264, 80} }, +};  /*   * Generic flash bbt descriptors @@ -428,20 +491,27 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,  		if (mtd->writesize > 512) {  			nand_fcr0 =  				(NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | -				(NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); +				(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | +				(NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT);  			out_be32(&ifc->ifc_nand.nand_fir0,  				 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) |  				 (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) |  				 (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) |  				 (IFC_FIR_OP_WBCD  << IFC_NAND_FIR0_OP3_SHIFT) | -				 (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT)); -			out_be32(&ifc->ifc_nand.nand_fir1, 0); +				 (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT)); +			out_be32(&ifc->ifc_nand.nand_fir1, +				 (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | +				 (IFC_FIR_OP_RDSTAT << +					IFC_NAND_FIR1_OP6_SHIFT) | +				 (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT));  		} else {  			nand_fcr0 = ((NAND_CMD_PAGEPROG <<  					IFC_NAND_FCR0_CMD1_SHIFT) |  				    (NAND_CMD_SEQIN << -					IFC_NAND_FCR0_CMD2_SHIFT)); +					IFC_NAND_FCR0_CMD2_SHIFT) | +				    (NAND_CMD_STATUS << +					IFC_NAND_FCR0_CMD3_SHIFT));  			out_be32(&ifc->ifc_nand.nand_fir0,  				 (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | @@ -450,7 +520,11 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command,  				 (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) |  				 (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT));  			out_be32(&ifc->ifc_nand.nand_fir1, -				 (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT)); +				 (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | +				 (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | +				 (IFC_FIR_OP_RDSTAT << +					IFC_NAND_FIR1_OP7_SHIFT) | +				 (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT));  			if (column >= mtd->writesize)  				nand_fcr0 |= @@ -902,6 +976,21 @@ static int fsl_ifc_chip_init(int devnum, u8 *addr)  		priv->bufnum_mask = 1;  		break; +	case CSOR_NAND_PGS_8K: +		if ((csor & CSOR_NAND_ECC_MODE_MASK) == +		    CSOR_NAND_ECC_MODE_4) { +			layout = &oob_8192_ecc4; +			nand->ecc.strength = 4; +		} else { +			layout = &oob_8192_ecc8; +			nand->ecc.strength = 8; +			nand->ecc.bytes = 16; +		} + +		priv->bufnum_mask = 0; +		break; + +  	default:  		printf("ifc nand: bad csor %#x: bad page size\n", csor);  		return -ENODEV; diff --git a/drivers/mtd/nand/fsl_ifc_spl.c b/drivers/mtd/nand/fsl_ifc_spl.c index d4622653f..9fa5ccbc5 100644 --- a/drivers/mtd/nand/fsl_ifc_spl.c +++ b/drivers/mtd/nand/fsl_ifc_spl.c @@ -112,10 +112,13 @@ static void nand_load(unsigned int offs, int uboot_size, uchar *dst)  	port_size = (cspr & CSPR_PORT_SIZE_16) ? 16 : 8; -	if (csor & CSOR_NAND_PGS_4K) { +	if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_8K) { +		page_size = 8192; +		bufnum_mask = 0x0; +	} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_4K) {  		page_size = 4096;  		bufnum_mask = 0x1; -	} else if (csor & CSOR_NAND_PGS_2K) { +	} else if ((csor & CSOR_NAND_PGS_MASK) == CSOR_NAND_PGS_2K) {  		page_size = 2048;  		bufnum_mask = 0x3;  	} else { diff --git a/drivers/mtd/nand/omap_elm.c b/drivers/mtd/nand/omap_elm.c new file mode 100644 index 000000000..2aa7807f3 --- /dev/null +++ b/drivers/mtd/nand/omap_elm.c @@ -0,0 +1,196 @@ +/* + * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com> + * Mansoor Ahamed <mansoor.ahamed@ti.com> + * + * BCH Error Location Module (ELM) support. + * + * NOTE: + * 1. Supports only continuous mode. Dont see need for page mode in uboot + * 2. Supports only syndrome polynomial 0. i.e. poly local variable is + *    always set to ELM_DEFAULT_POLY. Dont see need for other polynomial + *    sets in uboot + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/cpu.h> +#include <asm/omap_gpmc.h> +#include <asm/omap_elm.h> + +#define ELM_DEFAULT_POLY (0) + +struct elm *elm_cfg; + +/** + * elm_load_syndromes - Load BCH syndromes based on nibble selection + * @syndrome: BCH syndrome + * @nibbles: + * @poly: Syndrome Polynomial set to use + * + * Load BCH syndromes based on nibble selection + */ +static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly) +{ +	u32 *ptr; +	u32 val; + +	/* reg 0 */ +	ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; +	val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | +				(syndrome[3] << 24); +	writel(val, ptr); +	/* reg 1 */ +	ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; +	val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | +				(syndrome[7] << 24); +	writel(val, ptr); + +	/* BCH 8-bit with 26 nibbles (4*8=32) */ +	if (nibbles > 13) { +		/* reg 2 */ +		ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; +		val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | +				(syndrome[11] << 24); +		writel(val, ptr); +		/* reg 3 */ +		ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; +		val = syndrome[12] | (syndrome[13] << 8) | +			(syndrome[14] << 16) | (syndrome[15] << 24); +		writel(val, ptr); +	} + +	/* BCH 16-bit with 52 nibbles (7*8=56) */ +	if (nibbles > 26) { +		/* reg 4 */ +		ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; +		val = syndrome[16] | (syndrome[17] << 8) | +			(syndrome[18] << 16) | (syndrome[19] << 24); +		writel(val, ptr); + +		/* reg 5 */ +		ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; +		val = syndrome[20] | (syndrome[21] << 8) | +			(syndrome[22] << 16) | (syndrome[23] << 24); +		writel(val, ptr); + +		/* reg 6 */ +		ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; +		val = syndrome[24] | (syndrome[25] << 8) | +			(syndrome[26] << 16) | (syndrome[27] << 24); +		writel(val, ptr); +	} +} + +/** + * elm_check_errors - Check for BCH errors and return error locations + * @syndrome: BCH syndrome + * @nibbles: + * @error_count: Returns number of errrors in the syndrome + * @error_locations: Returns error locations (in decimal) in this array + * + * Check the provided syndrome for BCH errors and return error count + * and locations in the array passed. Returns -1 if error is not correctable, + * else returns 0 + */ +int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count, +		u32 *error_locations) +{ +	u8 poly = ELM_DEFAULT_POLY; +	s8 i; +	u32 location_status; + +	elm_load_syndromes(syndrome, nibbles, poly); + +	/* start processing */ +	writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) +				| ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), +		&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); + +	/* wait for processing to complete */ +	while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) +		; +	/* clear status */ +	writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), +			&elm_cfg->irqstatus); + +	/* check if correctable */ +	location_status = readl(&elm_cfg->error_location[poly].location_status); +	if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) +		return -1; + +	/* get error count */ +	*error_count = readl(&elm_cfg->error_location[poly].location_status) & +					ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; + +	for (i = 0; i < *error_count; i++) { +		error_locations[i] = +		     readl(&elm_cfg->error_location[poly].error_location_x[i]); +	} + +	return 0; +} + + +/** + * elm_config - Configure ELM module + * @level: 4 / 8 / 16 bit BCH + * + * Configure ELM module based on BCH level. + * Set mode as continuous mode. + * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. + * Also, the mode is set only for syndrome 0 + */ +int elm_config(enum bch_level level) +{ +	u32 val; +	u8 poly = ELM_DEFAULT_POLY; +	u32 buffer_size = 0x7FF; + +	/* config size and level */ +	val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; +	val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & +				ELM_LOCATION_CONFIG_ECC_SIZE_MASK); +	writel(val, &elm_cfg->location_config); + +	/* config continous mode */ +	/* enable interrupt generation for syndrome polynomial set */ +	writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), +			&elm_cfg->irqenable); +	/* set continuous mode for the syndrome polynomial set */ +	writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), +			&elm_cfg->page_ctrl); + +	return 0; +} + +/** + * elm_reset - Do a soft reset of ELM + * + * Perform a soft reset of ELM and return after reset is done. + */ +void elm_reset(void) +{ +	/* initiate reset */ +	writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), +			&elm_cfg->sysconfig); + +	/* wait for reset complete and normal operation */ +	while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != +		ELM_SYSSTATUS_RESETDONE) +		; +} + +/** + * elm_init - Initialize ELM module + * + * Initialize ELM support. Currently it does only base address init + * and ELM reset. + */ +void elm_init(void) +{ +	elm_cfg = (struct elm *)ELM_BASE; +	elm_reset(); +} diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index ec1787f22..5e7e6b337 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -15,15 +15,13 @@  #include <linux/bch.h>  #include <linux/compiler.h>  #include <nand.h> -#ifdef CONFIG_AM33XX -#include <asm/arch/elm.h> -#endif +#include <asm/omap_elm.h> + +#define BADBLOCK_MARKER_LENGTH	2 +#define SECTOR_BYTES		512  static uint8_t cs; -static __maybe_unused struct nand_ecclayout hw_nand_oob = -	GPMC_NAND_HW_ECC_LAYOUT; -static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob = -	GPMC_NAND_HW_BCH8_ECC_LAYOUT; +static __maybe_unused struct nand_ecclayout omap_ecclayout;  /*   * omap_nand_hwcontrol - Set the address pointers corretly for the @@ -233,6 +231,7 @@ struct nand_bch_priv {  	uint8_t type;  	uint8_t nibbles;  	struct bch_control *control; +	enum omap_ecc ecc_scheme;  };  /* bch types */ @@ -274,17 +273,15 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)  {  	uint32_t val;  	uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; -#ifdef CONFIG_AM33XX  	uint32_t unused_length = 0; -#endif  	uint32_t wr_mode = BCH_WRAPMODE_6;  	struct nand_bch_priv *bch = chip->priv;  	/* Clear the ecc result registers, select ecc reg as 1 */  	writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); -#ifdef CONFIG_AM33XX -	wr_mode = BCH_WRAPMODE_1; +	if (bch->ecc_scheme == OMAP_ECC_BCH8_CODE_HW) { +		wr_mode = BCH_WRAPMODE_1;  	switch (bch->nibbles) {  	case ECC_BCH4_NIBBLES: @@ -320,7 +317,7 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)  		val |= (unused_length << 22);  		break;  	} -#else +	} else {  	/*  	 * This ecc_size_config setting is for BCH sw library.  	 * @@ -333,7 +330,7 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)  	 *  size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)  	 */  	val = (32 << 22) | (0 << 12); -#endif +	}  	/* ecc size configuration */  	writel(val, &gpmc_cfg->ecc_size_config); @@ -376,9 +373,9 @@ static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd)  }  /* - * BCH8 support (needs ELM and thus AM33xx-only) + * BCH support using ELM module   */ -#ifdef CONFIG_AM33XX +#ifdef CONFIG_NAND_OMAP_ELM  /*   * omap_read_bch8_result - Read BCH result for BCH8 level   * @@ -631,20 +628,20 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,  	}  	return 0;  } -#endif /* CONFIG_AM33XX */ +#endif /* CONFIG_NAND_OMAP_ELM */  /*   * OMAP3 BCH8 support (with BCH library)   */ -#ifdef CONFIG_NAND_OMAP_BCH8 +#ifdef CONFIG_BCH  /* - *  omap_calculate_ecc_bch - Read BCH ECC result + *  omap_calculate_ecc_bch_sw - Read BCH ECC result   *   *  @mtd:	MTD device structure   *  @dat:	The pointer to data on which ecc is computed (unused here)   *  @ecc:	The ECC output buffer   */ -static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat, +static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, const uint8_t *dat,  				uint8_t *ecc)  {  	int ret = 0; @@ -689,13 +686,13 @@ static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,  }  /** - * omap_correct_data_bch - Decode received data and correct errors + * omap_correct_data_bch_sw - Decode received data and correct errors   * @mtd: MTD device structure   * @data: page data   * @read_ecc: ecc read from nand flash   * @calc_ecc: ecc read from HW ECC registers   */ -static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data, +static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,  				 u_char *read_ecc, u_char *calc_ecc)  {  	int i, count; @@ -752,7 +749,150 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd)  		chip_priv->control = NULL;  	}  } -#endif /* CONFIG_NAND_OMAP_BCH8 */ +#endif /* CONFIG_BCH */ + +/** + * omap_select_ecc_scheme - configures driver for particular ecc-scheme + * @nand: NAND chip device structure + * @ecc_scheme: ecc scheme to configure + * @pagesize: number of main-area bytes per page of NAND device + * @oobsize: number of OOB/spare bytes per page of NAND device + */ +static int omap_select_ecc_scheme(struct nand_chip *nand, +	enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { +	struct nand_bch_priv	*bch		= nand->priv; +	struct nand_ecclayout	*ecclayout	= nand->ecc.layout; +	int eccsteps = pagesize / SECTOR_BYTES; +	int i; + +	switch (ecc_scheme) { +	case OMAP_ECC_HAM1_CODE_SW: +		debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n"); +		/* For this ecc-scheme, ecc.bytes, ecc.layout, ... are +		 * initialized in nand_scan_tail(), so just set ecc.mode */ +		bch_priv.control	= NULL; +		bch_priv.type		= 0; +		nand->ecc.mode		= NAND_ECC_SOFT; +		nand->ecc.layout	= NULL; +		nand->ecc.size		= pagesize; +		bch->ecc_scheme		= OMAP_ECC_HAM1_CODE_SW; +		break; + +	case OMAP_ECC_HAM1_CODE_HW: +		debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n"); +		/* check ecc-scheme requirements before updating ecc info */ +		if ((3 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { +			printf("nand: error: insufficient OOB: require=%d\n", ( +				(3 * eccsteps) + BADBLOCK_MARKER_LENGTH)); +			return -EINVAL; +		} +		bch_priv.control	= NULL; +		bch_priv.type		= 0; +		/* populate ecc specific fields */ +		nand->ecc.mode		= NAND_ECC_HW; +		nand->ecc.strength	= 1; +		nand->ecc.size		= SECTOR_BYTES; +		nand->ecc.bytes		= 3; +		nand->ecc.hwctl		= omap_enable_hwecc; +		nand->ecc.correct	= omap_correct_data; +		nand->ecc.calculate	= omap_calculate_ecc; +		/* define ecc-layout */ +		ecclayout->eccbytes	= nand->ecc.bytes * eccsteps; +		for (i = 0; i < ecclayout->eccbytes; i++) +			ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - +						BADBLOCK_MARKER_LENGTH; +		bch->ecc_scheme		= OMAP_ECC_HAM1_CODE_HW; +		break; + +	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_BCH +		debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); +		/* check ecc-scheme requirements before updating ecc info */ +		if ((13 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { +			printf("nand: error: insufficient OOB: require=%d\n", ( +				(13 * eccsteps) + BADBLOCK_MARKER_LENGTH)); +			return -EINVAL; +		} +		/* check if BCH S/W library can be used for error detection */ +		bch_priv.control = init_bch(13, 8, 0x201b); +		if (!bch_priv.control) { +			printf("nand: error: could not init_bch()\n"); +			return -ENODEV; +		} +		bch_priv.type = ECC_BCH8; +		/* populate ecc specific fields */ +		nand->ecc.mode		= NAND_ECC_HW; +		nand->ecc.strength	= 8; +		nand->ecc.size		= SECTOR_BYTES; +		nand->ecc.bytes		= 13; +		nand->ecc.hwctl		= omap_enable_ecc_bch; +		nand->ecc.correct	= omap_correct_data_bch_sw; +		nand->ecc.calculate	= omap_calculate_ecc_bch_sw; +		/* define ecc-layout */ +		ecclayout->eccbytes	= nand->ecc.bytes * eccsteps; +		ecclayout->eccpos[0]	= BADBLOCK_MARKER_LENGTH; +		for (i = 1; i < ecclayout->eccbytes; i++) { +			if (i % nand->ecc.bytes) +				ecclayout->eccpos[i] = +						ecclayout->eccpos[i - 1] + 1; +			else +				ecclayout->eccpos[i] = +						ecclayout->eccpos[i - 1] + 2; +		} +		ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - +						BADBLOCK_MARKER_LENGTH; +		omap_hwecc_init_bch(nand, NAND_ECC_READ); +		bch->ecc_scheme		= OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; +		break; +#else +		printf("nand: error: CONFIG_BCH required for ECC\n"); +		return -EINVAL; +#endif + +	case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_NAND_OMAP_ELM +		debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n"); +		/* check ecc-scheme requirements before updating ecc info */ +		if ((14 * eccsteps) + BADBLOCK_MARKER_LENGTH > oobsize) { +			printf("nand: error: insufficient OOB: require=%d\n", ( +				(14 * eccsteps) + BADBLOCK_MARKER_LENGTH)); +			return -EINVAL; +		} +		/* intialize ELM for ECC error detection */ +		elm_init(); +		bch_priv.type		= ECC_BCH8; +		/* populate ecc specific fields */ +		nand->ecc.mode		= NAND_ECC_HW; +		nand->ecc.strength	= 8; +		nand->ecc.size		= SECTOR_BYTES; +		nand->ecc.bytes		= 14; +		nand->ecc.hwctl		= omap_enable_ecc_bch; +		nand->ecc.correct	= omap_correct_data_bch; +		nand->ecc.calculate	= omap_calculate_ecc_bch; +		nand->ecc.read_page	= omap_read_page_bch; +		/* define ecc-layout */ +		ecclayout->eccbytes	= nand->ecc.bytes * eccsteps; +		for (i = 0; i < ecclayout->eccbytes; i++) +			ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - +						BADBLOCK_MARKER_LENGTH; +		bch->ecc_scheme		= OMAP_ECC_BCH8_CODE_HW; +		break; +#else +		printf("nand: error: CONFIG_NAND_OMAP_ELM required for ECC\n"); +		return -EINVAL; +#endif + +	default: +		debug("nand: error: ecc scheme not enabled or supported\n"); +		return -EINVAL; +	} +	return 0; +}  #ifndef CONFIG_SPL_BUILD  /* @@ -763,77 +903,45 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd)   * @eccstrength		- the number of bits that could be corrected   *			  (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)   */ -void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength) +int __maybe_unused omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)  {  	struct nand_chip *nand;  	struct mtd_info *mtd; +	int err = 0;  	if (nand_curr_device < 0 ||  	    nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||  	    !nand_info[nand_curr_device].name) { -		printf("Error: Can't switch ecc, no devices available\n"); -		return; +		printf("nand: error: no NAND devices found\n"); +		return -ENODEV;  	}  	mtd = &nand_info[nand_curr_device];  	nand = mtd->priv; -  	nand->options |= NAND_OWN_BUFFERS; - -	/* Reset ecc interface */ -	nand->ecc.mode = NAND_ECC_NONE; -	nand->ecc.read_page = NULL; -	nand->ecc.write_page = NULL; -	nand->ecc.read_oob = NULL; -	nand->ecc.write_oob = NULL; -	nand->ecc.hwctl = NULL; -	nand->ecc.correct = NULL; -	nand->ecc.calculate = NULL; -	nand->ecc.strength = eccstrength; -  	/* Setup the ecc configurations again */  	if (hardware) {  		if (eccstrength == 1) { -			nand->ecc.mode = NAND_ECC_HW; -			nand->ecc.layout = &hw_nand_oob; -			nand->ecc.size = 512; -			nand->ecc.bytes = 3; -			nand->ecc.hwctl = omap_enable_hwecc; -			nand->ecc.correct = omap_correct_data; -			nand->ecc.calculate = omap_calculate_ecc; -			omap_hwecc_init(nand); -			printf("1-bit hamming HW ECC selected\n"); -		} -#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) -		else if (eccstrength == 8) { -			nand->ecc.mode = NAND_ECC_HW; -			nand->ecc.layout = &hw_bch8_nand_oob; -			nand->ecc.size = 512; -#ifdef CONFIG_AM33XX -			nand->ecc.bytes = 14; -			nand->ecc.read_page = omap_read_page_bch; -#else -			nand->ecc.bytes = 13; -#endif -			nand->ecc.hwctl = omap_enable_ecc_bch; -			nand->ecc.correct = omap_correct_data_bch; -			nand->ecc.calculate = omap_calculate_ecc_bch; -			omap_hwecc_init_bch(nand, NAND_ECC_READ); -			printf("8-bit BCH HW ECC selected\n"); +			err = omap_select_ecc_scheme(nand, +					OMAP_ECC_HAM1_CODE_HW, +					mtd->writesize, mtd->oobsize); +		} else if (eccstrength == 8) { +			err = omap_select_ecc_scheme(nand, +					OMAP_ECC_BCH8_CODE_HW, +					mtd->writesize, mtd->oobsize); +		} else { +			printf("nand: error: unsupported ECC scheme\n"); +			return -EINVAL;  		} -#endif  	} else { -		nand->ecc.mode = NAND_ECC_SOFT; -		/* Use mtd default settings */ -		nand->ecc.layout = NULL; -		nand->ecc.size = 0; -		printf("SW ECC selected\n"); +		err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, +					mtd->writesize, mtd->oobsize);  	}  	/* Update NAND handling after ECC mode switch */ -	nand_scan_tail(mtd); - -	nand->options &= ~NAND_OWN_BUFFERS; +	if (!err) +		err = nand_scan_tail(mtd); +	return err;  }  #endif /* CONFIG_SPL_BUILD */ @@ -856,7 +964,7 @@ int board_nand_init(struct nand_chip *nand)  {  	int32_t gpmc_config = 0;  	cs = 0; - +	int err = 0;  	/*  	 * xloader/Uboot's gpmc configuration would have configured GPMC for  	 * nand type of memory. The following logic scans and latches on to the @@ -873,7 +981,7 @@ int board_nand_init(struct nand_chip *nand)  		cs++;  	}  	if (cs >= GPMC_MAX_CS) { -		printf("NAND: Unable to find NAND settings in " +		printf("nand: error: Unable to find NAND settings in "  			"GPMC Configuration - quitting\n");  		return -ENODEV;  	} @@ -885,64 +993,27 @@ int board_nand_init(struct nand_chip *nand)  	nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;  	nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd; - -	nand->cmd_ctrl = omap_nand_hwcontrol; -	nand->options = NAND_NO_PADDING | NAND_CACHEPRG; +	nand->priv	= &bch_priv; +	nand->cmd_ctrl	= omap_nand_hwcontrol; +	nand->options	|= NAND_NO_PADDING | NAND_CACHEPRG;  	/* If we are 16 bit dev, our gpmc config tells us that */  	if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000)  		nand->options |= NAND_BUSWIDTH_16;  	nand->chip_delay = 100; +	nand->ecc.layout = &omap_ecclayout; -#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) -#ifdef CONFIG_AM33XX -	/* AM33xx uses the ELM */ -	/* required in case of BCH */ -	elm_init(); -#else -	/* -	 * Whereas other OMAP based SoC do not have the ELM, they use the BCH -	 * SW library. -	 */ -	bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */); -	if (!bch_priv.control) { -		puts("Could not init_bch()\n"); -		return -ENODEV; -	} -#endif -	/* BCH info that will be correct for SPL or overridden otherwise. */ -	nand->priv = &bch_priv; -#endif - -	/* Default ECC mode */ -#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8) -	nand->ecc.mode = NAND_ECC_HW; -	nand->ecc.layout = &hw_bch8_nand_oob; -	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; -	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; -	nand->ecc.strength = 8; -	nand->ecc.hwctl = omap_enable_ecc_bch; -	nand->ecc.correct = omap_correct_data_bch; -	nand->ecc.calculate = omap_calculate_ecc_bch; -#ifdef CONFIG_AM33XX -	nand->ecc.read_page = omap_read_page_bch; -#endif -	omap_hwecc_init_bch(nand, NAND_ECC_READ); -#else -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC) -	nand->ecc.mode = NAND_ECC_SOFT; +	/* select ECC scheme */ +#if defined(CONFIG_NAND_OMAP_ECCSCHEME) +	err = omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME, +			CONFIG_SYS_NAND_PAGE_SIZE, CONFIG_SYS_NAND_OOBSIZE);  #else -	nand->ecc.mode = NAND_ECC_HW; -	nand->ecc.layout = &hw_nand_oob; -	nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; -	nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; -	nand->ecc.hwctl = omap_enable_hwecc; -	nand->ecc.correct = omap_correct_data; -	nand->ecc.calculate = omap_calculate_ecc; -	nand->ecc.strength = 1; -	omap_hwecc_init(nand); -#endif +	/* pagesize and oobsize are not required to configure sw ecc-scheme */ +	err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW, +			0, 0);  #endif +	if (err) +		return err;  #ifdef CONFIG_SPL_BUILD  	if (nand->options & NAND_BUSWIDTH_16) |