diff options
Diffstat (limited to 'drivers/mtd/nand/sh_flctl.c')
-rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 115 |
1 files changed, 80 insertions, 35 deletions
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4814402902f9..6fa3bcd59769 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -31,7 +31,6 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_mtd.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sh_dma.h> @@ -43,26 +42,73 @@ #include <linux/mtd/partitions.h> #include <linux/mtd/sh_flctl.h> -static struct nand_ecclayout flctl_4secc_oob_16 = { - .eccbytes = 10, - .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - .oobfree = { - {.offset = 12, - . length = 4} }, +static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section) + return -ERANGE; + + oobregion->offset = 0; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + if (section) + return -ERANGE; + + oobregion->offset = 12; + oobregion->length = 4; + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { + .ecc = flctl_4secc_ooblayout_sp_ecc, + .free = flctl_4secc_ooblayout_sp_free, }; -static struct nand_ecclayout flctl_4secc_oob_64 = { - .eccbytes = 4 * 10, - .eccpos = { - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 }, - .oobfree = { - {.offset = 2, .length = 4}, - {.offset = 16, .length = 6}, - {.offset = 32, .length = 6}, - {.offset = 48, .length = 6} }, +static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = (section * 16) + 6; + oobregion->length = chip->ecc.bytes; + + return 0; +} + +static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *oobregion) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (section >= chip->ecc.steps) + return -ERANGE; + + oobregion->offset = section * 16; + oobregion->length = 6; + + if (!section) { + oobregion->offset += 2; + oobregion->length -= 2; + } + + return 0; +} + +static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { + .ecc = flctl_4secc_ooblayout_lp_ecc, + .free = flctl_4secc_ooblayout_lp_free, }; static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; @@ -987,10 +1033,10 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) if (flctl->hwecc) { if (mtd->writesize == 512) { - chip->ecc.layout = &flctl_4secc_oob_16; + mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); chip->badblock_pattern = &flctl_4secc_smallpage; } else { - chip->ecc.layout = &flctl_4secc_oob_64; + mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); chip->badblock_pattern = &flctl_4secc_largepage; } @@ -1005,6 +1051,7 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) flctl->flcmncr_base |= _4ECCEN; } else { chip->ecc.mode = NAND_ECC_SOFT; + chip->ecc.algo = NAND_ECC_HAMMING; } return 0; @@ -1044,8 +1091,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) const struct of_device_id *match; struct flctl_soc_config *config; struct sh_flctl_platform_data *pdata; - struct device_node *dn = dev->of_node; - int ret; match = of_match_device(of_flctl_match, dev); if (match) @@ -1065,15 +1110,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) pdata->has_hwecc = config->has_hwecc; pdata->use_holden = config->use_holden; - /* parse user defined options */ - ret = of_get_nand_bus_width(dn); - if (ret == 16) - pdata->flcmncr_val |= SEL_16BIT; - else if (ret != 8) { - dev_err(dev, "%s: invalid bus width\n", __func__); - return NULL; - } - return pdata; } @@ -1136,15 +1172,14 @@ static int flctl_probe(struct platform_device *pdev) nand->chip_delay = 20; nand->read_byte = flctl_read_byte; + nand->read_word = flctl_read_word; nand->write_buf = flctl_write_buf; nand->read_buf = flctl_read_buf; nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; - if (pdata->flcmncr_val & SEL_16BIT) { + if (pdata->flcmncr_val & SEL_16BIT) nand->options |= NAND_BUSWIDTH_16; - nand->read_word = flctl_read_word; - } pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); @@ -1155,6 +1190,16 @@ static int flctl_probe(struct platform_device *pdev) if (ret) goto err_chip; + if (nand->options & NAND_BUSWIDTH_16) { + /* + * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). + * Add the SEL_16BIT flag in pdata->flcmncr_val and re-assign + * flctl->flcmncr_base to pdata->flcmncr_val. + */ + pdata->flcmncr_val |= SEL_16BIT; + flctl->flcmncr_base = pdata->flcmncr_val; + } + ret = flctl_chip_init_tail(flctl_mtd); if (ret) goto err_chip; |