From 28b8b26b308e656edfa9467867d5f79212da2ec3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 30 Oct 2015 20:33:20 -0700 Subject: mtd: add get/set of_node/flash_node helpers We are going to begin using the mtd->dev.of_node field for MTD device nodes, so let's add helpers for it. Also, we'll be making some conversions on spi_nor (and nand_chip eventually) too, so get that ready with their own helpers. Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon --- include/linux/mtd/nand.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 5a9d1d4c2487..4f7c9b97982f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -719,6 +719,17 @@ struct nand_chip { void *priv; }; +static inline void nand_set_flash_node(struct nand_chip *chip, + struct device_node *np) +{ + chip->flash_node = np; +} + +static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) +{ + return chip->flash_node; +} + /* * NAND Flash Manufacturer ID Codes */ -- cgit From 9eba47ddd8fee8a21f45e6e1d707103f040d90c7 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 16 Nov 2015 14:37:35 +0100 Subject: mtd: nand: add an mtd_to_nand() helper Some drivers are retrieving the nand_chip pointer using the container_of macro on a struct wrapping both the nand_chip and the mtd_info struct while the standard way of retrieving this pointer is through mtd->priv. Provide an helper to do that. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 5a9d1d4c2487..a4839b3f27da 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -719,6 +719,11 @@ struct nand_chip { void *priv; }; +static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) +{ + return mtd->priv; +} + /* * NAND Flash Manufacturer ID Codes */ -- cgit From de64aa9ec129ba627634088f662a4d09e356ddb6 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 23 Nov 2015 11:23:07 +0100 Subject: mtd: nand: fix ONFI parameter page layout src_ssync_features field is only 1 byte large, and the 4th reserved area is actually 8 bytes large. Fixes: d1e1f4e42b5 ("mtd: nand: add support for reading ONFI parameters from NAND device") Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 056d1650eb89..eaf48b5a95dd 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -276,7 +276,7 @@ struct nand_onfi_params { __le16 t_r; __le16 t_ccs; __le16 src_sync_timing_mode; - __le16 src_ssync_features; + u8 src_ssync_features; __le16 clk_pin_capacitance_typ; __le16 io_pin_capacitance_typ; __le16 input_pin_capacitance_typ; @@ -284,7 +284,7 @@ struct nand_onfi_params { u8 driver_strength_support; __le16 t_int_r; __le16 t_ald; - u8 reserved4[7]; + u8 reserved4[8]; /* vendor */ __le16 vendor_revision; -- cgit From 74e98be45fe069fcdb00f35eccbb179309ab65cd Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Tue, 1 Dec 2015 11:08:32 -0800 Subject: mtd: nand: fix typo (t_ald -> t_adl) It's "ADL" ("ALE to data loading" time) not "ALD". Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon --- include/linux/mtd/nand.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index eaf48b5a95dd..fad634ea1685 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -283,7 +283,7 @@ struct nand_onfi_params { u8 input_pin_capacitance_max; u8 driver_strength_support; __le16 t_int_r; - __le16 t_ald; + __le16 t_adl; u8 reserved4[8]; /* vendor */ @@ -407,7 +407,7 @@ struct nand_jedec_params { __le16 input_pin_capacitance_typ; __le16 clk_pin_capacitance_typ; u8 driver_strength_support; - __le16 t_ald; + __le16 t_adl; u8 reserved4[36]; /* ECC and endurance block */ -- cgit From ed4f85c03cc7460a2f76afb73c22b8894b44ee20 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 1 Dec 2015 12:03:06 +0100 Subject: mtd: nand: embed an mtd_info structure into nand_chip Currently all NAND controller drivers are providing both the mtd_info and nand_chip struct and then let the NAND subsystem to initialize a few things before registering the mtd instance to the MTD layer. Embed an mtd_info field into nand_chip to add some consistency to all NAND controller drivers. This change will also help factorizing boilerplate code copied in all NAND drivers. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index fad634ea1685..d6710575ddb6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -540,6 +540,7 @@ struct nand_buffers { /** * struct nand_chip - NAND Private Flash Chip Data + * @mtd: MTD device registered to the MTD framework * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the * flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the @@ -640,6 +641,7 @@ struct nand_buffers { */ struct nand_chip { + struct mtd_info mtd; void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; -- cgit From ffd014f43fdcb6edb5a7f302de1e717e8c0673d5 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Tue, 1 Dec 2015 12:03:07 +0100 Subject: mtd: nand: add nand_to_mtd() helper Add a new helper to retrieve the MTD device attached to a NAND chip. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d6710575ddb6..b614ed2105ac 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -737,6 +737,11 @@ static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) return mtd->priv; } +static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip) +{ + return &chip->mtd; +} + /* * NAND Flash Manufacturer ID Codes */ -- cgit From 2d3b77bac34bf99d7fdfd712ec2dc4317b3e850b Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Dec 2015 09:00:33 +0100 Subject: mtd: nand: update mtd_to_nand() Now that all drivers are using the mtd instance embedded in the nand_chip struct we can safely update the mtd_to_nand() implementation to use the container_of macro instead of returning the content of mtd->priv. This will allow us to remove mtd->priv = chip assignments done in all NAND controller drivers. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index b614ed2105ac..9cb7ace6fb1f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -734,7 +734,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) { - return mtd->priv; + return container_of(mtd, struct nand_chip, mtd); } static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip) -- cgit From 29574ede097438c560e8115caff9b6b8668730be Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Dec 2015 09:00:38 +0100 Subject: mtd: nand: kill the chip->flash_node field Now that the nand_chip struct directly embeds an mtd_info struct we can get rid of the ->flash_node field and forward set/get_flash_node requests to the MTD layer. As a side effect, we no longer need the mtd_set_of_node() call done in nand_dt_init(). Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 3 --- include/linux/mtd/nand.h | 7 ++----- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ae3fd2a8c2f5..8bb8ebd62aaa 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3945,9 +3945,6 @@ static int nand_dt_init(struct nand_chip *chip) if (!dn) return 0; - /* MTD can automatically handle DT partitions, etc. */ - mtd_set_of_node(nand_to_mtd(chip), dn); - if (of_get_nand_bus_width(dn) == 16) chip->options |= NAND_BUSWIDTH_16; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 9cb7ace6fb1f..2bee2e42ae2f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -545,7 +545,6 @@ struct nand_buffers { * flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the * flash device. - * @flash_node: [BOARDSPECIFIC] device node describing this instance * @read_byte: [REPLACEABLE] read one byte from the chip * @read_word: [REPLACEABLE] read one word from the chip * @write_byte: [REPLACEABLE] write a single byte to the chip on the @@ -645,8 +644,6 @@ struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; - struct device_node *flash_node; - uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_byte)(struct mtd_info *mtd, uint8_t byte); @@ -724,12 +721,12 @@ struct nand_chip { static inline void nand_set_flash_node(struct nand_chip *chip, struct device_node *np) { - chip->flash_node = np; + mtd_set_of_node(&chip->mtd, np); } static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) { - return chip->flash_node; + return mtd_get_of_node(&chip->mtd); } static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd) -- cgit From 8142b47ef33c655a34e08efd46b65732fe190675 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Mon, 14 Dec 2015 16:13:31 +0100 Subject: mtd: nand: remove unused and buggy get_platform_nandchip() helper function Nobody uses the get_platform_nandchip() helper function which is supposed to return a pointer to a platform_nand_chip struct from an mtd_info pointer. Moreover, this function is buggy since the introduction of the plat_nand layer (chip->priv is now storing a pointer to an intermediate plat_nand_data structure allocated in plat_nand_probe(), and we have no way to retrieve a pointer to the provided platform_nand_chip struct from this plat_nand_data pointer). While we are at it, remove the useless (and buggy, since it's pointing to something stored on the stack) data->chip.priv assignment. Signed-off-by: Boris Brezillon Fixes: 711fdf627ce1 ("[MTD] [NAND] platform NAND driver: add driver") Cc: Vitaly Wool Signed-off-by: Brian Norris --- drivers/mtd/nand/plat_nand.c | 1 - include/linux/mtd/nand.h | 9 --------- 2 files changed, 10 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index dc88a58d5cde..a0e26dea1424 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -56,7 +56,6 @@ static int plat_nand_probe(struct platform_device *pdev) if (IS_ERR(data->io_base)) return PTR_ERR(data->io_base); - data->chip.priv = &data; nand_set_flash_node(&data->chip, pdev->dev.of_node); mtd = nand_to_mtd(&data->chip); mtd->dev.parent = &pdev->dev; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2bee2e42ae2f..3e92be1d2d43 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -927,15 +927,6 @@ struct platform_nand_data { struct platform_nand_ctrl ctrl; }; -/* Some helpers to access the data structures */ -static inline -struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) -{ - struct nand_chip *chip = mtd->priv; - - return chip->priv; -} - /* return the supported features. */ static inline int onfi_feature(struct nand_chip *chip) { -- cgit From 6e9411923b8f4c0e568cbae0f35b7ee4eb989914 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Dec 2015 20:32:03 +0100 Subject: mtd: nand: return consistent error codes in ecc.correct() implementations The error code returned by the ecc.correct() are not consistent over the all implementations. Document the expected behavior in include/linux/mtd/nand.h and fix offending implementations. [Brian: this looks like a bugfix for the ECC reporting in the bf5xx_nand driver, but we haven't seen any testing results for it] Signed-off-by: Boris Brezillon Tested-by: Franklin S Cooper Jr. Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 2 +- drivers/mtd/nand/bf5xx_nand.c | 20 ++++++++++++++------ drivers/mtd/nand/davinci_nand.c | 6 +++--- drivers/mtd/nand/jz4740_nand.c | 4 ++-- drivers/mtd/nand/mxc_nand.c | 4 ++-- drivers/mtd/nand/nand_bch.c | 2 +- drivers/mtd/nand/nand_ecc.c | 2 +- drivers/mtd/nand/omap2.c | 6 +++--- drivers/mtd/nand/r852.c | 4 ++-- include/linux/mtd/nand.h | 8 +++++++- include/linux/mtd/nand_bch.h | 2 +- 11 files changed, 37 insertions(+), 23 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 18c4e14ec29f..b216bf521349 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1445,7 +1445,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, * We can't correct so many errors */ dev_dbg(host->dev, "atmel_nand : multiple errors detected." " Unable to correct.\n"); - return -EIO; + return -EBADMSG; } /* if there's a single bit error : we can correct it */ diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 9514e136542f..89d9414c5593 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -252,7 +252,7 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, */ if (hweight32(syndrome[0]) == 1) { dev_err(info->device, "ECC data was incorrect!\n"); - return 1; + return -EBADMSG; } syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); @@ -285,7 +285,7 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, data = data ^ (0x1 << failing_bit); *(dat + failing_byte) = data; - return 0; + return 1; } /* @@ -298,26 +298,34 @@ static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, dev_err(info->device, "Please discard data, mark bad block\n"); - return 1; + return -EBADMSG; } static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { struct nand_chip *chip = mtd_to_nand(mtd); - int ret; + int ret, bitflips = 0; ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + if (ret < 0) + return ret; + + bitflips = ret; /* If ecc size is 512, correct second 256 bytes */ if (chip->ecc.size == 512) { dat += 256; read_ecc += 3; calc_ecc += 3; - ret |= bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); + if (ret < 0) + return ret; + + bitflips += ret; } - return ret; + return bitflips; } static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 3b49fe86625d..ddb73c331936 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -207,7 +207,7 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); return 1; } else { - return -1; + return -EBADMSG; } } else if (!(diff & (diff - 1))) { /* Single bit ECC error in the ECC itself, @@ -215,7 +215,7 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, return 1; } else { /* Uncorrectable error */ - return -1; + return -EBADMSG; } } @@ -391,7 +391,7 @@ compare: return 0; case 1: /* five or more errors detected */ davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET); - return -EIO; + return -EBADMSG; case 2: /* error addresses computed */ case 3: num_errors = 1 + ((fsr >> 16) & 0x03); diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index a2363d33cecc..adccae3da120 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -254,7 +254,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, } while (!(status & JZ_NAND_STATUS_DEC_FINISH) && --timeout); if (timeout == 0) - return -1; + return -ETIMEDOUT; reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); reg &= ~JZ_NAND_ECC_CTRL_ENABLE; @@ -262,7 +262,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat, if (status & JZ_NAND_STATUS_ERROR) { if (status & JZ_NAND_STATUS_UNCOR_ERROR) - return -1; + return -EBADMSG; error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 95400992c3e9..66e56bb22c0f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -674,7 +674,7 @@ static int mxc_nand_correct_data_v1(struct mtd_info *mtd, u_char *dat, if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { pr_debug("MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); - return -1; + return -EBADMSG; } return 0; @@ -701,7 +701,7 @@ static int mxc_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat, err = ecc_stat & ecc_bit_mask; if (err > err_limit) { printk(KERN_WARNING "UnCorrectable RS-ECC Error\n"); - return -1; + return -EBADMSG; } else { ret += err; } diff --git a/drivers/mtd/nand/nand_bch.c b/drivers/mtd/nand/nand_bch.c index e5758d885943..a87c1b628dfc 100644 --- a/drivers/mtd/nand/nand_bch.c +++ b/drivers/mtd/nand/nand_bch.c @@ -98,7 +98,7 @@ int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, } } else if (count < 0) { printk(KERN_ERR "ecc unrecoverable error\n"); - count = -1; + count = -EBADMSG; } return count; } diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index e6129851bfd8..d1770b066396 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -507,7 +507,7 @@ int __nand_correct_data(unsigned char *buf, return 1; /* error in ECC data; no action needed */ pr_err("%s: uncorrectable ECC error\n", __func__); - return -1; + return -EBADMSG; } EXPORT_SYMBOL(__nand_correct_data); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index e9cbbc63c566..c553f78ab83f 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -826,12 +826,12 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ case 1: /* Uncorrectable error */ pr_debug("ECC UNCORRECTED_ERROR 1\n"); - return -1; + return -EBADMSG; case 11: /* UN-Correctable error */ pr_debug("ECC UNCORRECTED_ERROR B\n"); - return -1; + return -EBADMSG; case 12: /* Correctable error */ @@ -861,7 +861,7 @@ static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ return 0; } pr_debug("UNCORRECTED_ERROR default\n"); - return -1; + return -EBADMSG; } } diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index cb0bf09214d5..5b15f2faee38 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -477,7 +477,7 @@ static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, if (dev->dma_error) { dev->dma_error = 0; - return -1; + return -EIO; } r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); @@ -491,7 +491,7 @@ static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, /* ecc uncorrectable error */ if (ecc_status & R852_ECC_FAIL) { dbg("ecc: unrecoverable error, in half %d", i); - error = -1; + error = -EBADMSG; goto exit; } diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 3e92be1d2d43..518958115182 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -456,7 +456,13 @@ struct nand_hw_control { * @hwctl: function to control hardware ECC generator. Must only * be provided if an hardware ECC is available * @calculate: function for ECC calculation or readback from ECC hardware - * @correct: function for ECC correction, matching to ECC generator (sw/hw) + * @correct: function for ECC correction, matching to ECC generator (sw/hw). + * Should return a positive number representing the number of + * corrected bitflips, -EBADMSG if the number of bitflips exceed + * ECC strength, or any other error code if the error is not + * directly related to correction. + * If -EBADMSG is returned the input buffers should be left + * untouched. * @read_page_raw: function to read a raw page without ECC. This function * should hide the specific layout used by the ECC * controller and always return contiguous in-band and diff --git a/include/linux/mtd/nand_bch.h b/include/linux/mtd/nand_bch.h index 74acf5367556..fb0bc3420a10 100644 --- a/include/linux/mtd/nand_bch.h +++ b/include/linux/mtd/nand_bch.h @@ -55,7 +55,7 @@ static inline int nand_bch_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { - return -1; + return -ENOTSUPP; } static inline struct nand_bch_control * -- cgit From 40cbe6eee97b706f27bcc4c6aa1018bbe4f1e577 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 30 Dec 2015 20:32:04 +0100 Subject: mtd: nand: use nand_check_erased_ecc_chunk in default ECC read functions The default NAND read functions are relying on the underlying controller driver to correct bitflips, but some of those controllers cannot properly fix bitflips in erased pages. Check for bitflips in erased pages in default core functions if the driver delegated the this check by setting the NAND_ECC_GENERIC_ERASED_CHECK flag. Signed-off-by: Boris Brezillon Tested-by: Franklin S Cooper Jr. Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 53 ++++++++++++++++++++++++++++++++++++++------ include/linux/mtd/nand.h | 10 +++++++++ 2 files changed, 56 insertions(+), 7 deletions(-) (limited to 'include/linux/mtd/nand.h') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 50514f2501bb..f2c8ff398d6c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1426,6 +1426,16 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + &chip->buffers->ecccode[i], + chip->ecc.bytes, + NULL, 0, + chip->ecc.strength); + } + if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1475,6 +1485,15 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, int stat; stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1527,6 +1546,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, chip->ecc.calculate(mtd, p, &ecc_calc[i]); stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, eccsize, + &ecc_code[i], eccbytes, + NULL, 0, + chip->ecc.strength); + } + if (stat < 0) { mtd->ecc_stats.failed++; } else { @@ -1554,6 +1582,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int i, eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; + int eccpadbytes = eccbytes + chip->ecc.prepad + chip->ecc.postpad; uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; @@ -1573,19 +1602,29 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf(mtd, oob, eccbytes); stat = chip->ecc.correct(mtd, p, oob, NULL); - if (stat < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += stat; - max_bitflips = max_t(unsigned int, max_bitflips, stat); - } - oob += eccbytes; if (chip->ecc.postpad) { chip->read_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; } + + if (stat == -EBADMSG && + (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) { + /* check for empty pages with bitflips */ + stat = nand_check_erased_ecc_chunk(p, chip->ecc.size, + oob - eccpadbytes, + eccpadbytes, + NULL, 0, + chip->ecc.strength); + } + + if (stat < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += stat; + max_bitflips = max_t(unsigned int, max_bitflips, stat); + } } /* Calculate remaining oob bytes */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 518958115182..86487dbe7358 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -129,6 +129,14 @@ typedef enum { /* Enable Hardware ECC before syndrome is read back from flash */ #define NAND_ECC_READSYN 2 +/* + * Enable generic NAND 'page erased' check. This check is only done when + * ecc.correct() returns -EBADMSG. + * Set this flag if your implementation does not fix bitflips in erased + * pages and you want to rely on the default implementation. + */ +#define NAND_ECC_GENERIC_ERASED_CHECK BIT(0) + /* Bit mask for flags passed to do_nand_read_ecc */ #define NAND_GET_DEVICE 0x80 @@ -451,6 +459,7 @@ struct nand_hw_control { * @total: total number of ECC bytes per page * @prepad: padding information for syndrome based ECC generators * @postpad: padding information for syndrome based ECC generators + * @options: ECC specific options (see NAND_ECC_XXX flags defined above) * @layout: ECC layout control struct pointer * @priv: pointer to private ECC control data * @hwctl: function to control hardware ECC generator. Must only @@ -500,6 +509,7 @@ struct nand_ecc_ctrl { int strength; int prepad; int postpad; + unsigned int options; struct nand_ecclayout *layout; void *priv; void (*hwctl)(struct mtd_info *mtd, int mode); -- cgit From d39ddbd9ef70949bb78283a067e1b3366111dd90 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Dec 2015 09:00:39 +0100 Subject: mtd: nand: add helpers to access ->priv Add two helpers to access the field reserved for private controller data. This makes it clearer what this field is reserved for and ease future refactoring. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux/mtd/nand.h') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 86487dbe7358..bdd68e22b5a5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -755,6 +755,16 @@ static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip) return &chip->mtd; } +static inline void *nand_get_controller_data(struct nand_chip *chip) +{ + return chip->priv; +} + +static inline void nand_set_controller_data(struct nand_chip *chip, void *priv) +{ + chip->priv = priv; +} + /* * NAND Flash Manufacturer ID Codes */ -- cgit