summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/omap2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r--drivers/mtd/nand/omap2.c182
1 files changed, 105 insertions, 77 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index f0ed92e210a..3b357e920a0 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -136,7 +136,6 @@
#define BADBLOCK_MARKER_LENGTH 2
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
@@ -144,7 +143,6 @@ static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
0xac, 0x6b, 0xff, 0x99, 0x7b};
static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
-#endif
/* oob info generated runtime depending on ecc algorithm and layout selected */
static struct nand_ecclayout omap_oobinfo;
@@ -931,7 +929,7 @@ static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u32 val;
val = readl(info->reg.gpmc_ecc_config);
- if (((val >> ECC_CONFIG_CS_SHIFT) & ~CS_MASK) != info->gpmc_cs)
+ if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
return -EINVAL;
/* read ecc result */
@@ -1292,7 +1290,6 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
return 0;
}
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
/**
* erased_sector_bitflips - count bit flips
* @data: data sector buffer
@@ -1378,7 +1375,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
erased_ecc_vec = bch16_vector;
break;
default:
- pr_err("invalid driver configuration\n");
+ dev_err(&info->pdev->dev, "invalid driver configuration\n");
return -EINVAL;
}
@@ -1449,7 +1446,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
err = 0;
for (i = 0; i < eccsteps; i++) {
if (err_vec[i].error_uncorrectable) {
- pr_err("nand: uncorrectable bit-flips found\n");
+ dev_err(&info->pdev->dev,
+ "uncorrectable bit-flips found\n");
err = -EBADMSG;
} else if (err_vec[i].error_reported) {
for (j = 0; j < err_vec[i].error_count; j++) {
@@ -1486,8 +1484,9 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
1 << bit_pos;
}
} else {
- pr_err("invalid bit-flip @ %d:%d\n",
- byte_pos, bit_pos);
+ dev_err(&info->pdev->dev,
+ "invalid bit-flip @ %d:%d\n",
+ byte_pos, bit_pos);
err = -EBADMSG;
}
}
@@ -1593,33 +1592,71 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
/**
* is_elm_present - checks for presence of ELM module by scanning DT nodes
* @omap_nand_info: NAND device structure containing platform data
- * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16
*/
-static int is_elm_present(struct omap_nand_info *info,
- struct device_node *elm_node, enum bch_ecc bch_type)
+static bool is_elm_present(struct omap_nand_info *info,
+ struct device_node *elm_node)
{
struct platform_device *pdev;
- struct nand_ecc_ctrl *ecc = &info->nand.ecc;
- int err;
+
/* check whether elm-id is passed via DT */
if (!elm_node) {
- pr_err("nand: error: ELM DT node not found\n");
- return -ENODEV;
+ dev_err(&info->pdev->dev, "ELM devicetree node not found\n");
+ return false;
}
pdev = of_find_device_by_node(elm_node);
/* check whether ELM device is registered */
if (!pdev) {
- pr_err("nand: error: ELM device not found\n");
- return -ENODEV;
+ dev_err(&info->pdev->dev, "ELM device not found\n");
+ return false;
}
/* ELM module available, now configure it */
info->elm_dev = &pdev->dev;
- err = elm_config(info->elm_dev, bch_type,
- (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes);
+ return true;
+}
- return err;
+static bool omap2_nand_ecc_check(struct omap_nand_info *info,
+ struct omap_nand_platform_data *pdata)
+{
+ bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm;
+
+ switch (info->ecc_opt) {
+ case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ ecc_needs_omap_bch = false;
+ ecc_needs_bch = true;
+ ecc_needs_elm = false;
+ break;
+ case OMAP_ECC_BCH4_CODE_HW:
+ case OMAP_ECC_BCH8_CODE_HW:
+ case OMAP_ECC_BCH16_CODE_HW:
+ ecc_needs_omap_bch = true;
+ ecc_needs_bch = false;
+ ecc_needs_elm = true;
+ break;
+ default:
+ ecc_needs_omap_bch = false;
+ ecc_needs_bch = false;
+ ecc_needs_elm = false;
+ break;
+ }
+
+ if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_BCH)) {
+ dev_err(&info->pdev->dev,
+ "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+ return false;
+ }
+ if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) {
+ dev_err(&info->pdev->dev,
+ "CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
+ return false;
+ }
+ if (ecc_needs_elm && !is_elm_present(info, pdata->elm_of_node)) {
+ dev_err(&info->pdev->dev, "ELM not available\n");
+ return false;
+ }
+
+ return true;
}
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
static int omap_nand_probe(struct platform_device *pdev)
{
@@ -1663,7 +1700,6 @@ static int omap_nand_probe(struct platform_device *pdev)
mtd->owner = THIS_MODULE;
nand_chip = &info->nand;
nand_chip->ecc.priv = NULL;
- nand_chip->options |= NAND_SKIP_BBTSCAN;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
@@ -1692,17 +1728,22 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 50;
}
+ if (pdata->flash_bbt)
+ nand_chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+ else
+ nand_chip->options |= NAND_SKIP_BBTSCAN;
+
/* scan NAND device connected to chip controller */
nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16;
if (nand_scan_ident(mtd, 1, NULL)) {
- pr_err("nand device scan failed, may be bus-width mismatch\n");
+ dev_err(&info->pdev->dev, "scan failed, may be bus-width mismatch\n");
err = -ENXIO;
goto return_error;
}
/* 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");
+ dev_err(&info->pdev->dev, "small page devices are not supported\n");
err = -EINVAL;
goto return_error;
}
@@ -1793,10 +1834,18 @@ static int omap_nand_probe(struct platform_device *pdev)
goto return_error;
}
+ if (!omap2_nand_ecc_check(info, pdata)) {
+ err = -EINVAL;
+ goto return_error;
+ }
+
/* populate MTD interface based on ECC scheme */
- nand_chip->ecc.layout = &omap_oobinfo;
ecclayout = &omap_oobinfo;
switch (info->ecc_opt) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ break;
+
case OMAP_ECC_HAM1_CODE_HW:
pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
@@ -1822,7 +1871,6 @@ static int omap_nand_probe(struct platform_device *pdev)
break;
case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
-#ifdef CONFIG_MTD_NAND_ECC_BCH
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
@@ -1848,20 +1896,15 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
- pr_err("nand: error: unable to use s/w BCH library\n");
+ dev_err(&info->pdev->dev, "unable to use BCH library\n");
err = -EINVAL;
+ goto return_error;
}
break;
-#else
- pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n");
- err = -EINVAL;
- goto return_error;
-#endif
case OMAP_ECC_BCH4_CODE_HW:
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
@@ -1883,21 +1926,15 @@ static int omap_nand_probe(struct platform_device *pdev)
/* reserved marker already included in ecclayout->eccbytes */
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
- /* 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");
- err = -ENODEV;
+
+ err = elm_config(info->elm_dev, BCH4_ECC,
+ info->mtd.writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
goto return_error;
- }
break;
-#else
- pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
- err = -EINVAL;
- goto return_error;
-#endif
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
-#ifdef CONFIG_MTD_NAND_ECC_BCH
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
@@ -1923,21 +1960,15 @@ static int omap_nand_probe(struct platform_device *pdev)
nand_chip->ecc.priv = nand_bch_init(mtd,
nand_chip->ecc.size,
nand_chip->ecc.bytes,
- &nand_chip->ecc.layout);
+ &ecclayout);
if (!nand_chip->ecc.priv) {
- pr_err("nand: error: unable to use s/w BCH library\n");
+ dev_err(&info->pdev->dev, "unable to use BCH library\n");
err = -EINVAL;
goto return_error;
}
break;
-#else
- pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n");
- err = -EINVAL;
- goto return_error;
-#endif
case OMAP_ECC_BCH8_CODE_HW:
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
@@ -1949,12 +1980,13 @@ 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;
- /* This ECC scheme requires ELM H/W block */
- err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC);
- if (err < 0) {
- pr_err("nand: error: could not initialize ELM\n");
+
+ err = elm_config(info->elm_dev, BCH8_ECC,
+ info->mtd.writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
goto return_error;
- }
+
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -1966,14 +1998,8 @@ static int omap_nand_probe(struct platform_device *pdev)
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
-#else
- pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
- err = -EINVAL;
- goto return_error;
-#endif
case OMAP_ECC_BCH16_CODE_HW:
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
pr_info("using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
nand_chip->ecc.mode = NAND_ECC_HW;
nand_chip->ecc.size = 512;
@@ -1984,12 +2010,13 @@ 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;
- /* This ECC scheme requires ELM H/W block */
- err = is_elm_present(info, pdata->elm_of_node, BCH16_ECC);
- if (err < 0) {
- pr_err("ELM is required for this ECC scheme\n");
+
+ err = elm_config(info->elm_dev, BCH16_ECC,
+ info->mtd.writesize / nand_chip->ecc.size,
+ nand_chip->ecc.size, nand_chip->ecc.bytes);
+ if (err < 0)
goto return_error;
- }
+
/* define ECC layout */
ecclayout->eccbytes = nand_chip->ecc.bytes *
(mtd->writesize /
@@ -2001,27 +2028,28 @@ static int omap_nand_probe(struct platform_device *pdev)
ecclayout->oobfree->offset =
ecclayout->eccpos[ecclayout->eccbytes - 1] + 1;
break;
-#else
- pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
- err = -EINVAL;
- goto return_error;
-#endif
default:
- pr_err("nand: error: invalid or unsupported ECC scheme\n");
+ dev_err(&info->pdev->dev, "invalid or unsupported ECC scheme\n");
err = -EINVAL;
goto return_error;
}
+ if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW)
+ goto scan_tail;
+
/* 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)) {
- pr_err("not enough OOB bytes required = %d, available=%d\n",
- ecclayout->eccbytes, mtd->oobsize);
+ dev_err(&info->pdev->dev,
+ "not enough OOB bytes required = %d, available=%d\n",
+ ecclayout->eccbytes, mtd->oobsize);
err = -EINVAL;
goto return_error;
}
+ nand_chip->ecc.layout = ecclayout;
+scan_tail:
/* second phase scan */
if (nand_scan_tail(mtd)) {
err = -ENXIO;