summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/raw/nand_base.c
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@bootlin.com>2018-06-07 22:52:56 +0200
committerBoris Brezillon <boris.brezillon@bootlin.com>2018-06-07 22:52:56 +0200
commit6e89b84e28f69c4f848dc8f1ea8fdecf33f2fbb4 (patch)
tree4c32febe723d922cf551e34c5a082033ebe371ce /drivers/mtd/nand/raw/nand_base.c
parentc64d4419a17cfb39a5b573f9016cd02ade4c9a64 (diff)
parentf567c71f4f91d173795a2f46d6fc493387ce084d (diff)
downloadlinux-6e89b84e28f69c4f848dc8f1ea8fdecf33f2fbb4.tar.bz2
Merge tag 'nand/for-4.18' of git://git.infradead.org/linux-mtd into mtd/next
Core changes: - Add Miquel as a NAND maintainer - Add access mode to the nand_page_io_req struct - Fix kernel-doc in rawnand.h - Support bit-wise majority to recover from corrupted ONFI parameter pages - Stop checking FAIL bit after a SET_FEATURES, as documented in the ONFI spec Raw NAND Driver changes: - Fix and cleanup the error path of many NAND controller drivers - GPMI: * Cleanup/simplification of a few aspects in the driver * Take ECC setup specified in the DT into account - sunxi: remove support for GPIO-based R/B polling - MTK: * Use of_device_get_match_data() instead of of_match_device() * Add an entry in MAINTAINERS for this driver * Fix nand-ecc-step-size and nand-ecc-strength description in the DT bindings doc - fsl_ifc: fix ->cmdfunc() to read more than one ONFI parameter page OneNAND driver changes: - samsung: use dev_get_drvdata() instead of platform_get_drvdata()
Diffstat (limited to 'drivers/mtd/nand/raw/nand_base.c')
-rw-r--r--drivers/mtd/nand/raw/nand_base.c89
1 files changed, 62 insertions, 27 deletions
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index 72f3a89da513..327c60b06f87 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -2169,7 +2169,6 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
struct mtd_info *mtd = nand_to_mtd(chip);
const u8 *params = data;
int i, ret;
- u8 status;
if (chip->exec_op) {
const struct nand_sdr_timings *sdr =
@@ -2183,26 +2182,18 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
};
struct nand_operation op = NAND_OPERATION(instrs);
- ret = nand_exec_op(chip, &op);
- if (ret)
- return ret;
-
- ret = nand_status_op(chip, &status);
- if (ret)
- return ret;
- } else {
- chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1);
- for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
- chip->write_byte(mtd, params[i]);
+ return nand_exec_op(chip, &op);
+ }
- ret = chip->waitfunc(mtd, chip);
- if (ret < 0)
- return ret;
+ chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, feature, -1);
+ for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i)
+ chip->write_byte(mtd, params[i]);
- status = ret;
- }
+ ret = chip->waitfunc(mtd, chip);
+ if (ret < 0)
+ return ret;
- if (status & NAND_STATUS_FAIL)
+ if (ret & NAND_STATUS_FAIL)
return -EIO;
return 0;
@@ -5087,6 +5078,37 @@ ext_out:
}
/*
+ * Recover data with bit-wise majority
+ */
+static void nand_bit_wise_majority(const void **srcbufs,
+ unsigned int nsrcbufs,
+ void *dstbuf,
+ unsigned int bufsize)
+{
+ int i, j, k;
+
+ for (i = 0; i < bufsize; i++) {
+ u8 val = 0;
+
+ for (j = 0; j < 8; j++) {
+ unsigned int cnt = 0;
+
+ for (k = 0; k < nsrcbufs; k++) {
+ const u8 *srcbuf = srcbufs[k];
+
+ if (srcbuf[i] & BIT(j))
+ cnt++;
+ }
+
+ if (cnt > nsrcbufs / 2)
+ val |= BIT(j);
+ }
+
+ ((u8 *)dstbuf)[i] = val;
+ }
+}
+
+/*
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
*/
static int nand_flash_detect_onfi(struct nand_chip *chip)
@@ -5102,7 +5124,7 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
return 0;
/* ONFI chip: allocate a buffer to hold its parameter page */
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc((sizeof(*p) * 3), GFP_KERNEL);
if (!p)
return -ENOMEM;
@@ -5113,21 +5135,32 @@ static int nand_flash_detect_onfi(struct nand_chip *chip)
}
for (i = 0; i < 3; i++) {
- ret = nand_read_data_op(chip, p, sizeof(*p), true);
+ ret = nand_read_data_op(chip, &p[i], sizeof(*p), true);
if (ret) {
ret = 0;
goto free_onfi_param_page;
}
- if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+ if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) ==
le16_to_cpu(p->crc)) {
+ if (i)
+ memcpy(p, &p[i], sizeof(*p));
break;
}
}
if (i == 3) {
- pr_err("Could not find valid ONFI parameter page; aborting\n");
- goto free_onfi_param_page;
+ const void *srcbufs[3] = {p, p + 1, p + 2};
+
+ pr_warn("Could not find a valid ONFI parameter page, trying bit-wise majority to recover it\n");
+ nand_bit_wise_majority(srcbufs, ARRAY_SIZE(srcbufs), p,
+ sizeof(*p));
+
+ if (onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 254) !=
+ le16_to_cpu(p->crc)) {
+ pr_err("ONFI parameter recovery failed, aborting\n");
+ goto free_onfi_param_page;
+ }
}
/* Check version */
@@ -6630,24 +6663,26 @@ EXPORT_SYMBOL(nand_scan_tail);
#endif
/**
- * nand_scan - [NAND Interface] Scan for the NAND device
+ * nand_scan_with_ids - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: number of chips to scan for
+ * @ids: optional flash IDs table
*
* This fills out all the uninitialized function pointers with the defaults.
* The flash ID is read and the mtd/chip structures are filled with the
* appropriate values.
*/
-int nand_scan(struct mtd_info *mtd, int maxchips)
+int nand_scan_with_ids(struct mtd_info *mtd, int maxchips,
+ struct nand_flash_dev *ids)
{
int ret;
- ret = nand_scan_ident(mtd, maxchips, NULL);
+ ret = nand_scan_ident(mtd, maxchips, ids);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
-EXPORT_SYMBOL(nand_scan);
+EXPORT_SYMBOL(nand_scan_with_ids);
/**
* nand_cleanup - [NAND Interface] Free resources held by the NAND device