|
@@ -178,8 +178,6 @@ struct omap_nand_info {
|
|
|
struct gpmc_nand_regs reg;
|
|
|
struct gpmc_nand_ops *ops;
|
|
|
bool flash_bbt;
|
|
|
- /* generated at runtime depending on ECC algorithm and layout selected */
|
|
|
- struct nand_ecclayout oobinfo;
|
|
|
/* fields specific for BCHx_HW ECC scheme */
|
|
|
struct device *elm_dev;
|
|
|
/* NAND ready gpio */
|
|
@@ -1714,20 +1712,115 @@ static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
|
|
|
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)
|
|
|
{
|
|
|
struct omap_nand_info *info;
|
|
|
struct omap_nand_platform_data *pdata = NULL;
|
|
|
struct mtd_info *mtd;
|
|
|
struct nand_chip *nand_chip;
|
|
|
- struct nand_ecclayout *ecclayout;
|
|
|
int err;
|
|
|
- int i;
|
|
|
dma_cap_mask_t mask;
|
|
|
unsigned sig;
|
|
|
- unsigned oob_index;
|
|
|
struct resource *res;
|
|
|
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),
|
|
|
GFP_KERNEL);
|
|
@@ -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
|
|
|
- * ecclayout instead of using ours.
|
|
|
+ * ooblayout instead of using ours.
|
|
|
*/
|
|
|
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
|
|
|
nand_chip->ecc.mode = NAND_ECC_SOFT;
|
|
@@ -1923,8 +2016,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
/* populate MTD interface based on ECC scheme */
|
|
|
- ecclayout = &info->oobinfo;
|
|
|
- nand_chip->ecc.layout = ecclayout;
|
|
|
switch (info->ecc_opt) {
|
|
|
case OMAP_ECC_HAM1_CODE_HW:
|
|
|
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
|
|
@@ -1935,19 +2026,12 @@ 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)
|
|
|
- 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;
|
|
|
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
|
+ oobbytes_per_step = nand_chip->ecc.bytes;
|
|
|
+
|
|
|
+ if (!(nand_chip->options & NAND_BUSWIDTH_16))
|
|
|
+ min_oobbytes = 1;
|
|
|
+
|
|
|
break;
|
|
|
|
|
|
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
|
|
@@ -1959,19 +2043,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
|
nand_chip->ecc.correct = nand_bch_correct_data;
|
|
|
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
|
|
- /* 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;
|
|
|
- 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;
|
|
|
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
|
|
+ /* Reserve one byte for the OMAP marker */
|
|
|
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
|
|
|
/* software bch library is used for locating errors */
|
|
|
nand_chip->ecc.priv = nand_bch_init(mtd);
|
|
|
if (!nand_chip->ecc.priv) {
|
|
@@ -1993,16 +2067,8 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
nand_chip->ecc.calculate = omap_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);
|
|
|
- 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;
|
|
|
+ mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
|
|
|
+ oobbytes_per_step = nand_chip->ecc.bytes;
|
|
|
|
|
|
err = elm_config(info->elm_dev, BCH4_ECC,
|
|
|
mtd->writesize / nand_chip->ecc.size,
|
|
@@ -2020,19 +2086,9 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
nand_chip->ecc.hwctl = omap_enable_hwecc_bch;
|
|
|
nand_chip->ecc.correct = nand_bch_correct_data;
|
|
|
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
|
|
- /* 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;
|
|
|
- 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;
|
|
|
+ mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
|
|
|
+ /* Reserve one byte for the OMAP marker */
|
|
|
+ oobbytes_per_step = nand_chip->ecc.bytes + 1;
|
|
|
/* software bch library is used for locating errors */
|
|
|
nand_chip->ecc.priv = nand_bch_init(mtd);
|
|
|
if (!nand_chip->ecc.priv) {
|
|
@@ -2054,6 +2110,8 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
|
|
nand_chip->ecc.read_page = omap_read_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,
|
|
|
mtd->writesize / nand_chip->ecc.size,
|
|
@@ -2061,16 +2119,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
if (err < 0)
|
|
|
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;
|
|
|
|
|
|
case OMAP_ECC_BCH16_CODE_HW:
|
|
@@ -2084,6 +2132,8 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
nand_chip->ecc.calculate = omap_calculate_ecc_bch;
|
|
|
nand_chip->ecc.read_page = omap_read_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,
|
|
|
mtd->writesize / nand_chip->ecc.size,
|
|
@@ -2091,16 +2141,6 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
if (err < 0)
|
|
|
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;
|
|
|
default:
|
|
|
dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
|
|
@@ -2108,13 +2148,13 @@ static int omap_nand_probe(struct platform_device *pdev)
|
|
|
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 */
|
|
|
- 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,
|
|
|
"not enough OOB bytes required = %d, available=%d\n",
|
|
|
- ecclayout->eccbytes, mtd->oobsize);
|
|
|
+ min_oobbytes, mtd->oobsize);
|
|
|
err = -EINVAL;
|
|
|
goto return_error;
|
|
|
}
|