diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/mtd/nand/omap2.c | 161 | 
1 files changed, 62 insertions, 99 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 86ce48b2c47..b6a08b23853 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -139,6 +139,7 @@  #define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */  #define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */ +#define BADBLOCK_MARKER_LENGTH		2  #define OMAP_ECC_BCH8_POLYNOMIAL	0x201b  #ifdef CONFIG_MTD_NAND_OMAP_BCH @@ -149,17 +150,6 @@ static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};  /* oob info generated runtime depending on ecc algorithm and layout selected */  static struct nand_ecclayout omap_oobinfo; -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr bb_descrip_flashbased = { -	.options = NAND_BBT_SCANALLPAGES, -	.offs = 0, -	.len = 1, -	.pattern = scan_ff_pattern, -}; -  struct omap_nand_info {  	struct nand_hw_control		controller; @@ -184,7 +174,6 @@ struct omap_nand_info {  	struct gpmc_nand_regs		reg;  	/* fields specific for BCHx_HW ECC scheme */  	struct bch_control             *bch; -	struct nand_ecclayout           ecclayout;  	bool				is_elm_used;  	struct device			*elm_dev;  	struct device_node		*of_node; @@ -1686,65 +1675,8 @@ static void omap3_free_bch(struct mtd_info *mtd)  	}  } -/** - * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. - * @mtd: MTD device structure - */ -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ -	int i, steps, offset; -	struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, -						   mtd); -	struct nand_ecclayout *layout = &info->ecclayout; - -	/* build oob layout */ -	steps = mtd->writesize/info->nand.ecc.size; -	layout->eccbytes = steps*info->nand.ecc.bytes; - -	/* do not bother creating special oob layouts for small page devices */ -	if (mtd->oobsize < 64) { -		pr_err("BCH ecc is not supported on small page devices\n"); -		goto fail; -	} - -	/* reserve 2 bytes for bad block marker */ -	if (layout->eccbytes+2 > mtd->oobsize) { -		pr_err("no oob layout available for oobsize %d eccbytes %u\n", -		       mtd->oobsize, layout->eccbytes); -		goto fail; -	} - -	/* ECC layout compatible with RBL for BCH8 */ -	if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) -		offset = 2; -	else -		offset = mtd->oobsize - layout->eccbytes; - -	/* put ecc bytes at oob tail */ -	for (i = 0; i < layout->eccbytes; i++) -		layout->eccpos[i] = offset + i; - -	if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) -		layout->oobfree[0].offset = 2 + layout->eccbytes * steps; -	else -		layout->oobfree[0].offset = 2; - -	layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; -	info->nand.ecc.layout = layout; - -	if (!(info->nand.options & NAND_BUSWIDTH_16)) -		info->nand.badblock_pattern = &bb_descrip_flashbased; -	return 0; -fail: -	omap3_free_bch(mtd); -	return -1; -} -  #else -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ -	return -1; -} +  static void omap3_free_bch(struct mtd_info *mtd)  {  } @@ -1756,8 +1688,9 @@ static int omap_nand_probe(struct platform_device *pdev)  	struct omap_nand_platform_data	*pdata;  	struct mtd_info			*mtd;  	struct nand_chip		*nand_chip; +	struct nand_ecclayout		*ecclayout;  	int				err; -	int				i, offset; +	int				i;  	dma_cap_mask_t			mask;  	unsigned			sig;  	struct resource			*res; @@ -1840,6 +1773,13 @@ static int omap_nand_probe(struct platform_device *pdev)  		goto out_release_mem_region;  	} +	/* check for small page devices */ +	if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) { +		pr_err("small page devices are not supported\n"); +		err = -EINVAL; +		goto out_release_mem_region; +	} +  	/* re-populate low-level callbacks based on xfer modes */  	switch (pdata->xfer_type) {  	case NAND_OMAP_PREFETCH_POLLED: @@ -1931,6 +1871,8 @@ static int omap_nand_probe(struct platform_device *pdev)  	}  	/* populate MTD interface based on ECC scheme */ +	nand_chip->ecc.layout	= &omap_oobinfo; +	ecclayout		= &omap_oobinfo;  	switch (pdata->ecc_opt) {  	case OMAP_ECC_HAM1_CODE_HW:  		pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); @@ -1941,6 +1883,16 @@ static int omap_nand_probe(struct platform_device *pdev)  		nand_chip->ecc.calculate        = omap_calculate_ecc;  		nand_chip->ecc.hwctl            = omap_enable_hwecc;  		nand_chip->ecc.correct          = omap_correct_data; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		if (nand_chip->options & NAND_BUSWIDTH_16) +			ecclayout->eccpos[0]	= BADBLOCK_MARKER_LENGTH; +		else +			ecclayout->eccpos[0]	= 1; +		ecclayout->oobfree->offset	= ecclayout->eccpos[0] + +							ecclayout->eccbytes;  		break;  	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: @@ -1953,6 +1905,13 @@ static int omap_nand_probe(struct platform_device *pdev)  		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch;  		nand_chip->ecc.correct		= omap3_correct_data_bch;  		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch4; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		ecclayout->eccpos[0]		= BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree->offset	= ecclayout->eccpos[0] + +							ecclayout->eccbytes;  		/* software bch library is used for locating errors */  		info->bch = init_bch(nand_chip->ecc.bytes,  					nand_chip->ecc.strength, @@ -1981,6 +1940,13 @@ static int omap_nand_probe(struct platform_device *pdev)  		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch;  		nand_chip->ecc.read_page	= omap_read_page_bch;  		nand_chip->ecc.write_page	= omap_write_page_bch; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		ecclayout->eccpos[0]		= BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree->offset	= ecclayout->eccpos[0] + +							ecclayout->eccbytes;  		/* This ECC scheme requires ELM H/W block */  		if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) {  			pr_err("nand: error: could not initialize ELM\n"); @@ -2004,6 +1970,13 @@ static int omap_nand_probe(struct platform_device *pdev)  		nand_chip->ecc.hwctl		= omap3_enable_hwecc_bch;  		nand_chip->ecc.correct		= omap3_correct_data_bch;  		nand_chip->ecc.calculate	= omap3_calculate_ecc_bch8; +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		ecclayout->eccpos[0]		= BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree->offset	= ecclayout->eccpos[0] + +							ecclayout->eccbytes;  		/* software bch library is used for locating errors */  		info->bch = init_bch(nand_chip->ecc.bytes,  					nand_chip->ecc.strength, @@ -2038,6 +2011,13 @@ static int omap_nand_probe(struct platform_device *pdev)  			pr_err("nand: error: could not initialize ELM\n");  			goto out_release_mem_region;  		} +		/* define ECC layout */ +		ecclayout->eccbytes		= nand_chip->ecc.bytes * +							(mtd->writesize / +							nand_chip->ecc.size); +		ecclayout->eccpos[0]		= BADBLOCK_MARKER_LENGTH; +		ecclayout->oobfree->offset	= ecclayout->eccpos[0] + +							ecclayout->eccbytes;  		break;  #else  		pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); @@ -2051,34 +2031,17 @@ static int omap_nand_probe(struct platform_device *pdev)  		goto out_release_mem_region;  	} -	/* rom code layout */ -	if (pdata->ecc_opt == OMAP_ECC_HAM1_CODE_HW) { - -		if (nand_chip->options & NAND_BUSWIDTH_16) { -			offset = 2; -		} else { -			offset = 1; -			nand_chip->badblock_pattern = &bb_descrip_flashbased; -		} -		omap_oobinfo.eccbytes = 3 * (mtd->writesize / 512); -		for (i = 0; i < omap_oobinfo.eccbytes; i++) -			omap_oobinfo.eccpos[i] = i+offset; - -		omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; -		omap_oobinfo.oobfree->length = mtd->oobsize - -					(offset + omap_oobinfo.eccbytes); - -		nand_chip->ecc.layout = &omap_oobinfo; -	} else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || -		   (pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW_DETECTION_SW) || -		   (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW_DETECTION_SW) || -		   (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { -		/* build OOB layout for BCH ECC correction */ -		err = omap3_init_bch_tail(mtd); -		if (err) { -			err = -EINVAL; -			goto out_release_mem_region; -		} +	/* populate remaining ECC layout data */ +	ecclayout->oobfree->length = mtd->oobsize - (BADBLOCK_MARKER_LENGTH + +							ecclayout->eccbytes); +	for (i = 1; i < ecclayout->eccbytes; i++) +		ecclayout->eccpos[i] = ecclayout->eccpos[0] + i; +	/* check if NAND device's OOB is enough to store ECC signatures */ +	if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { +		pr_err("not enough OOB bytes required = %d, available=%d\n", +					   ecclayout->eccbytes, mtd->oobsize); +		err = -EINVAL; +		goto out_release_mem_region;  	}  	/* second phase scan */  |