From 92752d9974882e2e5384e92668f02a134f9c7463 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 15 Jan 2016 14:46:29 +0100 Subject: mtd: mtk-nor: Drop bogus __init from mtk_nor_init() WARNING: drivers/mtd/spi-nor/mtk-quadspi.o(.text+0x77e): Section mismatch in reference from the function mtk_nor_drv_probe() to the function .init.text:mtk_nor_init() The function mtk_nor_drv_probe() references the function __init mtk_nor_init(). This is often because mtk_nor_drv_probe lacks a __init annotation or the annotation of mtk_nor_init is wrong. Drop the bogus __init from mtk_nor_init() to kill this warning. Signed-off-by: Geert Uytterhoeven Signed-off-by: Brian Norris --- drivers/mtd/spi-nor/mtk-quadspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c index d5f850d035bb..8bed1a4cb79c 100644 --- a/drivers/mtd/spi-nor/mtk-quadspi.c +++ b/drivers/mtd/spi-nor/mtk-quadspi.c @@ -371,8 +371,8 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, return ret; } -static int __init mtk_nor_init(struct mt8173_nor *mt8173_nor, - struct device_node *flash_node) +static int mtk_nor_init(struct mt8173_nor *mt8173_nor, + struct device_node *flash_node) { int ret; struct spi_nor *nor; -- cgit v1.2.3 From f9bdbd6c46c8ce0bb95f5b708a4a4a4b6b9a5917 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Jan 2016 22:38:08 +0100 Subject: mtd: nuc900_nand: read correct SMISR register The nuc900_nand driver has always passed an incorrect register address in its nuc900_check_rb() function, which cannot possibly work, and in some configurations gives us a build warning: drivers/mtd/nand/nuc900_nand.c: In function 'nuc900_check_rb': drivers/mtd/nand/nuc900_nand.c:27:23: warning: passing argument 1 of '__raw_readl' makes pointer from integer without a cast [-Wint-conversion] #define REG_SMISR 0xac drivers/mtd/nand/nuc900_nand.c:118:20: note: in expansion of macro 'REG_SMISR' val = __raw_readl(REG_SMISR); This makes sure we actually read from the register rather than from (void *)0x000000ac in user space. I suspect nobody noticed this before because the nuc900_nand_devready() function never gets called, or nobody uses this driver on an upstream kernel. Possibly even both. Signed-off-by: Arnd Bergmann Signed-off-by: Brian Norris --- drivers/mtd/nand/nuc900_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 220ddfcf29f5..dbc5b571c2bb 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -113,7 +113,7 @@ static int nuc900_check_rb(struct nuc900_nand *nand) { unsigned int val; spin_lock(&nand->lock); - val = __raw_readl(REG_SMISR); + val = __raw_readl(nand->reg + REG_SMISR); val &= READYBUSY; spin_unlock(&nand->lock); -- cgit v1.2.3 From 23819f2eaab87080b0190ed94a0728fc8ea84711 Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Sun, 10 Jan 2016 07:46:39 +0100 Subject: mtd: nand: mpc5121: use 'of_machine_is_compatible' to simplify code The current code is the same as 'of_machine_is_compatible'. So use it in order to remove a few lines of code and to be more consistent with other parts of the kernel. Signed-off-by: Christophe JAILLET Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/mpc5121_nfc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 6b93e899d4e9..5d7843ffff6a 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -626,7 +626,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) static int mpc5121_nfc_probe(struct platform_device *op) { - struct device_node *rootnode, *dn = op->dev.of_node; + struct device_node *dn = op->dev.of_node; struct clk *clk; struct device *dev = &op->dev; struct mpc5121_nfc_prv *prv; @@ -712,18 +712,15 @@ static int mpc5121_nfc_probe(struct platform_device *op) chip->ecc.mode = NAND_ECC_SOFT; /* Support external chip-select logic on ADS5121 board */ - rootnode = of_find_node_by_path("/"); - if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) { + if (of_machine_is_compatible("fsl,mpc5121ads")) { retval = ads5121_chipselect_init(mtd); if (retval) { dev_err(dev, "Chipselect init error!\n"); - of_node_put(rootnode); return retval; } chip->select_chip = ads5121_select_chip; } - of_node_put(rootnode); /* Enable NFC clock */ clk = devm_clk_get(dev, "ipg"); -- cgit v1.2.3 From c03d996900f9d063b47ef7462885a9085c8a587f Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Dec 2015 12:01:05 +0100 Subject: mtd: nand: add NAND_NEED_SCRAMBLING option flag Some MLC NANDs are sensitive to repeated patterns and require data to be scrambled in order to limit the number of bitflips. Add a new flag to let the NAND controller know about this constraint. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index bdd68e22b5a5..a13dfd5bc58b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -168,6 +168,12 @@ typedef enum { /* Device supports subpage reads */ #define NAND_SUBPAGE_READ 0x00001000 +/* + * Some MLC NANDs need data scrambling to limit bitflips caused by repeated + * patterns. + */ +#define NAND_NEED_SCRAMBLING 0x00002000 + /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG -- cgit v1.2.3 From 8ebc5637154dc58378c85ea7d0047fbc72cfc2d7 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Dec 2015 12:01:06 +0100 Subject: mtd: nand: add NAND_NEED_SCRAMBLING flag to the H27UCG8T2ATR-BC definition The H27UCG8T2ATR-BC requires an external data scrambler. Reflect this constraint in the nand_flash_ids definition. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_ids.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a8804a3da076..ccc05f5b2695 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -50,8 +50,8 @@ struct nand_flash_dev nand_flash_ids[] = { SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, {"H27UCG8T2ATR-BC 64G 3.3V 8-bit", { .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} }, - SZ_8K, SZ_8K, SZ_2M, 0, 6, 640, NAND_ECC_INFO(40, SZ_1K), - 4 }, + SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640, + NAND_ECC_INFO(40, SZ_1K), 4 }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), -- cgit v1.2.3 From 4be4e03efc7f45ec002e8eddc83c22f80fed392c Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Wed, 2 Dec 2015 12:01:07 +0100 Subject: mtd: nand: sunxi: add randomizer support Add support for the randomizer engine available in Allwinner's NFC IP. Randomization is useful to support modern NAND chips which are sensitive to repeated patterns. On such NANDs you might experience an unexpectedly high number of bitflips when you repeat the same pattern all over a given NAND block. Randomizing input data mitigate this problem by avoiding such repeated patterns. Signed-off-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/sunxi_nand.c | 287 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 261 insertions(+), 26 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 51e10a35fe08..5f700719d5c2 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -60,6 +60,7 @@ #define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) #define NFC_REG_SPARE_AREA 0x00A0 +#define NFC_REG_PAT_ID 0x00A4 #define NFC_RAM0_BASE 0x0400 #define NFC_RAM1_BASE 0x0800 @@ -538,6 +539,174 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); } +/* These seed values have been extracted from Allwinner's BSP */ +static const u16 sunxi_nfc_randomizer_page_seeds[] = { + 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, + 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, + 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, + 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, + 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, + 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, + 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, + 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, + 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, + 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, + 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, + 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, + 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, + 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, + 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, + 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, +}; + +/* + * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds + * have been generated using + * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what + * the randomizer engine does internally before de/scrambling OOB data. + * + * Those tables are statically defined to avoid calculating randomizer state + * at runtime. + */ +static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { + 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, + 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, + 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, + 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, + 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, + 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, + 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, + 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, + 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, + 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, + 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, + 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, + 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, + 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, + 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, + 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, +}; + +static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { + 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, + 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, + 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, + 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, + 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, + 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, + 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, + 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, + 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, + 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, + 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, + 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, + 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, + 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, + 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, + 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, +}; + +static u16 sunxi_nfc_randomizer_step(u16 state, int count) +{ + state &= 0x7fff; + + /* + * This loop is just a simple implementation of a Fibonacci LFSR using + * the x16 + x15 + 1 polynomial. + */ + while (count--) + state = ((state >> 1) | + (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) +{ + const u16 *seeds = sunxi_nfc_randomizer_page_seeds; + int mod = mtd->erasesize / mtd->writesize; + + if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) + mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); + + if (ecc) { + if (mtd->ecc_step_size == 512) + seeds = sunxi_nfc_randomizer_ecc512_seeds; + else + seeds = sunxi_nfc_randomizer_ecc1024_seeds; + } + + return seeds[page % mod]; +} + +static void sunxi_nfc_randomizer_config(struct mtd_info *mtd, + int page, bool ecc) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + u16 state; + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); + state = sunxi_nfc_randomizer_state(mtd, page, ecc); + ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; + writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_enable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomizer_disable(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + if (!(nand->options & NAND_NEED_SCRAMBLING)) + return; + + writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, + nfc->regs + NFC_REG_ECC_CTL); +} + +static void sunxi_nfc_randomize_bbm(struct mtd_info *mtd, int page, u8 *bbm) +{ + u16 state = sunxi_nfc_randomizer_state(mtd, page, true); + + bbm[0] ^= state; + bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); +} + +static void sunxi_nfc_randomizer_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len, + bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_write_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + +static void sunxi_nfc_randomizer_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len, bool ecc, int page) +{ + sunxi_nfc_randomizer_config(mtd, page, ecc); + sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_read_buf(mtd, buf, len); + sunxi_nfc_randomizer_disable(mtd); +} + static void sunxi_nfc_hw_ecc_enable(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); @@ -574,18 +743,20 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, int *cur_off, - unsigned int *max_bitflips) + unsigned int *max_bitflips, + bool bbm, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; + int raw_mode = 0; u32 status; int ret; if (*cur_off != data_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); - sunxi_nfc_read_buf(mtd, NULL, ecc->size); + sunxi_nfc_randomizer_read_buf(mtd, NULL, ecc->size, false, page); if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); @@ -594,25 +765,54 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (ret) return ret; + sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + sunxi_nfc_randomizer_disable(mtd); if (ret) return ret; + *cur_off = oob_off + ecc->bytes + 4; + status = readl(nfc->regs + NFC_REG_ECC_ST); + if (status & NFC_ECC_PAT_FOUND(0)) { + u8 pattern = 0xff; + + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) + pattern = 0x0; + + memset(data, pattern, ecc->size); + memset(oob, pattern, ecc->bytes + 4); + + return 1; + } + ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_read_buf(mtd, oob, ecc->bytes + 4); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); if (status & NFC_ECC_ERR(0)) { + /* + * Re-read the data with the randomizer disabled to identify + * bitflips in erased pages. + */ + if (nand->options & NAND_NEED_SCRAMBLING) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); + nand->read_buf(mtd, data, ecc->size); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand->read_buf(mtd, oob, ecc->bytes + 4); + } + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, ecc->bytes + 4, NULL, 0, ecc->strength); + if (ret >= 0) + raw_mode = 1; } else { /* * The engine protects 4 bytes of OOB data per chunk. @@ -620,6 +820,10 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, */ sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)), oob); + + /* De-randomize the Bad Block Marker. */ + if (bbm && nand->options & NAND_NEED_SCRAMBLING) + sunxi_nfc_randomize_bbm(mtd, page, oob); } if (ret < 0) { @@ -629,13 +833,12 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, *max_bitflips = max_t(unsigned int, *max_bitflips, ret); } - *cur_off = oob_off + ecc->bytes + 4; - - return 0; + return raw_mode; } static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off) + u8 *oob, int *cur_off, + bool randomize, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; @@ -649,7 +852,11 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDOUT, offset + mtd->writesize, -1); - sunxi_nfc_read_buf(mtd, oob + offset, len); + if (!randomize) + sunxi_nfc_read_buf(mtd, oob + offset, len); + else + sunxi_nfc_randomizer_read_buf(mtd, oob + offset, len, + false, page); *cur_off = mtd->oobsize + mtd->writesize; } @@ -662,7 +869,8 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, - int *cur_off) + int *cur_off, bool bbm, + int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); @@ -672,11 +880,20 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (data_off != *cur_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); - sunxi_nfc_write_buf(mtd, data, ecc->size); + sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); /* Fill OOB data in */ - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(0)); + if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { + u8 user_data[4]; + + memcpy(user_data, oob, 4); + sunxi_nfc_randomize_bbm(mtd, page, user_data); + writel(sunxi_nfc_buf_to_user_data(user_data), + nfc->regs + NFC_REG_USER_DATA(0)); + } else { + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(0)); + } if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); @@ -685,11 +902,13 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (ret) return ret; + sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | NFC_ECC_OP, nfc->regs + NFC_REG_CMD); ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0); + sunxi_nfc_randomizer_disable(mtd); if (ret) return ret; @@ -699,7 +918,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, } static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, - u8 *oob, int *cur_off) + u8 *oob, int *cur_off, + int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; @@ -713,7 +933,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDIN, offset + mtd->writesize, -1); - sunxi_nfc_write_buf(mtd, oob + offset, len); + sunxi_nfc_randomizer_write_buf(mtd, oob + offset, len, false, page); *cur_off = mtd->oobsize + mtd->writesize; } @@ -725,6 +945,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; + bool raw_mode = false; sunxi_nfc_hw_ecc_enable(mtd); @@ -736,13 +957,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, &max_bitflips); - if (ret) + &cur_off, &max_bitflips, + !i, page); + if (ret < 0) return ret; + else if (ret) + raw_mode = true; } if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); sunxi_nfc_hw_ecc_disable(mtd); @@ -767,13 +992,14 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off); + &cur_off, !i, page); if (ret) return ret; } - if (oob_required) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); sunxi_nfc_hw_ecc_disable(mtd); @@ -788,6 +1014,7 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; + bool raw_mode = false; sunxi_nfc_hw_ecc_enable(mtd); @@ -799,13 +1026,16 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off, &cur_off, - &max_bitflips); - if (ret) + &max_bitflips, !i, page); + if (ret < 0) return ret; + else if (ret) + raw_mode = true; } if (oob_required) - sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off); + sunxi_nfc_hw_ecc_read_extra_oob(mtd, chip->oob_poi, &cur_off, + !raw_mode, page); sunxi_nfc_hw_ecc_disable(mtd); @@ -829,13 +1059,15 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, - oob, oob_off, &cur_off); + oob, oob_off, &cur_off, + false, page); if (ret) return ret; } - if (oob_required) - sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, &cur_off); + if (oob_required || (chip->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_hw_ecc_write_extra_oob(mtd, chip->oob_poi, + &cur_off, page); sunxi_nfc_hw_ecc_disable(mtd); @@ -1345,6 +1577,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, if (nand->bbt_options & NAND_BBT_USE_FLASH) nand->bbt_options |= NAND_BBT_NO_OOB; + if (nand->options & NAND_NEED_SCRAMBLING) + nand->options |= NAND_NO_SUBPAGE_WRITE; + ret = sunxi_nand_chip_init_timings(chip, np); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); -- cgit v1.2.3 From 12197bf21c26e23053fa6223f3a731a965542986 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Wed, 13 Jan 2016 17:34:13 +0100 Subject: mtd: atmel_nand: Do not warn on bitflips When using multi-bit ECC, it is normal for the NAND Flash driver to correct bit errors during the life of the product. Those errors will only be cleared once a threshold has been reached, and corrections can occur regularly before this. Use only dev_dbg and not dev_info to report the bitflips, to keep the system log clean when everything works correctly. Signed-off-by: Romain Izard Acked-by: Wenyou Yang Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index bddcf83d6859..0ae6cba5bef5 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -825,7 +825,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, *(buf + byte_pos) ^= (1 << bit_pos); pos = sector_num * host->pmecc_sector_size + byte_pos; - dev_info(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", pos, bit_pos, err_byte, *(buf + byte_pos)); } else { /* Bit flip in OOB area */ @@ -835,7 +835,7 @@ static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc, ecc[tmp] ^= (1 << bit_pos); pos = tmp + nand_chip->ecc.layout->eccpos[0]; - dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", + dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n", pos, bit_pos, err_byte, ecc[tmp]); } -- cgit v1.2.3 From 46c135c208a3a80bd7f12dee0032bbc6d507a0d9 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 22 Jan 2016 18:57:13 -0800 Subject: mtd: nand: sunxi: use mtd_div_by_ws() helper Suggested-by: Richard Weinberger Signed-off-by: Brian Norris Acked-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index 5f700719d5c2..b5ea6b312df0 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -624,7 +624,7 @@ static u16 sunxi_nfc_randomizer_step(u16 state, int count) static u16 sunxi_nfc_randomizer_state(struct mtd_info *mtd, int page, bool ecc) { const u16 *seeds = sunxi_nfc_randomizer_page_seeds; - int mod = mtd->erasesize / mtd->writesize; + int mod = mtd_div_by_ws(mtd->erasesize, mtd); if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); -- cgit v1.2.3 From 54ca3cd594db58d65b3670f2af452c1104b09ac6 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 14 Jan 2016 15:44:49 +0100 Subject: mtd: nand: remove EXPORT_SYMBOL of nand_scan_bbt() Since commit 17799359e7b3fa6ef4f2bf926cd6821cf7903ecf ("mtd: nand_bbt: make nand_scan_bbt() static"), the nand_scan_bbt() function is marked as static but is still exported using EXPORT_SYMBOL(), which doesn't make much sense. This commit gets rid of the useless EXPORT_SYMBOL. Signed-off-by: Thomas Petazzoni Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_bbt.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 4b6a7085b442..2fbb523df066 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1373,5 +1373,3 @@ int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) return ret; } - -EXPORT_SYMBOL(nand_scan_bbt); -- cgit v1.2.3 From fd2a2f20c7a8173acd4858e5178eb40fd7c025b9 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 14 Jan 2016 15:44:50 +0100 Subject: mtd: onenand: make onenand_scan_bbt() static Like was done in commit 17799359e7b3fa6ef4f2bf926cd6821cf7903ecf ("mtd: nand_bbt: make nand_scan_bbt() static") for the NAND code, this commit makes the onenand_scan_bbt() function static in the OneNAND code, since it is only used in onenand_bbt.c itself. Consequently, the EXPORT_SYMBOL() and declaration in bbm.h are also removed. Signed-off-by: Thomas Petazzoni Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/onenand/onenand_bbt.c | 3 +-- include/linux/mtd/bbm.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 08d0085f3e93..5f8d47073c97 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -179,7 +179,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) * by the onenand_release function. * */ -int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) +static int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; @@ -248,5 +248,4 @@ int onenand_default_bbt(struct mtd_info *mtd) return onenand_scan_bbt(mtd, bbm->badblock_pattern); } -EXPORT_SYMBOL(onenand_scan_bbt); EXPORT_SYMBOL(onenand_default_bbt); diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 36bb6a503f19..3bf8f954b642 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -166,7 +166,6 @@ struct bbm_info { }; /* OneNAND BBT interface */ -extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); extern int onenand_default_bbt(struct mtd_info *mtd); #endif /* __LINUX_MTD_BBM_H */ -- cgit v1.2.3 From d652436102b1580d8d968a2047decbb737cc4e6d Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Thu, 14 Jan 2016 15:44:51 +0100 Subject: mtd: onenand: unexport onenand_default_bbt() The onenand_default_bbt() function is only used by the OneNAND core and not by drivers, so there is no real need to export it. Additionally, the corresponding nand_default_bbt() for regular NANDs is not exported either, so for consistency reasons, this commit removes the EXPORT_SYMBOL on onenand_default_bbt(). Signed-off-by: Thomas Petazzoni Reviewed-by: Boris Brezillon Signed-off-by: Brian Norris --- drivers/mtd/onenand/onenand_bbt.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 5f8d47073c97..680188a88130 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -247,5 +247,3 @@ int onenand_default_bbt(struct mtd_info *mtd) return onenand_scan_bbt(mtd, bbm->badblock_pattern); } - -EXPORT_SYMBOL(onenand_default_bbt); -- cgit v1.2.3 From 2a36a5c30eab9cd1c9d2d08bd27cd763325d70c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 5 Dec 2015 02:09:43 +0100 Subject: mtd: bcm47xxpart: limit scanned flash area on BCM47XX (MIPS) only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We allowed using bcm47xxpart on BCM5301X arch with commit: 9e3afa5f5c7 ("mtd: bcm47xxpart: allow enabling on ARCH_BCM_5301X") BCM5301X devices may contain some partitions in higher memory, e.g. Netgear R8000 has board_data at 0x2600000. To detect them we should use size limit on MIPS only. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 8282f47bcf5d..b06d4c4553ed 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -118,8 +118,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* Parse block by block looking for magics */ for (offset = 0; offset <= master->size - blocksize; offset += blocksize) { - /* Nothing more in higher memory */ - if (offset >= 0x2000000) + /* Nothing more in higher memory on BCM47XX (MIPS) */ + if (config_enabled(CONFIG_BCM47XX) && offset >= 0x2000000) break; if (curr_part >= BCM47XXPART_MAX_PARTS) { -- cgit v1.2.3 From 36bcc0c9c2bc8f56569cd735ba531a51358d7c2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sun, 6 Dec 2015 11:31:38 +0100 Subject: mtd: bcm47xxpart: don't fail because of bit-flips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bit-flip errors may occur on NAND flashes and are harmless. Handle them gracefully as read content is still reliable and can be parsed. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index b06d4c4553ed..845dd27d9f41 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -66,11 +66,13 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, { uint32_t buf; size_t bytes_read; + int err; - if (mtd_read(master, offset, sizeof(buf), &bytes_read, - (uint8_t *)&buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset, sizeof(buf), &bytes_read, + (uint8_t *)&buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); goto out_default; } @@ -95,6 +97,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, int trx_part = -1; int last_trx_part = -1; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; + int err; /* * Some really old flashes (like AT45DB*) had smaller erasesize-s, but @@ -128,10 +131,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Read beginning of the block */ - if (mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, - &bytes_read, (uint8_t *)buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset, BCM47XXPART_BYTES_TO_READ, + &bytes_read, (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); continue; } @@ -254,10 +258,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } /* Read middle of the block */ - if (mtd_read(master, offset + 0x8000, 0x4, - &bytes_read, (uint8_t *)buf) < 0) { - pr_err("mtd_read error while parsing (offset: 0x%X)!\n", - offset); + err = mtd_read(master, offset + 0x8000, 0x4, &bytes_read, + (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while parsing (offset: 0x%X): %d\n", + offset, err); continue; } @@ -277,10 +282,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, } offset = master->size - possible_nvram_sizes[i]; - if (mtd_read(master, offset, 0x4, &bytes_read, - (uint8_t *)buf) < 0) { - pr_err("mtd_read error while reading at offset 0x%X!\n", - offset); + err = mtd_read(master, offset, 0x4, &bytes_read, + (uint8_t *)buf); + if (err && !mtd_is_bitflip(err)) { + pr_err("mtd_read error while reading (offset 0x%X): %d\n", + offset, err); continue; } -- cgit v1.2.3 From 026918e71111afe88382f8d800a852d3e36bf3d4 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 2 Dec 2015 16:47:40 -0600 Subject: mtd: nand: gpmi: add gpmi dsm supend/resume support i.MX6SX supports deep sleep mode(DSM) that may turn off GPMI/BCH power during suspend, add gpmi nand suspend/resume function to release DMA channel in suspend function and re-init GPMI/BCH controller during resume function. Although it is not necessary to restore GPMI/BCH registers value for i.MX6QDL, the code doesn't distinguish different platforms to keep the code simple. Signed-off-by: Huang Shijie Signed-off-by: Han Xu Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 47 +++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 235ddcb58f39..9c311b06018b 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -1,7 +1,7 @@ /* * Freescale GPMI NAND Flash Driver * - * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. * * This program is free software; you can redistribute it and/or modify @@ -2033,9 +2033,54 @@ static int gpmi_nand_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int gpmi_pm_suspend(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + + release_dma_channels(this); + return 0; +} + +static int gpmi_pm_resume(struct device *dev) +{ + struct gpmi_nand_data *this = dev_get_drvdata(dev); + int ret; + + ret = acquire_dma_channels(this); + if (ret < 0) + return ret; + + /* re-init the GPMI registers */ + this->flags &= ~GPMI_TIMING_INIT_OK; + ret = gpmi_init(this); + if (ret) { + dev_err(this->dev, "Error setting GPMI : %d\n", ret); + return ret; + } + + /* re-init the BCH registers */ + ret = bch_set_geometry(this); + if (ret) { + dev_err(this->dev, "Error setting BCH : %d\n", ret); + return ret; + } + + /* re-init others */ + gpmi_extra_init(this); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops gpmi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) +}; + static struct platform_driver gpmi_nand_driver = { .driver = { .name = "gpmi-nand", + .pm = &gpmi_pm_ops, .of_match_table = gpmi_nand_id_table, }, .probe = gpmi_nand_probe, -- cgit v1.2.3 From b8b0e465ddb0c9cb16089ddfed68a8569e006465 Mon Sep 17 00:00:00 2001 From: Han Xu Date: Wed, 2 Dec 2015 16:47:43 -0600 Subject: mtd: nand: gpmi: may use minimum required ecc for 744 oobsize NAND By default NAND driver will choose the highest ecc strength that oob could contain, in this case, for some 8K+744 NAND flash, the ecc strength will be up to 52bit, which beyonds the i.MX6QDL BCH capability (40bit). This patch allows the NAND driver try to use minimum required ecc strength if it failed to use the highest ecc, even without explicitly claiming "fsl,use-minimum-ecc" in dts. Signed-off-by: Han Xu Acked-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 9c311b06018b..8122c699ccf2 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -136,7 +136,7 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) * * We may have available oob space in this case. */ -static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) +static int set_geometry_by_ecc_info(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; struct nand_chip *chip = &this->nand; @@ -145,7 +145,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) unsigned int block_mark_bit_offset; if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return false; + return -EINVAL; switch (chip->ecc_step_ds) { case SZ_512: @@ -158,19 +158,19 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) dev_err(this->dev, "unsupported nand chip. ecc bits : %d, ecc size : %d\n", chip->ecc_strength_ds, chip->ecc_step_ds); - return false; + return -EINVAL; } geo->ecc_chunk_size = chip->ecc_step_ds; geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); if (!gpmi_check_ecc(this)) - return false; + return -EINVAL; /* Keep the C >= O */ if (geo->ecc_chunk_size < mtd->oobsize) { dev_err(this->dev, "unsupported nand chip. ecc size: %d, oob size : %d\n", chip->ecc_step_ds, mtd->oobsize); - return false; + return -EINVAL; } /* The default value, see comment in the legacy_set_geometry(). */ @@ -242,7 +242,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) + ALIGN(geo->ecc_chunk_count, 4); if (!this->swap_block_mark) - return true; + return 0; /* For bit swap. */ block_mark_bit_offset = mtd->writesize * 8 - @@ -251,7 +251,7 @@ static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) geo->block_mark_byte_offset = block_mark_bit_offset / 8; geo->block_mark_bit_offset = block_mark_bit_offset % 8; - return true; + return 0; } static int legacy_set_geometry(struct gpmi_nand_data *this) @@ -285,7 +285,8 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) geo->ecc_strength = get_ecc_strength(this); if (!gpmi_check_ecc(this)) { dev_err(this->dev, - "required ecc strength of the NAND chip: %d is not supported by the GPMI controller (%d)\n", + "ecc strength: %d cannot be supported by the controller (%d)\n" + "try to use minimum ecc strength that NAND chip required\n", geo->ecc_strength, this->devdata->bch_max_ecc_strength); return -EINVAL; @@ -366,10 +367,11 @@ static int legacy_set_geometry(struct gpmi_nand_data *this) int common_nfc_set_geometry(struct gpmi_nand_data *this) { - if (of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc") - && set_geometry_by_ecc_info(this)) - return 0; - return legacy_set_geometry(this); + if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) + || legacy_set_geometry(this)) + return set_geometry_by_ecc_info(this); + + return 0; } struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) -- cgit v1.2.3 From e88b7f7d6ca47531af602cfb6abdb31ea13eabc3 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Fri, 15 Jan 2016 11:34:56 +0100 Subject: mtd: atmel_nand: Simplify error messages The error messages when the ECC controller is misconfigured through the device tree are very precise. As a result they can (and will) get obsolete when new revisions of the controller appear. Simplify them before adding the support for the new revision. Signed-off-by: Romain Izard Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 0ae6cba5bef5..ad9f4b062049 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1550,7 +1550,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host, if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && (val != 24)) { dev_err(host->dev, - "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", + "Required ECC strength not supported: %u\n", val); return -EINVAL; } @@ -1560,7 +1560,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host, if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { if ((val != 512) && (val != 1024)) { dev_err(host->dev, - "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", + "Required ECC sector size not supported: %u\n", val); return -EINVAL; } -- cgit v1.2.3 From 8ce06d379a06b4d5c9f944bb60e80b084d16bfc0 Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Fri, 15 Jan 2016 11:34:57 +0100 Subject: mtd: atmel_nand: Use of_device_get_match_data Remove the need for forward declaration and the risk for a null pointer when accessing the private part of the compatible match table, by using the newly introduced of_device_get_match_data function. Signed-off-by: Romain Izard Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index ad9f4b062049..affe7a7e9ad7 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1486,8 +1486,6 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); } -static const struct of_device_id atmel_nand_dt_ids[]; - static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { @@ -1498,7 +1496,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host, enum of_gpio_flags flags = 0; host->caps = (struct atmel_nand_caps *) - of_match_device(atmel_nand_dt_ids, host->dev)->data; + of_device_get_match_data(host->dev); if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { if (val >= 32) { -- cgit v1.2.3