Commit e04dbf35 authored by Boris Brezillon's avatar Boris Brezillon

mtd: nand: omap2: switch to mtd_ooblayout_ops

Implementing the mtd_ooblayout_ops interface is the new way of exposing
ECC/OOB layout to MTD users.
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
parent a894cf6c
...@@ -178,8 +178,6 @@ struct omap_nand_info { ...@@ -178,8 +178,6 @@ struct omap_nand_info {
struct gpmc_nand_regs reg; struct gpmc_nand_regs reg;
struct gpmc_nand_ops *ops; struct gpmc_nand_ops *ops;
bool flash_bbt; bool flash_bbt;
/* generated at runtime depending on ECC algorithm and layout selected */
struct nand_ecclayout oobinfo;
/* fields specific for BCHx_HW ECC scheme */ /* fields specific for BCHx_HW ECC scheme */
struct device *elm_dev; struct device *elm_dev;
/* NAND ready gpio */ /* NAND ready gpio */
...@@ -1714,20 +1712,115 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) ...@@ -1714,20 +1712,115 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
return 0; return 0;
} }
static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
int off = BADBLOCK_MARKER_LENGTH;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
off = 1;
if (section)
return -ERANGE;
oobregion->offset = off;
oobregion->length = chip->ecc.total;
return 0;
}
static int omap_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
int off = BADBLOCK_MARKER_LENGTH;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
off = 1;
if (section)
return -ERANGE;
off += chip->ecc.total;
if (off >= mtd->oobsize)
return -ERANGE;
oobregion->offset = off;
oobregion->length = mtd->oobsize - off;
return 0;
}
static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
.ecc = omap_ooblayout_ecc,
.free = omap_ooblayout_free,
};
static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int off = BADBLOCK_MARKER_LENGTH;
if (section >= chip->ecc.steps)
return -ERANGE;
/*
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
oobregion->offset = off + (section * (chip->ecc.bytes + 1));
oobregion->length = chip->ecc.bytes;
return 0;
}
static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int off = BADBLOCK_MARKER_LENGTH;
if (section)
return -ERANGE;
/*
* When SW correction is employed, one OMAP specific marker byte is
* reserved after each ECC step.
*/
off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
if (off >= mtd->oobsize)
return -ERANGE;
oobregion->offset = off;
oobregion->length = mtd->oobsize - off;
return 0;
}
static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
.ecc = omap_sw_ooblayout_ecc,
.free = omap_sw_ooblayout_free,
};
static int omap_nand_probe(struct platform_device *pdev) static int omap_nand_probe(struct platform_device *pdev)
{ {
struct omap_nand_info *info; struct omap_nand_info *info;
struct omap_nand_platform_data *pdata = NULL; struct omap_nand_platform_data *pdata = NULL;
struct mtd_info *mtd; struct mtd_info *mtd;
struct nand_chip *nand_chip; struct nand_chip *nand_chip;
struct nand_ecclayout *ecclayout;
int err; int err;
int i;
dma_cap_mask_t mask; dma_cap_mask_t mask;
unsigned sig; unsigned sig;
unsigned oob_index;
struct resource *res; struct resource *res;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
int min_oobbytes = BADBLOCK_MARKER_LENGTH;
int oobbytes_per_step;
info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
GFP_KERNEL); GFP_KERNEL);
...@@ -1915,7 +2008,7 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1915,7 +2008,7 @@ static int omap_nand_probe(struct platform_device *pdev)
/* /*
* Bail out earlier to let NAND_ECC_SOFT code create its own * Bail out earlier to let NAND_ECC_SOFT code create its own
* ecclayout instead of using ours. * ooblayout instead of using ours.
*/ */
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
nand_chip->ecc.mode = NAND_ECC_SOFT; nand_chip->ecc.mode = NAND_ECC_SOFT;
...@@ -1923,8 +2016,6 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1923,8 +2016,6 @@ static int omap_nand_probe(struct platform_device *pdev)
} }
/* populate MTD interface based on ECC scheme */ /* populate MTD interface based on ECC scheme */
ecclayout = &info->oobinfo;
nand_chip->ecc.layout = ecclayout;
switch (info->ecc_opt) { switch (info->ecc_opt) {
case OMAP_ECC_HAM1_CODE_HW: case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
...@@ -1935,19 +2026,12 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1935,19 +2026,12 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc; nand_chip->ecc.calculate = omap_calculate_ecc;
nand_chip->ecc.hwctl = omap_enable_hwecc; nand_chip->ecc.hwctl = omap_enable_hwecc;
nand_chip->ecc.correct = omap_correct_data; nand_chip->ecc.correct = omap_correct_data;
/* define ECC layout */ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
ecclayout->eccbytes = nand_chip->ecc.bytes * oobbytes_per_step = nand_chip->ecc.bytes;
(mtd->writesize /
nand_chip->ecc.size); if (!(nand_chip->options & NAND_BUSWIDTH_16))
if (nand_chip->options & NAND_BUSWIDTH_16) min_oobbytes = 1;
oob_index = BADBLOCK_MARKER_LENGTH;
else
oob_index = 1;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* no reserved-marker in ecclayout for this ecc-scheme */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break; break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
...@@ -1959,19 +2043,9 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1959,19 +2043,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data; nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
ecclayout->eccbytes = nand_chip->ecc.bytes * /* Reserve one byte for the OMAP marker */
(mtd->writesize / oobbytes_per_step = nand_chip->ecc.bytes + 1;
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
ecclayout->eccpos[i] = oob_index;
if (((i + 1) % nand_chip->ecc.bytes) == 0)
oob_index++;
}
/* include reserved-marker in ecclayout->oobfree calculation */
ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
/* software bch library is used for locating errors */ /* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd); nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) { if (!nand_chip->ecc.priv) {
...@@ -1993,16 +2067,8 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -1993,16 +2067,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; nand_chip->ecc.write_page = omap_write_page_bch;
/* define ECC layout */ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
ecclayout->eccbytes = nand_chip->ecc.bytes * oobbytes_per_step = nand_chip->ecc.bytes;
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
err = elm_config(info->elm_dev, BCH4_ECC, err = elm_config(info->elm_dev, BCH4_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / nand_chip->ecc.size,
...@@ -2020,19 +2086,9 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2020,19 +2086,9 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
nand_chip->ecc.correct = nand_bch_correct_data; nand_chip->ecc.correct = nand_bch_correct_data;
nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.calculate = omap_calculate_ecc_bch;
/* define ECC layout */ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
ecclayout->eccbytes = nand_chip->ecc.bytes * /* Reserve one byte for the OMAP marker */
(mtd->writesize / oobbytes_per_step = nand_chip->ecc.bytes + 1;
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++) {
ecclayout->eccpos[i] = oob_index;
if (((i + 1) % nand_chip->ecc.bytes) == 0)
oob_index++;
}
/* include reserved-marker in ecclayout->oobfree calculation */
ecclayout->oobfree->offset = 1 +
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
/* software bch library is used for locating errors */ /* software bch library is used for locating errors */
nand_chip->ecc.priv = nand_bch_init(mtd); nand_chip->ecc.priv = nand_bch_init(mtd);
if (!nand_chip->ecc.priv) { if (!nand_chip->ecc.priv) {
...@@ -2054,6 +2110,8 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2054,6 +2110,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; nand_chip->ecc.write_page = omap_write_page_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH8_ECC, err = elm_config(info->elm_dev, BCH8_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / nand_chip->ecc.size,
...@@ -2061,16 +2119,6 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2061,16 +2119,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0) if (err < 0)
goto return_error; goto return_error;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break; break;
case OMAP_ECC_BCH16_CODE_HW: case OMAP_ECC_BCH16_CODE_HW:
...@@ -2084,6 +2132,8 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2084,6 +2132,8 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.calculate = omap_calculate_ecc_bch;
nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.read_page = omap_read_page_bch;
nand_chip->ecc.write_page = omap_write_page_bch; nand_chip->ecc.write_page = omap_write_page_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = nand_chip->ecc.bytes;
err = elm_config(info->elm_dev, BCH16_ECC, err = elm_config(info->elm_dev, BCH16_ECC,
mtd->writesize / nand_chip->ecc.size, mtd->writesize / nand_chip->ecc.size,
...@@ -2091,16 +2141,6 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2091,16 +2141,6 @@ static int omap_nand_probe(struct platform_device *pdev)
if (err < 0) if (err < 0)
goto return_error; goto return_error;
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
nand_chip->ecc.size);
oob_index = BADBLOCK_MARKER_LENGTH;
for (i = 0; i < ecclayout->eccbytes; i++, oob_index++)
ecclayout->eccpos[i] = oob_index;
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break; break;
default: default:
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n"); dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
...@@ -2108,13 +2148,13 @@ static int omap_nand_probe(struct platform_device *pdev) ...@@ -2108,13 +2148,13 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error; goto return_error;
} }
/* all OOB bytes from oobfree->offset till end off OOB are free */
ecclayout->oobfree->length = mtd->oobsize - ecclayout->oobfree->offset;
/* check if NAND device's OOB is enough to store ECC signatures */ /* check if NAND device's OOB is enough to store ECC signatures */
if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { min_oobbytes += (oobbytes_per_step *
(mtd->writesize / nand_chip->ecc.size));
if (mtd->oobsize < min_oobbytes) {
dev_err(&info->pdev->dev, dev_err(&info->pdev->dev,
"not enough OOB bytes required = %d, available=%d\n", "not enough OOB bytes required = %d, available=%d\n",
ecclayout->eccbytes, mtd->oobsize); min_oobbytes, mtd->oobsize);
err = -EINVAL; err = -EINVAL;
goto return_error; goto return_error;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment