From ad9679f3811899fd1c21dc7bdd715e8e1cfb46b9 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 26 Dec 2022 13:01:58 +0900 Subject: [PATCH 01/33] mtd: spi-nor: sfdp: Fix index value for SCCR dwords Array index for SCCR 22th DOWRD should be 21. Fixes: 981a8d60e01f ("mtd: spi-nor: Parse SFDP SCCR Map") Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/d8a2a77c2c95cf776e7dcae6392d29fdcf5d6307.1672026365.git.Takahiro.Kuwano@infineon.com --- drivers/mtd/spi-nor/sfdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 8434f654eca1..5df2fcba5483 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -1228,7 +1228,7 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, le32_to_cpu_array(dwords, sccr_header->length); - if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[22])) + if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[21])) nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; out: From 86d4cdf88c81778bb4cd5c4bd7f8a6c64e69491f Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 26 Dec 2022 13:01:59 +0900 Subject: [PATCH 02/33] mtd: spi-nor: sfdp: Rename BFPT_DWORD() macro to SFDP_DWORD() BFPT_DWORD() converts 1-based indexing to 0-based indexing for C arrays, and is used in BFPT parse. Per JESD216F.02, the conversion is applicable to other parameter tables than BFPT. This patch renames the macro to SFDP_DWORD() so that we can use it for other parameter tables than BFPT. Suggested-by: Tudor Ambarus Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/e42feac840fe3a31187419e91b2d514d9f259d15.1672026365.git.Takahiro.Kuwano@infineon.com --- drivers/mtd/spi-nor/issi.c | 2 +- drivers/mtd/spi-nor/macronix.c | 2 +- drivers/mtd/spi-nor/sfdp.c | 44 +++++++++++++++++----------------- drivers/mtd/spi-nor/sfdp.h | 9 ++++--- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index a0ddad2afffc..400e2b42f45a 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -18,7 +18,7 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY. * Overwrite the number of address bytes advertised by the BFPT. */ - if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == + if ((bfpt->dwords[SFDP_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) nor->params->addr_nbytes = 4; diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index d81a4cb2812b..6853ec9ae65d 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -22,7 +22,7 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor, * seems that the F version advertises support for Fast Read 4-4-4 in * its BFPT table. */ - if (bfpt->dwords[BFPT_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) + if (bfpt->dwords[SFDP_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4) nor->flags |= SNOR_F_4B_OPCODES; return 0; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 5df2fcba5483..5c2ab868707b 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -242,64 +242,64 @@ static const struct sfdp_bfpt_read sfdp_bfpt_reads[] = { /* Fast Read 1-1-2 */ { SNOR_HWCAPS_READ_1_1_2, - BFPT_DWORD(1), BIT(16), /* Supported bit */ - BFPT_DWORD(4), 0, /* Settings */ + SFDP_DWORD(1), BIT(16), /* Supported bit */ + SFDP_DWORD(4), 0, /* Settings */ SNOR_PROTO_1_1_2, }, /* Fast Read 1-2-2 */ { SNOR_HWCAPS_READ_1_2_2, - BFPT_DWORD(1), BIT(20), /* Supported bit */ - BFPT_DWORD(4), 16, /* Settings */ + SFDP_DWORD(1), BIT(20), /* Supported bit */ + SFDP_DWORD(4), 16, /* Settings */ SNOR_PROTO_1_2_2, }, /* Fast Read 2-2-2 */ { SNOR_HWCAPS_READ_2_2_2, - BFPT_DWORD(5), BIT(0), /* Supported bit */ - BFPT_DWORD(6), 16, /* Settings */ + SFDP_DWORD(5), BIT(0), /* Supported bit */ + SFDP_DWORD(6), 16, /* Settings */ SNOR_PROTO_2_2_2, }, /* Fast Read 1-1-4 */ { SNOR_HWCAPS_READ_1_1_4, - BFPT_DWORD(1), BIT(22), /* Supported bit */ - BFPT_DWORD(3), 16, /* Settings */ + SFDP_DWORD(1), BIT(22), /* Supported bit */ + SFDP_DWORD(3), 16, /* Settings */ SNOR_PROTO_1_1_4, }, /* Fast Read 1-4-4 */ { SNOR_HWCAPS_READ_1_4_4, - BFPT_DWORD(1), BIT(21), /* Supported bit */ - BFPT_DWORD(3), 0, /* Settings */ + SFDP_DWORD(1), BIT(21), /* Supported bit */ + SFDP_DWORD(3), 0, /* Settings */ SNOR_PROTO_1_4_4, }, /* Fast Read 4-4-4 */ { SNOR_HWCAPS_READ_4_4_4, - BFPT_DWORD(5), BIT(4), /* Supported bit */ - BFPT_DWORD(7), 16, /* Settings */ + SFDP_DWORD(5), BIT(4), /* Supported bit */ + SFDP_DWORD(7), 16, /* Settings */ SNOR_PROTO_4_4_4, }, }; static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = { /* Erase Type 1 in DWORD8 bits[15:0] */ - {BFPT_DWORD(8), 0}, + {SFDP_DWORD(8), 0}, /* Erase Type 2 in DWORD8 bits[31:16] */ - {BFPT_DWORD(8), 16}, + {SFDP_DWORD(8), 16}, /* Erase Type 3 in DWORD9 bits[15:0] */ - {BFPT_DWORD(9), 0}, + {SFDP_DWORD(9), 0}, /* Erase Type 4 in DWORD9 bits[31:16] */ - {BFPT_DWORD(9), 16}, + {SFDP_DWORD(9), 16}, }; /** @@ -458,7 +458,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, le32_to_cpu_array(bfpt.dwords, BFPT_DWORD_MAX); /* Number of address bytes. */ - switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { + switch (bfpt.dwords[SFDP_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: params->addr_nbytes = 3; @@ -475,7 +475,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, } /* Flash Memory Density (in bits). */ - val = bfpt.dwords[BFPT_DWORD(2)]; + val = bfpt.dwords[SFDP_DWORD(2)]; if (val & BIT(31)) { val &= ~BIT(31); @@ -555,13 +555,13 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); /* Page size: this field specifies 'N' so the page size = 2^N bytes. */ - val = bfpt.dwords[BFPT_DWORD(11)]; + val = bfpt.dwords[SFDP_DWORD(11)]; val &= BFPT_DWORD11_PAGE_SIZE_MASK; val >>= BFPT_DWORD11_PAGE_SIZE_SHIFT; params->page_size = 1U << val; /* Quad Enable Requirements. */ - switch (bfpt.dwords[BFPT_DWORD(15)] & BFPT_DWORD15_QER_MASK) { + switch (bfpt.dwords[SFDP_DWORD(15)] & BFPT_DWORD15_QER_MASK) { case BFPT_DWORD15_QER_NONE: params->quad_enable = NULL; break; @@ -608,7 +608,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, } /* Soft Reset support. */ - if (bfpt.dwords[BFPT_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) + if (bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST) nor->flags |= SNOR_F_SOFT_RESET; /* Stop here if not JESD216 rev C or later. */ @@ -616,7 +616,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); /* 8D-8D-8D command extension. */ - switch (bfpt.dwords[BFPT_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { + switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { case BFPT_DWORD18_CMD_EXT_REP: nor->cmd_ext_type = SPI_NOR_EXT_REPEAT; break; diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index c1969f0a2f46..500659b35655 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -13,13 +13,12 @@ #define SFDP_JESD216A_MINOR 5 #define SFDP_JESD216B_MINOR 6 +/* SFDP DWORDS are indexed from 1 but C arrays are indexed from 0. */ +#define SFDP_DWORD(i) ((i) - 1) + /* Basic Flash Parameter Table */ -/* - * JESD216 rev D defines a Basic Flash Parameter Table of 20 DWORDs. - * They are indexed from 1 but C arrays are indexed from 0. - */ -#define BFPT_DWORD(i) ((i) - 1) +/* JESD216 rev D defines a Basic Flash Parameter Table of 20 DWORDs. */ #define BFPT_DWORD_MAX 20 struct sfdp_bfpt { From 55398beb0846a9eca38db5250c9e7b3e59d407ca Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 26 Dec 2022 13:02:00 +0900 Subject: [PATCH 03/33] mtd: spi-nor: sfdp: Use SFDP_DWORD() macro for optional parameter tables Change 0-based indexing values of parameter tables to 1-based ones by SFDP_DWORD() macro. Suggested-by: Tudor Ambarus Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/48cb008b40fdef4bf7f87e37029efaa2bfefa9ef.1672026365.git.Takahiro.Kuwano@infineon.com --- drivers/mtd/spi-nor/sfdp.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 5c2ab868707b..3acc01c3a900 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -1004,7 +1004,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, discard_hwcaps |= read->hwcaps; if ((params->hwcaps.mask & read->hwcaps) && - (dwords[0] & read->supported_bit)) + (dwords[SFDP_DWORD(1)] & read->supported_bit)) read_hwcaps |= read->hwcaps; } @@ -1023,7 +1023,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * authority for specifying Page Program support. */ discard_hwcaps |= program->hwcaps; - if (dwords[0] & program->supported_bit) + if (dwords[SFDP_DWORD(1)] & program->supported_bit) pp_hwcaps |= program->hwcaps; } @@ -1035,7 +1035,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { const struct sfdp_4bait *erase = &erases[i]; - if (dwords[0] & erase->supported_bit) + if (dwords[SFDP_DWORD(1)] & erase->supported_bit) erase_mask |= BIT(i); } @@ -1086,7 +1086,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { if (erase_mask & BIT(i)) - erase_type[i].opcode = (dwords[1] >> + erase_type[i].opcode = (dwords[SFDP_DWORD(2)] >> erase_type[i].idx * 8) & 0xFF; else spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); @@ -1145,15 +1145,15 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, le32_to_cpu_array(dwords, profile1_header->length); /* Get 8D-8D-8D fast read opcode and dummy cycles. */ - opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, dwords[0]); + opcode = FIELD_GET(PROFILE1_DWORD1_RD_FAST_CMD, dwords[SFDP_DWORD(1)]); /* Set the Read Status Register dummy cycles and dummy address bytes. */ - if (dwords[0] & PROFILE1_DWORD1_RDSR_DUMMY) + if (dwords[SFDP_DWORD(1)] & PROFILE1_DWORD1_RDSR_DUMMY) nor->params->rdsr_dummy = 8; else nor->params->rdsr_dummy = 4; - if (dwords[0] & PROFILE1_DWORD1_RDSR_ADDR_BYTES) + if (dwords[SFDP_DWORD(1)] & PROFILE1_DWORD1_RDSR_ADDR_BYTES) nor->params->rdsr_addr_nbytes = 4; else nor->params->rdsr_addr_nbytes = 0; @@ -1167,13 +1167,16 @@ static int spi_nor_parse_profile1(struct spi_nor *nor, * Default to PROFILE1_DUMMY_DEFAULT if we don't find anything, and let * flashes set the correct value if needed in their fixup hooks. */ - dummy = FIELD_GET(PROFILE1_DWORD4_DUMMY_200MHZ, dwords[3]); + dummy = FIELD_GET(PROFILE1_DWORD4_DUMMY_200MHZ, dwords[SFDP_DWORD(4)]); if (!dummy) - dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_166MHZ, dwords[4]); + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_166MHZ, + dwords[SFDP_DWORD(5)]); if (!dummy) - dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_133MHZ, dwords[4]); + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_133MHZ, + dwords[SFDP_DWORD(5)]); if (!dummy) - dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_100MHZ, dwords[4]); + dummy = FIELD_GET(PROFILE1_DWORD5_DUMMY_100MHZ, + dwords[SFDP_DWORD(5)]); if (!dummy) dev_dbg(nor->dev, "Can't find dummy cycles from Profile 1.0 table\n"); @@ -1228,7 +1231,8 @@ static int spi_nor_parse_sccr(struct spi_nor *nor, le32_to_cpu_array(dwords, sccr_header->length); - if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, dwords[21])) + if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE, + dwords[SFDP_DWORD(22)])) nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; out: From 68c18dae68881054ed5426c00f0ed351b1db9bc0 Mon Sep 17 00:00:00 2001 From: Aviram Dali Date: Fri, 16 Dec 2022 18:27:15 +0200 Subject: [PATCH 04/33] mtd: rawnand: marvell: add missing layouts A missing layouts were added to the driver to support NAND flashes with ECC layouts of 12 or 16 with page sized of 2048, 4096 or 8192. Usually theses are rare layouts, but in Marvell AC5 driver, the ECC level is set according to the spare area, so we may use these layouts more frequently. Signed-off-by: Aviram Dali Signed-off-by: Vadym Kochan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221216162715.3230766-1-vadym.kochan@plvision.eu --- drivers/mtd/nand/raw/marvell_nand.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 42c64dcea767..3034916d2e25 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -288,10 +288,17 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = { MARVELL_LAYOUT( 2048, 512, 1, 1, 1, 2048, 40, 24, 0, 0, 0), MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0), MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30), + MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,64, 30), + MARVELL_LAYOUT( 2048, 512, 12, 3, 2, 704, 0, 30,640, 0, 30), + MARVELL_LAYOUT( 2048, 512, 16, 5, 4, 512, 0, 30, 0, 32, 30), MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0), MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30), + MARVELL_LAYOUT( 4096, 512, 12, 6, 5, 704, 0, 30,576, 32, 30), + MARVELL_LAYOUT( 4096, 512, 16, 9, 8, 512, 0, 30, 0, 32, 30), MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0), MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30), + MARVELL_LAYOUT( 8192, 512, 12, 12, 11, 704, 0, 30,448, 64, 30), + MARVELL_LAYOUT( 8192, 512, 16, 17, 16, 512, 0, 30, 0, 32, 30), }; /** From 6d7fea226b238b95d69faba49b8faf73d6e8c469 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 29 Dec 2022 12:15:20 -0600 Subject: [PATCH 05/33] mtd: rawnand: sunxi: Clean up chips after failed init If a chip fails to initialize, we need to clean up any chips that were already initialized/registered. Fixes: 1fef62c1423b ("mtd: nand: add sunxi NAND flash controller support") Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221229181526.53766-2-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 39 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index ea953e31933e..2ee86f7b0905 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1950,6 +1950,25 @@ static const struct nand_controller_ops sunxi_nand_controller_ops = { .exec_op = sunxi_nfc_exec_op, }; +static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) +{ + struct sunxi_nand_chip *sunxi_nand; + struct nand_chip *chip; + int ret; + + while (!list_empty(&nfc->chips)) { + sunxi_nand = list_first_entry(&nfc->chips, + struct sunxi_nand_chip, + node); + chip = &sunxi_nand->nand; + ret = mtd_device_unregister(nand_to_mtd(chip)); + WARN_ON(ret); + nand_cleanup(chip); + sunxi_nand_ecc_cleanup(sunxi_nand); + list_del(&sunxi_nand->node); + } +} + static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, struct device_node *np) { @@ -2053,6 +2072,7 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) ret = sunxi_nand_chip_init(dev, nfc, nand_np); if (ret) { of_node_put(nand_np); + sunxi_nand_chips_cleanup(nfc); return ret; } } @@ -2060,25 +2080,6 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) return 0; } -static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) -{ - struct sunxi_nand_chip *sunxi_nand; - struct nand_chip *chip; - int ret; - - while (!list_empty(&nfc->chips)) { - sunxi_nand = list_first_entry(&nfc->chips, - struct sunxi_nand_chip, - node); - chip = &sunxi_nand->nand; - ret = mtd_device_unregister(nand_to_mtd(chip)); - WARN_ON(ret); - nand_cleanup(chip); - sunxi_nand_ecc_cleanup(sunxi_nand); - list_del(&sunxi_nand->node); - } -} - static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) { int ret; From 59186a402ab07d205428ee9f62ff0e95ecb06b63 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 29 Dec 2022 12:15:21 -0600 Subject: [PATCH 06/33] mtd: rawnand: sunxi: Remove an unnecessary check sunxi_nand->nsels cannot be zero, so the second check implies the first. Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221229181526.53766-3-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 2ee86f7b0905..8b221f9f10a7 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -421,7 +421,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) struct sunxi_nand_chip_sel *sel; u32 ctl; - if (cs > 0 && cs >= sunxi_nand->nsels) + if (cs >= sunxi_nand->nsels) return; ctl = readl(nfc->regs + NFC_REG_CTL) & From 85e8177e581964a727410c781410f626e1cb4c25 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 29 Dec 2022 12:15:22 -0600 Subject: [PATCH 07/33] mtd: rawnand: sunxi: Remove an unnecessary check Each chip is required to have a unique CS number ("reg" property) in the range 0-7, so there is no need to separately count the number of chips. Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221229181526.53766-4-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 8b221f9f10a7..1bddeb1be66f 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2060,14 +2060,8 @@ static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) { struct device_node *np = dev->of_node; struct device_node *nand_np; - int nchips = of_get_child_count(np); int ret; - if (nchips > 8) { - dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); - return -EINVAL; - } - for_each_child_of_node(np, nand_np) { ret = sunxi_nand_chip_init(dev, nfc, nand_np); if (ret) { From 34569d869532b54d6e360d224a0254dcdd6a1785 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Thu, 29 Dec 2022 12:15:24 -0600 Subject: [PATCH 08/33] mtd: rawnand: sunxi: Fix the size of the last OOB region The previous code assigned to the wrong structure member. Fixes: c66811e6d350 ("mtd: nand: sunxi: switch to mtd_ooblayout_ops") Signed-off-by: Samuel Holland Acked-By: Dhruva Gole Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221229181526.53766-6-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 1bddeb1be66f..e673ac46f2e8 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1609,7 +1609,7 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, if (section < ecc->steps) oobregion->length = 4; else - oobregion->offset = mtd->oobsize - oobregion->offset; + oobregion->length = mtd->oobsize - oobregion->offset; return 0; } From a30144c02c84614ed160ab70e38fcf7b024a1bb0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 15 Dec 2022 17:47:30 +0100 Subject: [PATCH 09/33] mtd: dataflash: remove duplicate SPI ID table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building with -Werror=override-init reveals that two patches added the same device ID table to this driver: drivers/mtd/devices/mtd_dataflash.c:946:27: error: initialized field overwritten [-Werror=override-init] 946 | .id_table = dataflash_spi_ids, | ^~~~~~~~~~~~~~~~~ drivers/mtd/devices/mtd_dataflash.c:946:27: note: (near initialization for 'dataflash_driver.id_table') Remove one of the copies. Fixes: 27a030e87292 ("mtd: dataflash: Add device-tree SPI IDs") Fixes: ac4f83482afb ("mtd: dataflash: Add SPI ID table") Signed-off-by: Arnd Bergmann Acked-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20221215164736.1315815-1-arnd@kernel.org --- drivers/mtd/devices/mtd_dataflash.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 25bad4318305..3bbaa590c768 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -96,13 +96,6 @@ struct dataflash { struct mtd_info mtd; }; -static const struct spi_device_id dataflash_dev_ids[] = { - { "at45" }, - { "dataflash" }, - { }, -}; -MODULE_DEVICE_TABLE(spi, dataflash_dev_ids); - #ifdef CONFIG_OF static const struct of_device_id dataflash_dt_ids[] = { { .compatible = "atmel,at45", }, @@ -939,8 +932,6 @@ static struct spi_driver dataflash_driver = { .name = "mtd_dataflash", .of_match_table = of_match_ptr(dataflash_dt_ids), }, - .id_table = dataflash_dev_ids, - .probe = dataflash_probe, .remove = dataflash_remove, .id_table = dataflash_spi_ids, From 718004a5972c83cbcc4b649ec34ff314de373650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 2 Jan 2023 13:40:51 +0100 Subject: [PATCH 10/33] mtd: rawnand: pasemi: Don't use static data to track per-device state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now the pasemi nand driver only supported a single device instance. However the check for that was racy because two parallel calls of pasemi_nand_probe() could pass the check if (pasemi_nand_mtd) return -ENODEV; before any of them assigns a non-NULL value to it. So rework the driver to make use of per-device driver data. As an intended side effect the driver can bind more than one device and also gets rid of the check if (!pasemi_nand_mtd) return 0; in the remove callback that could only ever trigger after the above race happened. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230102124051.1508424-1-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/pasemi_nand.c | 63 +++++++++++++++++------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/nand/raw/pasemi_nand.c b/drivers/mtd/nand/raw/pasemi_nand.c index c176036453ed..f7ef6ca06ca9 100644 --- a/drivers/mtd/nand/raw/pasemi_nand.c +++ b/drivers/mtd/nand/raw/pasemi_nand.c @@ -26,9 +26,12 @@ #define CLE_PIN_CTL 15 #define ALE_PIN_CTL 14 -static unsigned int lpcctl; -static struct mtd_info *pasemi_nand_mtd; -static struct nand_controller controller; +struct pasemi_ddata { + struct nand_chip chip; + unsigned int lpcctl; + struct nand_controller controller; +}; + static const char driver_name[] = "pasemi-nand"; static void pasemi_read_buf(struct nand_chip *chip, u_char *buf, int len) @@ -55,6 +58,8 @@ static void pasemi_write_buf(struct nand_chip *chip, const u_char *buf, static void pasemi_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl) { + struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip); + if (cmd == NAND_CMD_NONE) return; @@ -65,12 +70,14 @@ static void pasemi_hwcontrol(struct nand_chip *chip, int cmd, /* Push out posted writes */ eieio(); - inl(lpcctl); + inl(ddata->lpcctl); } static int pasemi_device_ready(struct nand_chip *chip) { - return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); + struct pasemi_ddata *ddata = container_of(chip, struct pasemi_ddata, chip); + + return !!(inl(ddata->lpcctl) & LBICTRL_LPCCTL_NR); } static int pasemi_attach_chip(struct nand_chip *chip) @@ -93,29 +100,31 @@ static int pasemi_nand_probe(struct platform_device *ofdev) struct device_node *np = dev->of_node; struct resource res; struct nand_chip *chip; + struct nand_controller *controller; int err = 0; + struct pasemi_ddata *ddata; + struct mtd_info *pasemi_nand_mtd; err = of_address_to_resource(np, 0, &res); if (err) return -EINVAL; - /* We only support one device at the moment */ - if (pasemi_nand_mtd) - return -ENODEV; - dev_dbg(dev, "pasemi_nand at %pR\n", &res); /* Allocate memory for MTD device structure and private data */ - chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); - if (!chip) { + ddata = kzalloc(sizeof(*ddata), GFP_KERNEL); + if (!ddata) { err = -ENOMEM; goto out; } + platform_set_drvdata(ofdev, ddata); + chip = &ddata->chip; + controller = &ddata->controller; - controller.ops = &pasemi_ops; - nand_controller_init(&controller); - chip->controller = &controller; + controller->ops = &pasemi_ops; + nand_controller_init(controller); + chip->controller = controller; pasemi_nand_mtd = nand_to_mtd(chip); @@ -136,10 +145,10 @@ static int pasemi_nand_probe(struct platform_device *ofdev) goto out_ior; } - lpcctl = pci_resource_start(pdev, 0); + ddata->lpcctl = pci_resource_start(pdev, 0); pci_dev_put(pdev); - if (!request_region(lpcctl, 4, driver_name)) { + if (!request_region(ddata->lpcctl, 4, driver_name)) { err = -EBUSY; goto out_ior; } @@ -172,45 +181,43 @@ static int pasemi_nand_probe(struct platform_device *ofdev) } dev_info(dev, "PA Semi NAND flash at %pR, control at I/O %x\n", &res, - lpcctl); + ddata->lpcctl); return 0; out_cleanup_nand: nand_cleanup(chip); out_lpc: - release_region(lpcctl, 4); + release_region(ddata->lpcctl, 4); out_ior: iounmap(chip->legacy.IO_ADDR_R); out_mtd: - kfree(chip); + kfree(ddata); out: return err; } static int pasemi_nand_remove(struct platform_device *ofdev) { - struct nand_chip *chip; + struct pasemi_ddata *ddata = platform_get_drvdata(ofdev); + struct mtd_info *pasemi_nand_mtd; int ret; + struct nand_chip *chip; - if (!pasemi_nand_mtd) - return 0; - - chip = mtd_to_nand(pasemi_nand_mtd); + chip = &ddata->chip; + pasemi_nand_mtd = nand_to_mtd(chip); /* Release resources, unregister device */ ret = mtd_device_unregister(pasemi_nand_mtd); WARN_ON(ret); nand_cleanup(chip); - release_region(lpcctl, 4); + release_region(ddata->lpcctl, 4); iounmap(chip->legacy.IO_ADDR_R); /* Free the MTD device structure */ - kfree(chip); - - pasemi_nand_mtd = NULL; + kfree(ddata); return 0; } From 568494db680991d6a09b5ab92dcddb7dfd3cbe25 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 5 Jan 2023 14:46:15 +0100 Subject: [PATCH 11/33] mtd: remove tmio_nand driver With the TMIO MFD drivers gone, the NAND support is also obsolete and can be removed. Cc: Miquel Raynal Cc: Richard Weinberger Cc: Vignesh Raghavendra Cc: linux-mtd@lists.infradead.org Signed-off-by: Arnd Bergmann Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/Kconfig | 7 - drivers/mtd/nand/raw/Makefile | 1 - drivers/mtd/nand/raw/tmio_nand.c | 533 ------------------------------- 3 files changed, 541 deletions(-) delete mode 100644 drivers/mtd/nand/raw/tmio_nand.c diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 98ea1c9e65c8..75a445200434 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -193,13 +193,6 @@ config MTD_NAND_PASEMI Enables support for NAND Flash interface on PA Semi PWRficient based boards -config MTD_NAND_TMIO - tristate "Toshiba Mobile IO NAND controller" - depends on MFD_TMIO - help - Support for NAND flash connected to a Toshiba Mobile IO - Controller in some PDAs, including the Sharp SL6000x. - source "drivers/mtd/nand/raw/brcmnand/Kconfig" config MTD_NAND_BCM47XXNFLASH diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index fa1d00120310..917cdfb815b9 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -23,7 +23,6 @@ omap2_nand-objs := omap2.o obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o -obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o diff --git a/drivers/mtd/nand/raw/tmio_nand.c b/drivers/mtd/nand/raw/tmio_nand.c deleted file mode 100644 index 8f1a42bf199c..000000000000 --- a/drivers/mtd/nand/raw/tmio_nand.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Toshiba TMIO NAND flash controller driver - * - * Slightly murky pre-git history of the driver: - * - * Copyright (c) Ian Molton 2004, 2005, 2008 - * Original work, independent of sharps code. Included hardware ECC support. - * Hard ECC did not work for writes in the early revisions. - * Copyright (c) Dirk Opfer 2005. - * Modifications developed from sharps code but - * NOT containing any, ported onto Ians base. - * Copyright (c) Chris Humbert 2005 - * Copyright (c) Dmitry Baryshkov 2008 - * Minor fixes - * - * Parts copyright Sebastian Carlier - * - * This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*--------------------------------------------------------------------------*/ - -/* - * NAND Flash Host Controller Configuration Register - */ -#define CCR_COMMAND 0x04 /* w Command */ -#define CCR_BASE 0x10 /* l NAND Flash Control Reg Base Addr */ -#define CCR_INTP 0x3d /* b Interrupt Pin */ -#define CCR_INTE 0x48 /* b Interrupt Enable */ -#define CCR_EC 0x4a /* b Event Control */ -#define CCR_ICC 0x4c /* b Internal Clock Control */ -#define CCR_ECCC 0x5b /* b ECC Control */ -#define CCR_NFTC 0x60 /* b NAND Flash Transaction Control */ -#define CCR_NFM 0x61 /* b NAND Flash Monitor */ -#define CCR_NFPSC 0x62 /* b NAND Flash Power Supply Control */ -#define CCR_NFDC 0x63 /* b NAND Flash Detect Control */ - -/* - * NAND Flash Control Register - */ -#define FCR_DATA 0x00 /* bwl Data Register */ -#define FCR_MODE 0x04 /* b Mode Register */ -#define FCR_STATUS 0x05 /* b Status Register */ -#define FCR_ISR 0x06 /* b Interrupt Status Register */ -#define FCR_IMR 0x07 /* b Interrupt Mask Register */ - -/* FCR_MODE Register Command List */ -#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ -#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ -#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ - -#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ -#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calc result Read_Mode */ -#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ - -#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ -#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ - -#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ -#define FCR_MODE_LED_ON 0x04 /* LED ON */ - -#define FCR_MODE_EJECT_ON 0x68 /* Ejection events active */ -#define FCR_MODE_EJECT_OFF 0x08 /* Ejection events ignored */ - -#define FCR_MODE_LOCK 0x6C /* Lock_Mode. Eject Switch Invalid */ -#define FCR_MODE_UNLOCK 0x0C /* UnLock_Mode. Eject Switch is valid */ - -#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ -#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ - -#define FCR_MODE_WE 0x80 -#define FCR_MODE_ECC1 0x40 -#define FCR_MODE_ECC0 0x20 -#define FCR_MODE_CE 0x10 -#define FCR_MODE_PCNT1 0x08 -#define FCR_MODE_PCNT0 0x04 -#define FCR_MODE_ALE 0x02 -#define FCR_MODE_CLE 0x01 - -#define FCR_STATUS_BUSY 0x80 - -/*--------------------------------------------------------------------------*/ - -struct tmio_nand { - struct nand_controller controller; - struct nand_chip chip; - struct completion comp; - - struct platform_device *dev; - - void __iomem *ccr; - void __iomem *fcr; - unsigned long fcr_base; - - unsigned int irq; - - /* for tmio_nand_read_byte */ - u8 read; - unsigned read_good:1; -}; - -static inline struct tmio_nand *mtd_to_tmio(struct mtd_info *mtd) -{ - return container_of(mtd_to_nand(mtd), struct tmio_nand, chip); -} - - -/*--------------------------------------------------------------------------*/ - -static void tmio_nand_hwcontrol(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - - if (ctrl & NAND_CTRL_CHANGE) { - u8 mode; - - if (ctrl & NAND_NCE) { - mode = FCR_MODE_DATA; - - if (ctrl & NAND_CLE) - mode |= FCR_MODE_CLE; - else - mode &= ~FCR_MODE_CLE; - - if (ctrl & NAND_ALE) - mode |= FCR_MODE_ALE; - else - mode &= ~FCR_MODE_ALE; - } else { - mode = FCR_MODE_STANDBY; - } - - tmio_iowrite8(mode, tmio->fcr + FCR_MODE); - tmio->read_good = 0; - } - - if (cmd != NAND_CMD_NONE) - tmio_iowrite8(cmd, chip->legacy.IO_ADDR_W); -} - -static int tmio_nand_dev_ready(struct nand_chip *chip) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - - return !(tmio_ioread8(tmio->fcr + FCR_STATUS) & FCR_STATUS_BUSY); -} - -static irqreturn_t tmio_irq(int irq, void *__tmio) -{ - struct tmio_nand *tmio = __tmio; - - /* disable RDYREQ interrupt */ - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - complete(&tmio->comp); - - return IRQ_HANDLED; -} - -/* - *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. - *This interrupt is normally disabled, but for long operations like - *erase and write, we enable it to wake us up. The irq handler - *disables the interrupt. - */ -static int tmio_nand_wait(struct nand_chip *nand_chip) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(nand_chip)); - long timeout; - u8 status; - - /* enable RDYREQ interrupt */ - - tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); - reinit_completion(&tmio->comp); - tmio_iowrite8(0x81, tmio->fcr + FCR_IMR); - - timeout = 400; - timeout = wait_for_completion_timeout(&tmio->comp, - msecs_to_jiffies(timeout)); - - if (unlikely(!tmio_nand_dev_ready(nand_chip))) { - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - dev_warn(&tmio->dev->dev, "still busy after 400 ms\n"); - - } else if (unlikely(!timeout)) { - tmio_iowrite8(0x00, tmio->fcr + FCR_IMR); - dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); - } - - nand_status_op(nand_chip, &status); - return status; -} - -/* - *The TMIO controller combines two 8-bit data bytes into one 16-bit - *word. This function separates them so nand_base.c works as expected, - *especially its NAND_CMD_READID routines. - * - *To prevent stale data from being read, tmio_nand_hwcontrol() clears - *tmio->read_good. - */ -static u_char tmio_nand_read_byte(struct nand_chip *chip) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - unsigned int data; - - if (tmio->read_good--) - return tmio->read; - - data = tmio_ioread16(tmio->fcr + FCR_DATA); - tmio->read = data >> 8; - return data; -} - -/* - *The TMIO controller converts an 8-bit NAND interface to a 16-bit - *bus interface, so all data reads and writes must be 16-bit wide. - *Thus, we implement 16-bit versions of the read, write, and verify - *buffer functions. - */ -static void -tmio_nand_write_buf(struct nand_chip *chip, const u_char *buf, int len) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - - tmio_iowrite16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); -} - -static void tmio_nand_read_buf(struct nand_chip *chip, u_char *buf, int len) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - - tmio_ioread16_rep(tmio->fcr + FCR_DATA, buf, len >> 1); -} - -static void tmio_nand_enable_hwecc(struct nand_chip *chip, int mode) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - - tmio_iowrite8(FCR_MODE_HWECC_RESET, tmio->fcr + FCR_MODE); - tmio_ioread8(tmio->fcr + FCR_DATA); /* dummy read */ - tmio_iowrite8(FCR_MODE_HWECC_CALC, tmio->fcr + FCR_MODE); -} - -static int tmio_nand_calculate_ecc(struct nand_chip *chip, const u_char *dat, - u_char *ecc_code) -{ - struct tmio_nand *tmio = mtd_to_tmio(nand_to_mtd(chip)); - unsigned int ecc; - - tmio_iowrite8(FCR_MODE_HWECC_RESULT, tmio->fcr + FCR_MODE); - - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[1] = ecc; /* 000-255 LP7-0 */ - ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ - ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ - ecc = tmio_ioread16(tmio->fcr + FCR_DATA); - ecc_code[3] = ecc; /* 256-511 LP15-8 */ - ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ - - tmio_iowrite8(FCR_MODE_DATA, tmio->fcr + FCR_MODE); - return 0; -} - -static int tmio_nand_correct_data(struct nand_chip *chip, unsigned char *buf, - unsigned char *read_ecc, - unsigned char *calc_ecc) -{ - int r0, r1; - - /* assume ecc.size = 512 and ecc.bytes = 6 */ - r0 = rawnand_sw_hamming_correct(chip, buf, read_ecc, calc_ecc); - if (r0 < 0) - return r0; - r1 = rawnand_sw_hamming_correct(chip, buf + 256, read_ecc + 3, - calc_ecc + 3); - if (r1 < 0) - return r1; - return r0 + r1; -} - -static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - int ret; - - if (cell->enable) { - ret = cell->enable(dev); - if (ret) - return ret; - } - - /* (4Ch) CLKRUN Enable 1st spcrunc */ - tmio_iowrite8(0x81, tmio->ccr + CCR_ICC); - - /* (10h)BaseAddress 0x1000 spba.spba2 */ - tmio_iowrite16(tmio->fcr_base, tmio->ccr + CCR_BASE); - tmio_iowrite16(tmio->fcr_base >> 16, tmio->ccr + CCR_BASE + 2); - - /* (04h)Command Register I/O spcmd */ - tmio_iowrite8(0x02, tmio->ccr + CCR_COMMAND); - - /* (62h) Power Supply Control ssmpwc */ - /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ - tmio_iowrite8(0x02, tmio->ccr + CCR_NFPSC); - - /* (63h) Detect Control ssmdtc */ - tmio_iowrite8(0x02, tmio->ccr + CCR_NFDC); - - /* Interrupt status register clear sintst */ - tmio_iowrite8(0x0f, tmio->fcr + FCR_ISR); - - /* After power supply, Media are reset smode */ - tmio_iowrite8(FCR_MODE_POWER_ON, tmio->fcr + FCR_MODE); - tmio_iowrite8(FCR_MODE_COMMAND, tmio->fcr + FCR_MODE); - tmio_iowrite8(NAND_CMD_RESET, tmio->fcr + FCR_DATA); - - /* Standby Mode smode */ - tmio_iowrite8(FCR_MODE_STANDBY, tmio->fcr + FCR_MODE); - - mdelay(5); - - return 0; -} - -static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); - if (cell->disable) - cell->disable(dev); -} - -static int tmio_attach_chip(struct nand_chip *chip) -{ - if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) - return 0; - - chip->ecc.size = 512; - chip->ecc.bytes = 6; - chip->ecc.strength = 2; - chip->ecc.hwctl = tmio_nand_enable_hwecc; - chip->ecc.calculate = tmio_nand_calculate_ecc; - chip->ecc.correct = tmio_nand_correct_data; - - return 0; -} - -static const struct nand_controller_ops tmio_ops = { - .attach_chip = tmio_attach_chip, -}; - -static int tmio_probe(struct platform_device *dev) -{ - struct tmio_nand_data *data = dev_get_platdata(&dev->dev); - struct resource *fcr = platform_get_resource(dev, - IORESOURCE_MEM, 0); - struct resource *ccr = platform_get_resource(dev, - IORESOURCE_MEM, 1); - int irq = platform_get_irq(dev, 0); - struct tmio_nand *tmio; - struct mtd_info *mtd; - struct nand_chip *nand_chip; - int retval; - - if (data == NULL) - dev_warn(&dev->dev, "NULL platform data!\n"); - - if (!ccr || !fcr) - return -EINVAL; - - tmio = devm_kzalloc(&dev->dev, sizeof(*tmio), GFP_KERNEL); - if (!tmio) - return -ENOMEM; - - init_completion(&tmio->comp); - - tmio->dev = dev; - - platform_set_drvdata(dev, tmio); - nand_chip = &tmio->chip; - mtd = nand_to_mtd(nand_chip); - mtd->name = "tmio-nand"; - mtd->dev.parent = &dev->dev; - - nand_controller_init(&tmio->controller); - tmio->controller.ops = &tmio_ops; - nand_chip->controller = &tmio->controller; - - tmio->ccr = devm_ioremap(&dev->dev, ccr->start, resource_size(ccr)); - if (!tmio->ccr) - return -EIO; - - tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = devm_ioremap(&dev->dev, fcr->start, resource_size(fcr)); - if (!tmio->fcr) - return -EIO; - - retval = tmio_hw_init(dev, tmio); - if (retval) - return retval; - - /* Set address of NAND IO lines */ - nand_chip->legacy.IO_ADDR_R = tmio->fcr; - nand_chip->legacy.IO_ADDR_W = tmio->fcr; - - /* Set address of hardware control function */ - nand_chip->legacy.cmd_ctrl = tmio_nand_hwcontrol; - nand_chip->legacy.dev_ready = tmio_nand_dev_ready; - nand_chip->legacy.read_byte = tmio_nand_read_byte; - nand_chip->legacy.write_buf = tmio_nand_write_buf; - nand_chip->legacy.read_buf = tmio_nand_read_buf; - - if (data) - nand_chip->badblock_pattern = data->badblock_pattern; - - /* 15 us command delay time */ - nand_chip->legacy.chip_delay = 15; - - retval = devm_request_irq(&dev->dev, irq, &tmio_irq, 0, - dev_name(&dev->dev), tmio); - if (retval) { - dev_err(&dev->dev, "request_irq error %d\n", retval); - goto err_irq; - } - - tmio->irq = irq; - nand_chip->legacy.waitfunc = tmio_nand_wait; - - /* Scan to find existence of the device */ - retval = nand_scan(nand_chip, 1); - if (retval) - goto err_irq; - - /* Register the partitions */ - retval = mtd_device_parse_register(mtd, - data ? data->part_parsers : NULL, - NULL, - data ? data->partition : NULL, - data ? data->num_partitions : 0); - if (!retval) - return retval; - - nand_cleanup(nand_chip); - -err_irq: - tmio_hw_stop(dev, tmio); - return retval; -} - -static int tmio_remove(struct platform_device *dev) -{ - struct tmio_nand *tmio = platform_get_drvdata(dev); - struct nand_chip *chip = &tmio->chip; - int ret; - - ret = mtd_device_unregister(nand_to_mtd(chip)); - WARN_ON(ret); - nand_cleanup(chip); - tmio_hw_stop(dev, tmio); - return 0; -} - -#ifdef CONFIG_PM -static int tmio_suspend(struct platform_device *dev, pm_message_t state) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - if (cell->suspend) - cell->suspend(dev); - - tmio_hw_stop(dev, platform_get_drvdata(dev)); - return 0; -} - -static int tmio_resume(struct platform_device *dev) -{ - const struct mfd_cell *cell = mfd_get_cell(dev); - - /* FIXME - is this required or merely another attack of the broken - * SHARP platform? Looks suspicious. - */ - tmio_hw_init(dev, platform_get_drvdata(dev)); - - if (cell->resume) - cell->resume(dev); - - return 0; -} -#else -#define tmio_suspend NULL -#define tmio_resume NULL -#endif - -static struct platform_driver tmio_driver = { - .driver.name = "tmio-nand", - .driver.owner = THIS_MODULE, - .probe = tmio_probe, - .remove = tmio_remove, - .suspend = tmio_suspend, - .resume = tmio_resume, -}; - -module_platform_driver(tmio_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Ian Molton, Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); -MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); -MODULE_ALIAS("platform:tmio-nand"); From 9f820fc0651c32f8dde26b8e3ad93e1b9fdb62c4 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 12 Jan 2023 10:36:35 +0100 Subject: [PATCH 12/33] mtd: rawnand: Check the data only read pattern only once Instead of checking if a pattern is supported each time we need it, let's create a bitfield that only the core would be allowed to fill at startup time. The core and the individual drivers may then use it in order to check what operation they should use. This bitfield is supposed to grow over time. Signed-off-by: Miquel Raynal Tested-by: Liao Jaime Link: https://lore.kernel.org/linux-mtd/20230112093637.987838-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/nand_base.c | 20 ++++++++++++++++++++ drivers/mtd/nand/raw/nand_jedec.c | 3 +-- drivers/mtd/nand/raw/nand_onfi.c | 3 +-- include/linux/mtd/rawnand.h | 8 ++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c3cc66039925..7b87e2e70484 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4991,6 +4991,24 @@ nand_manufacturer_name(const struct nand_manufacturer_desc *manufacturer_desc) return manufacturer_desc ? manufacturer_desc->name : "Unknown"; } +static void rawnand_check_data_only_read_support(struct nand_chip *chip) +{ + /* Use an arbitrary size for the check */ + if (!nand_read_data_op(chip, NULL, SZ_512, true, true)) + chip->controller->supported_op.data_only_read = 1; +} + +static void rawnand_early_check_supported_ops(struct nand_chip *chip) +{ + /* The supported_op fields should not be set by individual drivers */ + WARN_ON_ONCE(chip->controller->supported_op.data_only_read); + + if (!nand_has_exec_op(chip)) + return; + + rawnand_check_data_only_read_support(chip); +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -5023,6 +5041,8 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) /* Select the device */ nand_select_target(chip, 0); + rawnand_early_check_supported_ops(chip); + /* Send the command for reading device ID */ ret = nand_readid_op(chip, 0, id_data, 2); if (ret) diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c index 85b6d9372d80..836757717660 100644 --- a/drivers/mtd/nand/raw/nand_jedec.c +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -46,8 +46,7 @@ int nand_jedec_detect(struct nand_chip *chip) if (!p) return -ENOMEM; - if (!nand_has_exec_op(chip) || - !nand_read_data_op(chip, p, sizeof(*p), true, true)) + if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) use_datain = true; for (i = 0; i < JEDEC_PARAM_PAGES; i++) { diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index 7586befce7f9..f15ef90aec8c 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -166,8 +166,7 @@ int nand_onfi_detect(struct nand_chip *chip) if (!pbuf) return -ENOMEM; - if (!nand_has_exec_op(chip) || - !nand_read_data_op(chip, &pbuf[0], sizeof(*pbuf), true, true)) + if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read) use_datain = true; for (i = 0; i < ONFI_PARAM_PAGES; i++) { diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index dcf90144d70b..28c5dce782dd 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1094,10 +1094,18 @@ struct nand_controller_ops { * * @lock: lock used to serialize accesses to the NAND controller * @ops: NAND controller operations. + * @supported_op: NAND controller known-to-be-supported operations, + * only writable by the core after initial checking. + * @supported_op.data_only_read: The controller supports reading more data from + * the bus without restarting an entire read operation nor + * changing the column. */ struct nand_controller { struct mutex lock; const struct nand_controller_ops *ops; + struct { + unsigned int data_only_read: 1; + } supported_op; }; static inline void nand_controller_init(struct nand_controller *nfc) From b1f9ffbfda07acee9bdbc77416facf606647b523 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 12 Jan 2023 10:36:36 +0100 Subject: [PATCH 13/33] mtd: rawnand: Prepare the late addition of supported operation checks Add an empty envelope just to show how to add additional checks for new operations. This is going to be used for sequential cached reads, which require the page size to be known (and the discovery to be over), hence the "late" designation. Signed-off-by: Miquel Raynal Tested-by: Liao Jaime Link: https://lore.kernel.org/linux-mtd/20230112093637.987838-3-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/nand_base.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 7b87e2e70484..e1f74e2fa415 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -5009,6 +5009,14 @@ static void rawnand_early_check_supported_ops(struct nand_chip *chip) rawnand_check_data_only_read_support(chip); } +static void rawnand_late_check_supported_ops(struct nand_chip *chip) +{ + /* The supported_op fields should not be set by individual drivers */ + + if (!nand_has_exec_op(chip)) + return; +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -6345,6 +6353,8 @@ static int nand_scan_tail(struct nand_chip *chip) goto err_free_interface_config; } + rawnand_late_check_supported_ops(chip); + /* * Look for secure regions in the NAND chip. These regions are supposed * to be protected by a secure element like Trustzone. So the read/write From 003fe4b9545b83cca4f7a7633695d1f69a0b0011 Mon Sep 17 00:00:00 2001 From: JaimeLiao Date: Thu, 12 Jan 2023 10:36:37 +0100 Subject: [PATCH 14/33] mtd: rawnand: Support for sequential cache reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for sequential cache reads for controllers using the generic core helpers for their fast read/write helpers. Sequential reads may reduce the overhead when accessing physically continuous data by loading in cache the next page while the previous page gets sent out on the NAND bus. The ONFI specification provides the following additional commands to handle sequential cached reads: * 0x31 - READ CACHE SEQUENTIAL: Requires the NAND chip to load the next page into cache while keeping the current cache available for host reads. * 0x3F - READ CACHE END: Tells the NAND chip this is the end of the sequential cache read, the current cache shall remain accessible for the host but no more internal cache loading operation is required. On the bus, a multi page read operation is currently handled like this: 00 -- ADDR1 -- 30 -- WAIT_RDY (tR+tRR) -- DATA1_IN 00 -- ADDR2 -- 30 -- WAIT_RDY (tR+tRR) -- DATA2_IN 00 -- ADDR3 -- 30 -- WAIT_RDY (tR+tRR) -- DATA3_IN Sequential cached reads may instead be achieved with: 00 -- ADDR1 -- 30 -- WAIT_RDY (tR) -- \ 31 -- WAIT_RDY (tRCBSY+tRR) -- DATA1_IN \ 31 -- WAIT_RDY (tRCBSY+tRR) -- DATA2_IN \ 3F -- WAIT_RDY (tRCBSY+tRR) -- DATA3_IN Below are the read speed test results with regular reads and sequential cached reads, on NXP i.MX6 VAR-SOM-SOLO in mapping mode with a NAND chip characterized with the following timings: * tR: 20 µs * tRCBSY: 5 µs * tRR: 20 ns and the following geometry: * device size: 2 MiB * eraseblock size: 128 kiB * page size: 2 kiB ============= Normal read @ 33MHz ================= mtd_speedtest: eraseblock read speed is 15633 KiB/s mtd_speedtest: page read speed is 15515 KiB/s mtd_speedtest: 2 page read speed is 15398 KiB/s =================================================== ========= Sequential cache read @ 33MHz =========== mtd_speedtest: eraseblock read speed is 18285 KiB/s mtd_speedtest: page read speed is 15875 KiB/s mtd_speedtest: 2 page read speed is 16253 KiB/s =================================================== We observe an overall speed improvement of about 5% when reading 2 pages, up to 15% when reading an entire block. This is due to the ~14us gain on each additional page read (tR - (tRCBSY + tRR)). Co-developed-by: Miquel Raynal Signed-off-by: Miquel Raynal Signed-off-by: JaimeLiao Tested-by: Liao Jaime Link: https://lore.kernel.org/linux-mtd/20230112093637.987838-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/nand_base.c | 119 +++++++++++++++++++++++++++++-- include/linux/mtd/rawnand.h | 9 +++ 2 files changed, 124 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index e1f74e2fa415..a6af521832aa 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1208,6 +1208,73 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, return nand_exec_op(chip, &op); } +static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page, + unsigned int offset_in_page, void *buf, + unsigned int len, bool check_only) +{ + const struct nand_interface_config *conf = + nand_get_interface_config(chip); + u8 addrs[5]; + struct nand_op_instr start_instrs[] = { + NAND_OP_CMD(NAND_CMD_READ0, 0), + NAND_OP_ADDR(4, addrs, 0), + NAND_OP_CMD(NAND_CMD_READSTART, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), 0), + NAND_OP_CMD(NAND_CMD_READCACHESEQ, NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_op_instr cont_instrs[] = { + NAND_OP_CMD(page == chip->cont_read.last_page ? + NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ, + NAND_COMMON_TIMING_NS(conf, tWB_max)), + NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), + NAND_COMMON_TIMING_NS(conf, tRR_min)), + NAND_OP_DATA_IN(len, buf, 0), + }; + struct nand_operation start_op = NAND_OPERATION(chip->cur_cs, start_instrs); + struct nand_operation cont_op = NAND_OPERATION(chip->cur_cs, cont_instrs); + int ret; + + if (!len) { + start_op.ninstrs--; + cont_op.ninstrs--; + } + + ret = nand_fill_column_cycles(chip, addrs, offset_in_page); + if (ret < 0) + return ret; + + addrs[2] = page; + addrs[3] = page >> 8; + + if (chip->options & NAND_ROW_ADDR_3) { + addrs[4] = page >> 16; + start_instrs[1].ctx.addr.naddrs++; + } + + /* Check if cache reads are supported */ + if (check_only) { + if (nand_check_op(chip, &start_op) || nand_check_op(chip, &cont_op)) + return -EOPNOTSUPP; + + return 0; + } + + if (page == chip->cont_read.first_page) + return nand_exec_op(chip, &start_op); + else + return nand_exec_op(chip, &cont_op); +} + +static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page) +{ + return chip->cont_read.ongoing && + page >= chip->cont_read.first_page && + page <= chip->cont_read.last_page; +} + /** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip @@ -1233,10 +1300,16 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page, return -EINVAL; if (nand_has_exec_op(chip)) { - if (mtd->writesize > 512) - return nand_lp_exec_read_page_op(chip, page, - offset_in_page, buf, - len); + if (mtd->writesize > 512) { + if (rawnand_cont_read_ongoing(chip, page)) + return nand_lp_exec_cont_read_page_op(chip, page, + offset_in_page, + buf, len, false); + else + return nand_lp_exec_read_page_op(chip, page, + offset_in_page, buf, + len); + } return nand_sp_exec_read_page_op(chip, page, offset_in_page, buf, len); @@ -3353,6 +3426,27 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, return NULL; } +static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page, + u32 readlen, int col) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (!chip->controller->supported_op.cont_read) + return; + + if ((col && col + readlen < (3 * mtd->writesize)) || + (!col && readlen < (2 * mtd->writesize))) { + chip->cont_read.ongoing = false; + return; + } + + chip->cont_read.ongoing = true; + chip->cont_read.first_page = page; + if (col) + chip->cont_read.first_page++; + chip->cont_read.last_page = page + ((readlen >> chip->page_shift) & chip->pagemask); +} + /** * nand_setup_read_retry - [INTERN] Set the READ RETRY mode * @chip: NAND chip object @@ -3426,6 +3520,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from, oob = ops->oobbuf; oob_required = oob ? 1 : 0; + rawnand_enable_cont_reads(chip, page, readlen, col); + while (1) { struct mtd_ecc_stats ecc_stats = mtd->ecc_stats; @@ -5009,12 +5105,27 @@ static void rawnand_early_check_supported_ops(struct nand_chip *chip) rawnand_check_data_only_read_support(chip); } +static void rawnand_check_cont_read_support(struct nand_chip *chip) +{ + struct mtd_info *mtd = nand_to_mtd(chip); + + if (chip->read_retries) + return; + + if (!nand_lp_exec_cont_read_page_op(chip, 0, 0, NULL, + mtd->writesize, true)) + chip->controller->supported_op.cont_read = 1; +} + static void rawnand_late_check_supported_ops(struct nand_chip *chip) { /* The supported_op fields should not be set by individual drivers */ + WARN_ON_ONCE(chip->controller->supported_op.cont_read); if (!nand_has_exec_op(chip)) return; + + rawnand_check_cont_read_support(chip); } /* diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 28c5dce782dd..1b0936fe3c6e 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -67,6 +67,8 @@ struct gpio_desc; /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 +#define NAND_CMD_READCACHESEQ 0x31 +#define NAND_CMD_READCACHEEND 0x3f #define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 @@ -1099,12 +1101,14 @@ struct nand_controller_ops { * @supported_op.data_only_read: The controller supports reading more data from * the bus without restarting an entire read operation nor * changing the column. + * @supported_op.cont_read: The controller supports sequential cache reads. */ struct nand_controller { struct mutex lock; const struct nand_controller_ops *ops; struct { unsigned int data_only_read: 1; + unsigned int cont_read: 1; } supported_op; }; @@ -1308,6 +1312,11 @@ struct nand_chip { int read_retries; struct nand_secure_region *secure_regions; u8 nr_secure_regions; + struct { + bool ongoing; + unsigned int first_page; + unsigned int last_page; + } cont_read; /* Externals */ struct nand_controller *controller; From a2cfa6a24c61bf2178048397f6512ebb15e1ebfe Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 12 Jan 2023 22:40:04 -0800 Subject: [PATCH 15/33] mtd: rawnand: vf610_nfc: use regular comments for functions These comments are not quite in kernel-doc format and they don't need to be, so just use "/*" comment markers for them. This prevents these kernel-doc warnings: drivers/mtd/nand/raw/vf610_nfc.c:210: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * Read accessor for internal SRAM buffer drivers/mtd/nand/raw/vf610_nfc.c:245: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst * Write accessor for internal SRAM buffer Signed-off-by: Randy Dunlap Cc: Stefan Agner Cc: Miquel Raynal Cc: Richard Weinberger Cc: Vignesh Raghavendra Cc: linux-mtd@lists.infradead.org Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230113064004.24391-1-rdunlap@infradead.org --- drivers/mtd/nand/raw/vf610_nfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index a2b89b75073f..b643332ea1ff 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -206,7 +206,7 @@ static inline bool vf610_nfc_kernel_is_little_endian(void) #endif } -/** +/* * Read accessor for internal SRAM buffer * @dst: destination address in regular memory * @src: source address in SRAM buffer @@ -241,7 +241,7 @@ static inline void vf610_nfc_rd_from_sram(void *dst, const void __iomem *src, } } -/** +/* * Write accessor for internal SRAM buffer * @dst: destination address in SRAM buffer * @src: source address in regular memory From 43651e60aa167974955b23f73a25eb79886ab6d0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 16 Jan 2023 10:47:35 +0100 Subject: [PATCH 16/33] mtd: rawnand: Fix nand_chip kdoc Describe the continuous read nand_chip fields to avoid the following htmldocs warning: include/linux/mtd/rawnand.h:1325: warning: Function parameter or member 'cont_read' not described in 'nand_chip' Reported-by: Stephen Rothwell Fixes: 003fe4b9545b ("mtd: rawnand: Support for sequential cache reads") Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230116094735.11483-1-miquel.raynal@bootlin.com --- include/linux/mtd/rawnand.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 1b0936fe3c6e..f8d4be9c587a 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1260,6 +1260,10 @@ struct nand_secure_region { * @read_retries: The number of read retry modes supported * @secure_regions: Structure containing the secure regions info * @nr_secure_regions: Number of secure regions + * @cont_read: Sequential page read internals + * @cont_read.ongoing: Whether a continuous read is ongoing or not + * @cont_read.first_page: Start of the continuous read operation + * @cont_read.last_page: End of the continuous read operation * @controller: The hardware controller structure which is shared among multiple * independent devices * @ecc: The ECC controller structure From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 19 Jan 2023 03:45:43 +0000 Subject: [PATCH 17/33] mtd: spinand: macronix: use scratch buffer for DMA operation The mx35lf1ge4ab_get_eccsr() function uses an SPI DMA operation to read the eccsr, hence the buffer should not be on stack. Since commit 380583227c0c7f ("spi: spi-mem: Add extra sanity checks on the op param") the kernel emmits a warning and blocks such operations. Use the scratch buffer to get eccsr instead of trying to directly read into a stack-allocated variable. Signed-off-by: Daniel Golle Reviewed-by: Dhruva Gole Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/Y8i85zM0u4XdM46z@makrotopia.org --- drivers/mtd/nand/spi/macronix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index dce835132a1e..722a9738ba37 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, * in order to avoid forcing the wear-leveling layer to move * data around if it's not necessary. */ - if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr)) + if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf)) return nanddev_get_ecc_conf(nand)->strength; + eccsr = *spinand->scratchbuf; if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength || !eccsr)) return nanddev_get_ecc_conf(nand)->strength; From 25e3f30601a368642678744fc8a9b1dce183c7bc Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Fri, 23 Sep 2022 11:14:57 +0800 Subject: [PATCH 18/33] mtd: spi-nor: core: fix implicit declaration warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spi-nor/core.c needs to include linux/delay.h, or it would raise below compile warning: drivers/mtd/spi-nor/core.c: In function ‘spi_nor_soft_reset’: drivers/mtd/spi-nor/core.c:2779:2: error: implicit declaration of function ‘usleep_range’ [-Werror=implicit-function-declaration] 2779 | usleep_range(SPI_NOR_SRST_SLEEP_MIN, SPI_NOR_SRST_SLEEP_MAX); | ^~~~~~~~~~~~ Fixes: d73ee7534cc5 ("mtd: spi-nor: core: perform a Soft Reset on shutdown") Signed-off-by: Zeng Heng Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20220923031457.56103-1-zengheng4@huawei.com --- drivers/mtd/spi-nor/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index d8703d7dfd0a..b500655f7937 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include From 3f592a869f87723314f0cb1ac232bd3bf8245be8 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 10 Jan 2023 18:47:02 +0200 Subject: [PATCH 19/33] mtd: spi-nor: spansion: Consider reserved bits in CFR5 register CFR5[6] is reserved bit and must be always 1. Set it to comply with flash requirements. While fixing SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_{EN, DS} definition, stop using magic numbers and describe the missing bit fields in CFR5 register. This is useful for both readability and future possible addition of Octal STR mode support. Fixes: c3266af101f2 ("mtd: spi-nor: spansion: add support for Cypress Semper flash") Cc: stable@vger.kernel.org Reported-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Dhruva Gole Reviewed-by: Pratyush Yadav Tested-by: Dhruva Gole Link: https://lore.kernel.org/linux-mtd/20230110164703.83413-1-tudor.ambarus@linaro.org --- drivers/mtd/spi-nor/spansion.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index b621cdfd506f..07fe0f6fdfe3 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -21,8 +21,13 @@ #define SPINOR_REG_CYPRESS_CFR3V 0x00800004 #define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */ #define SPINOR_REG_CYPRESS_CFR5V 0x00800006 -#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3 -#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS 0 +#define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6) +#define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1) +#define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0) +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN \ + (SPINOR_REG_CYPRESS_CFR5_BIT6 | SPINOR_REG_CYPRESS_CFR5_DDR | \ + SPINOR_REG_CYPRESS_CFR5_OPI) +#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS SPINOR_REG_CYPRESS_CFR5_BIT6 #define SPINOR_OP_CYPRESS_RD_FAST 0xee /* Cypress SPI NOR flash operations. */ From ca5a16db010018095da35c074397289a5bbcb543 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 10 Jan 2023 18:47:03 +0200 Subject: [PATCH 20/33] mtd: spi-nor: spansion: Make CFRx reg fields generic Cypress defines two flavors of configuration registers, volatile and non volatile, and both use the same bit fields. Rename the bitfields in the configuration registers so that they can be used for both flavors. Signed-off-by: Tudor Ambarus Reviewed-by: Dhruva Gole Reviewed-by: Takahiro Kuwano Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/linux-mtd/20230110164703.83413-2-tudor.ambarus@linaro.org --- drivers/mtd/spi-nor/spansion.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 07fe0f6fdfe3..12a256c0ef4c 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -15,19 +15,19 @@ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_REG_CYPRESS_CFR1V 0x00800002 -#define SPINOR_REG_CYPRESS_CFR1V_QUAD_EN BIT(1) /* Quad Enable */ +#define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */ #define SPINOR_REG_CYPRESS_CFR2V 0x00800003 -#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb +#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR3V 0x00800004 -#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */ +#define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */ #define SPINOR_REG_CYPRESS_CFR5V 0x00800006 #define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6) #define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1) #define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0) -#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN \ +#define SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN \ (SPINOR_REG_CYPRESS_CFR5_BIT6 | SPINOR_REG_CYPRESS_CFR5_DDR | \ SPINOR_REG_CYPRESS_CFR5_OPI) -#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS SPINOR_REG_CYPRESS_CFR5_BIT6 +#define SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS SPINOR_REG_CYPRESS_CFR5_BIT6 #define SPINOR_OP_CYPRESS_RD_FAST 0xee /* Cypress SPI NOR flash operations. */ @@ -57,7 +57,7 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; /* Use 24 dummy cycles for memory array reads. */ - *buf = SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24; + *buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24; op = (struct spi_mem_op) CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, SPINOR_REG_CYPRESS_CFR2V, 1, buf); @@ -69,7 +69,7 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) nor->read_dummy = 24; /* Set the octal and DTR enable bits. */ - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN; + buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; op = (struct spi_mem_op) CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, SPINOR_REG_CYPRESS_CFR5V, 1, buf); @@ -103,7 +103,7 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) * in 8D-8D-8D mode. Since there is no register at the next location, * just initialize the value to 0 and let the transaction go on. */ - buf[0] = SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_DS; + buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS; buf[1] = 0; op = (struct spi_mem_op) CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, @@ -155,11 +155,11 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) if (ret) return ret; - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1V_QUAD_EN) + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1_QUAD_EN) return 0; /* Update the Quad Enable bit. */ - nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1V_QUAD_EN; + nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1_QUAD_EN; op = (struct spi_mem_op) CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, SPINOR_REG_CYPRESS_CFR1V, 1, @@ -210,7 +210,7 @@ static int cypress_nor_set_page_size(struct spi_nor *nor) if (ret) return ret; - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ) nor->params->page_size = 512; else nor->params->page_size = 256; From 59273180299ad264d086135e8199c1b05ea51b69 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 15 Dec 2022 09:12:33 +0100 Subject: [PATCH 21/33] mtd: spi-nor: Create macros to define chip IDs and geometries The INFO() macro defines an ID array and a couple of geometry properties. Right now all its lines are duplicated twice because of the INFO6() macro (for extended IDs) and soon as well we will need to add a geometry parameter to include the number of banks. In order to limit the code duplication, let's create a number of intermediate macros which will facilitate defining high-level INFOX() macros. There is no functional change. Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20221215081241.407098-2-miquel.raynal@bootlin.com Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.h | 43 ++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index f03b55cf7e6f..f6d012e1f681 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -529,33 +529,30 @@ struct flash_info { const struct spi_nor_fixups *fixups; }; +#define SPI_NOR_ID_2ITEMS(_id) ((_id) >> 8) & 0xff, (_id) & 0xff +#define SPI_NOR_ID_3ITEMS(_id) ((_id) >> 16) & 0xff, SPI_NOR_ID_2ITEMS(_id) + +#define SPI_NOR_ID(_jedec_id, _ext_id) \ + .id = { SPI_NOR_ID_3ITEMS(_jedec_id), SPI_NOR_ID_2ITEMS(_ext_id) }, \ + .id_len = !(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0)) + +#define SPI_NOR_ID6(_jedec_id, _ext_id) \ + .id = { SPI_NOR_ID_3ITEMS(_jedec_id), SPI_NOR_ID_3ITEMS(_ext_id) }, \ + .id_len = 6 + +#define SPI_NOR_GEOMETRY(_sector_size, _n_sectors) \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = 256 + /* Used when the "_ext_id" is two bytes at most */ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ + SPI_NOR_ID((_jedec_id), (_ext_id)), \ + SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)), #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors) \ - .id = { \ - ((_jedec_id) >> 16) & 0xff, \ - ((_jedec_id) >> 8) & 0xff, \ - (_jedec_id) & 0xff, \ - ((_ext_id) >> 16) & 0xff, \ - ((_ext_id) >> 8) & 0xff, \ - (_ext_id) & 0xff, \ - }, \ - .id_len = 6, \ - .sector_size = (_sector_size), \ - .n_sectors = (_n_sectors), \ - .page_size = 256, \ + SPI_NOR_ID6((_jedec_id), (_ext_id)), \ + SPI_NOR_GEOMETRY((_sector_size), (_n_sectors)), #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes) \ .sector_size = (_sector_size), \ From c705e63a323a1a6c8d5830c2c3992586ef5fba26 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 19 Jan 2023 20:04:53 -0600 Subject: [PATCH 22/33] dt-bindings: mtd: partitions: Fix partition node name pattern The 'partition' node name pattern is missing start and end anchors, so anything is allowed before or after the regex pattern. There's no in tree users needing that, so add anchors to the pattern. Signed-off-by: Rob Herring Reviewed-by: Pratyush Yadav Reviewed-by: Dhruva Gole Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230120020454.3225796-1-robh@kernel.org --- .../devicetree/bindings/mtd/partitions/partitions.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mtd/partitions/partitions.yaml b/Documentation/devicetree/bindings/mtd/partitions/partitions.yaml index 9aca4e6c6047..2edc65e0e361 100644 --- a/Documentation/devicetree/bindings/mtd/partitions/partitions.yaml +++ b/Documentation/devicetree/bindings/mtd/partitions/partitions.yaml @@ -32,7 +32,7 @@ properties: enum: [1, 2] patternProperties: - "partition(-.+|@[0-9a-f]+)": + "^partition(-.+|@[0-9a-f]+)$": $ref: partition.yaml required: From 724ef01569519d1e7a95231688b6a5f8eaba29f2 Mon Sep 17 00:00:00 2001 From: Mario Kicherer Date: Thu, 26 Jan 2023 15:40:50 +0100 Subject: [PATCH 23/33] mtd: spinand: Add support for AllianceMemory AS5F34G04SND Add support for AllianceMemory AS5F34G04SND SPI NAND flash Datasheet: - https://www.alliancememory.com/wp-content/uploads/pdf/flash/AllianceMemory_SPI_NAND_Flash_July2020_Rev1.0.pdf Signed-off-by: Mario Kicherer Reviewed-by: Dhruva Gole Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230126144050.2656358-1-dev@kicherer.org --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/alliancememory.c | 153 ++++++++++++++++++++++++++ drivers/mtd/nand/spi/core.c | 1 + include/linux/mtd/spinand.h | 1 + 4 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/alliancememory.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index b520fe634041..4ec973b8b6bf 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o +spinand-objs := core.o alliancememory.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/alliancememory.c b/drivers/mtd/nand/spi/alliancememory.c new file mode 100644 index 000000000000..7936ea546b03 --- /dev/null +++ b/drivers/mtd/nand/spi/alliancememory.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Author: Mario Kicherer + */ + +#include +#include +#include + +#define SPINAND_MFR_ALLIANCEMEMORY 0x52 + +#define AM_STATUS_ECC_BITMASK (3 << 4) + +#define AM_STATUS_ECC_NONE_DETECTED (0 << 4) +#define AM_STATUS_ECC_CORRECTED (1 << 4) +#define AM_STATUS_ECC_ERRORED (2 << 4) +#define AM_STATUS_ECC_MAX_CORRECTED (3 << 4) + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +static int am_get_eccsize(struct mtd_info *mtd) +{ + if (mtd->oobsize == 64) + return 0x20; + else if (mtd->oobsize == 128) + return 0x38; + else if (mtd->oobsize == 256) + return 0x70; + else + return -EINVAL; +} + +static int am_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + int ecc_bytes; + + ecc_bytes = am_get_eccsize(mtd); + if (ecc_bytes < 0) + return ecc_bytes; + + region->offset = mtd->oobsize - ecc_bytes; + region->length = ecc_bytes; + + return 0; +} + +static int am_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + int ecc_bytes; + + if (section) + return -ERANGE; + + ecc_bytes = am_get_eccsize(mtd); + if (ecc_bytes < 0) + return ecc_bytes; + + /* + * It is unclear how many bytes are used for the bad block marker. We + * reserve the common two bytes here. + * + * The free area in this kind of flash is divided into chunks where the + * first 4 bytes of each chunk are unprotected. The number of chunks + * depends on the specific model. The models with 4096+256 bytes pages + * have 8 chunks, the others 4 chunks. + */ + + region->offset = 2; + region->length = mtd->oobsize - 2 - ecc_bytes; + + return 0; +} + +static const struct mtd_ooblayout_ops am_ooblayout = { + .ecc = am_ooblayout_ecc, + .free = am_ooblayout_free, +}; + +static int am_ecc_get_status(struct spinand_device *spinand, u8 status) +{ + switch (status & AM_STATUS_ECC_BITMASK) { + case AM_STATUS_ECC_NONE_DETECTED: + return 0; + + case AM_STATUS_ECC_CORRECTED: + /* + * use oobsize to determine the flash model and the maximum of + * correctable errors and return maximum - 1 by convention + */ + if (spinand->base.mtd.oobsize == 64) + return 3; + else + return 7; + + case AM_STATUS_ECC_ERRORED: + return -EBADMSG; + + case AM_STATUS_ECC_MAX_CORRECTED: + /* + * use oobsize to determine the flash model and the maximum of + * correctable errors + */ + if (spinand->base.mtd.oobsize == 64) + return 4; + else + return 8; + + default: + break; + } + + return -EINVAL; +} + +static const struct spinand_info alliancememory_spinand_table[] = { + SPINAND_INFO("AS5F34G04SND", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2f), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&am_ooblayout, + am_ecc_get_status)), +}; + +static const struct spinand_manufacturer_ops alliancememory_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer alliancememory_spinand_manufacturer = { + .id = SPINAND_MFR_ALLIANCEMEMORY, + .name = "AllianceMemory", + .chips = alliancememory_spinand_table, + .nchips = ARRAY_SIZE(alliancememory_spinand_table), + .ops = &alliancememory_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index dacd9c0e8b20..638391f77d8c 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -937,6 +937,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + &alliancememory_spinand_manufacturer, &ato_spinand_manufacturer, &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 6d3392a7edc6..01be9f0f008a 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -260,6 +260,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; extern const struct spinand_manufacturer ato_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; From b56265257d38af5abf43bd5461ca166b401c35a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sat, 28 Jan 2023 14:41:11 +0100 Subject: [PATCH 24/33] mtd: rawnand: fsl_elbc: Propagate HW ECC settings to HW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible that current chip->ecc.engine_type value does not match to configured HW value (if HW ECC checking and generating is enabled or not). This can happen with old U-Boot bootloader version which either does not initialize NAND (and let it in some default unusable state) or initialize NAND with different parameters than what is specified in kernel DTS file. So if kernel chose to use some chip->ecc.engine_type settings (e.g. from DTS file) then do not depend on bootloader HW configuration and configures HW ECC settings according to chip->ecc.engine_type value. BR_DECC must be set to BR_DECC_CHK_GEN when HW is doing ECC (both generating and checking), or to BR_DECC_OFF when HW is not doing ECC. This change fixes usage of SW ECC support in case bootloader explicitly enabled HW ECC support and kernel DTS file has specified to use SW ECC. (Of course this works only in case when NAND is not a boot device and both bootloader and kernel are loaded from different location, e.g. FLASH NOR.) Fixes: f6424c22aa36 ("mtd: rawnand: fsl_elbc: Make SW ECC work") Signed-off-by: Pali Rohár Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230128134111.32559-1-pali@kernel.org --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index a18d121396aa..e25119e58b69 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -725,6 +725,7 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) struct fsl_lbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; unsigned int al; + u32 br; /* * if ECC was not chosen in DT, decide whether to use HW or SW ECC from @@ -764,6 +765,13 @@ static int fsl_elbc_attach_chip(struct nand_chip *chip) return -EINVAL; } + /* enable/disable HW ECC checking and generating based on if HW ECC was chosen */ + br = in_be32(&lbc->bank[priv->bank].br) & ~BR_DECC; + if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_CHK_GEN); + else + out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_OFF); + /* calculate FMR Address Length field */ al = 0; if (chip->pagemask & 0xffff0000) From 3af7ade257649d8e2e17eae96422b7868ed215a8 Mon Sep 17 00:00:00 2001 From: Xiangsheng Hou Date: Wed, 1 Feb 2023 10:14:56 +0800 Subject: [PATCH 25/33] dt-bindings: mtd: Split ECC engine with rawnand controller Split MediaTek ECC engine with rawnand controller and convert to YAML schema. Signed-off-by: Xiangsheng Hou Reviewed-by: Krzysztof Kozlowski Reported-by: kernel test robot Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230201021500.26769-2-xiangsheng.hou@mediatek.com --- .../bindings/mtd/mediatek,mtk-nfc.yaml | 155 +++++++++++++++ .../mtd/mediatek,nand-ecc-engine.yaml | 62 ++++++ .../devicetree/bindings/mtd/mtk-nand.txt | 176 ------------------ MAINTAINERS | 2 +- 4 files changed, 218 insertions(+), 177 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml create mode 100644 Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml delete mode 100644 Documentation/devicetree/bindings/mtd/mtk-nand.txt diff --git a/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml b/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml new file mode 100644 index 000000000000..a6e7f123eda7 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml @@ -0,0 +1,155 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/mediatek,mtk-nfc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek(MTK) SoCs raw NAND FLASH controller (NFC) + +maintainers: + - Xiangsheng Hou + +properties: + compatible: + enum: + - mediatek,mt2701-nfc + - mediatek,mt2712-nfc + - mediatek,mt7622-nfc + + reg: + items: + - description: Base physical address and size of NFI. + + interrupts: + items: + - description: NFI interrupt + + clocks: + items: + - description: clock used for the controller + - description: clock used for the pad + + clock-names: + items: + - const: nfi_clk + - const: pad_clk + + ecc-engine: + description: device-tree node of the required ECC engine. + $ref: /schemas/types.yaml#/definitions/phandle + +patternProperties: + "^nand@[a-f0-9]$": + $ref: nand-chip.yaml# + unevaluatedProperties: false + properties: + reg: + maximum: 1 + nand-on-flash-bbt: true + nand-ecc-mode: + const: hw + +allOf: + - $ref: nand-controller.yaml# + + - if: + properties: + compatible: + contains: + const: mediatek,mt2701-nfc + then: + patternProperties: + "^nand@[a-f0-9]$": + properties: + nand-ecc-step-size: + enum: [ 512, 1024 ] + nand-ecc-strength: + enum: [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60] + + - if: + properties: + compatible: + contains: + const: mediatek,mt2712-nfc + then: + patternProperties: + "^nand@[a-f0-9]$": + properties: + nand-ecc-step-size: + enum: [ 512, 1024 ] + nand-ecc-strength: + enum: [4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, + 40, 44, 48, 52, 56, 60, 68, 72, 80] + + - if: + properties: + compatible: + contains: + const: mediatek,mt7622-nfc + then: + patternProperties: + "^nand@[a-f0-9]$": + properties: + nand-ecc-step-size: + const: 512 + nand-ecc-strength: + enum: [4, 6, 8, 10, 12] + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - ecc-engine + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + nand-controller@1100d000 { + compatible = "mediatek,mt2701-nfc"; + reg = <0 0x1100d000 0 0x1000>; + interrupts = ; + clocks = <&pericfg CLK_PERI_NFI>, + <&pericfg CLK_PERI_NFI_PAD>; + clock-names = "nfi_clk", "pad_clk"; + ecc-engine = <&bch>; + #address-cells = <1>; + #size-cells = <0>; + + nand@0 { + reg = <0>; + + nand-on-flash-bbt; + nand-ecc-mode = "hw"; + nand-ecc-step-size = <1024>; + nand-ecc-strength = <24>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + preloader@0 { + label = "pl"; + read-only; + reg = <0x0 0x400000>; + }; + android@400000 { + label = "android"; + reg = <0x400000 0x12c00000>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml new file mode 100644 index 000000000000..b13d801eda76 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/mediatek,nand-ecc-engine.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek(MTK) SoCs NAND ECC engine + +maintainers: + - Xiangsheng Hou + +description: | + MTK NAND ECC engine can cowork with MTK raw NAND and SPI NAND controller. + +properties: + compatible: + enum: + - mediatek,mt2701-ecc + - mediatek,mt2712-ecc + - mediatek,mt7622-ecc + + reg: + items: + - description: Base physical address and size of ECC. + + interrupts: + items: + - description: ECC interrupt + + clocks: + maxItems: 1 + + clock-names: + const: nfiecc_clk + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + bch: ecc@1100e000 { + compatible = "mediatek,mt2701-ecc"; + reg = <0 0x1100e000 0 0x1000>; + interrupts = ; + clocks = <&pericfg CLK_PERI_NFI_ECC>; + clock-names = "nfiecc_clk"; + }; + }; diff --git a/Documentation/devicetree/bindings/mtd/mtk-nand.txt b/Documentation/devicetree/bindings/mtd/mtk-nand.txt deleted file mode 100644 index 839ea2f93d04..000000000000 --- a/Documentation/devicetree/bindings/mtd/mtk-nand.txt +++ /dev/null @@ -1,176 +0,0 @@ -MTK SoCs NAND FLASH controller (NFC) DT binding - -This file documents the device tree bindings for MTK SoCs NAND controllers. -The functional split of the controller requires two drivers to operate: -the nand controller interface driver and the ECC engine driver. - -The hardware description for both devices must be captured as device -tree nodes. - -1) NFC NAND Controller Interface (NFI): -======================================= - -The first part of NFC is NAND Controller Interface (NFI) HW. -Required NFI properties: -- compatible: Should be one of - "mediatek,mt2701-nfc", - "mediatek,mt2712-nfc", - "mediatek,mt7622-nfc". -- reg: Base physical address and size of NFI. -- interrupts: Interrupts of NFI. -- clocks: NFI required clocks. -- clock-names: NFI clocks internal name. -- ecc-engine: Required ECC Engine node. -- #address-cells: NAND chip index, should be 1. -- #size-cells: Should be 0. - -Example: - - nandc: nfi@1100d000 { - compatible = "mediatek,mt2701-nfc"; - reg = <0 0x1100d000 0 0x1000>; - interrupts = ; - clocks = <&pericfg CLK_PERI_NFI>, - <&pericfg CLK_PERI_NFI_PAD>; - clock-names = "nfi_clk", "pad_clk"; - ecc-engine = <&bch>; - #address-cells = <1>; - #size-cells = <0>; - }; - -Platform related properties, should be set in {platform_name}.dts: -- children nodes: NAND chips. - -Children nodes properties: -- reg: Chip Select Signal, default 0. - Set as reg = <0>, <1> when need 2 CS. -Optional: -- nand-on-flash-bbt: Store BBT on NAND Flash. -- nand-ecc-mode: the NAND ecc mode (check driver for supported modes) -- nand-ecc-step-size: Number of data bytes covered by a single ECC step. - valid values: - 512 and 1024 on mt2701 and mt2712. - 512 only on mt7622. - 1024 is recommended for large page NANDs. -- nand-ecc-strength: Number of bits to correct per ECC step. - The valid values that each controller supports: - mt2701: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, - 32, 36, 40, 44, 48, 52, 56, 60. - mt2712: 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, - 32, 36, 40, 44, 48, 52, 56, 60, 68, 72, 80. - mt7622: 4, 6, 8, 10, 12, 14, 16. - The strength should be calculated as follows: - E = (S - F) * 8 / B - S = O / (P / Q) - E : nand-ecc-strength. - S : spare size per sector. - F : FDM size, should be in the range [1,8]. - It is used to store free oob data. - O : oob size. - P : page size. - Q : nand-ecc-step-size. - B : number of parity bits needed to correct - 1 bitflip. - According to MTK NAND controller design, - this number depends on max ecc step size - that MTK NAND controller supports. - If max ecc step size supported is 1024, - then it should be always 14. And if max - ecc step size is 512, then it should be - always 13. - If the result does not match any one of the listed - choices above, please select the smaller valid value from - the list. - (otherwise the driver will do the adjustment at runtime) -- pinctrl-names: Default NAND pin GPIO setting name. -- pinctrl-0: GPIO setting node. - -Example: - &pio { - nand_pins_default: nanddefault { - pins_dat { - pinmux = , - , - , - , - , - , - , - , - ; - input-enable; - drive-strength = ; - bias-pull-up; - }; - - pins_we { - pinmux = ; - drive-strength = ; - bias-pull-up = ; - }; - - pins_ale { - pinmux = ; - drive-strength = ; - bias-pull-down = ; - }; - }; - }; - - &nandc { - status = "okay"; - pinctrl-names = "default"; - pinctrl-0 = <&nand_pins_default>; - nand@0 { - reg = <0>; - nand-on-flash-bbt; - nand-ecc-mode = "hw"; - nand-ecc-strength = <24>; - nand-ecc-step-size = <1024>; - }; - }; - -NAND chip optional subnodes: -- Partitions, see Documentation/devicetree/bindings/mtd/mtd.yaml - -Example: - nand@0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - preloader@0 { - label = "pl"; - read-only; - reg = <0x00000000 0x00400000>; - }; - android@00400000 { - label = "android"; - reg = <0x00400000 0x12c00000>; - }; - }; - }; - -2) ECC Engine: -============== - -Required BCH properties: -- compatible: Should be one of - "mediatek,mt2701-ecc", - "mediatek,mt2712-ecc", - "mediatek,mt7622-ecc". -- reg: Base physical address and size of ECC. -- interrupts: Interrupts of ECC. -- clocks: ECC required clocks. -- clock-names: ECC clocks internal name. - -Example: - - bch: ecc@1100e000 { - compatible = "mediatek,mt2701-ecc"; - reg = <0 0x1100e000 0 0x1000>; - interrupts = ; - clocks = <&pericfg CLK_PERI_NFI_ECC>; - clock-names = "nfiecc_clk"; - }; diff --git a/MAINTAINERS b/MAINTAINERS index f61eb221415b..75ef0b2ddb2e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13213,7 +13213,7 @@ F: drivers/phy/ralink/phy-mt7621-pci.c MEDIATEK NAND CONTROLLER DRIVER L: linux-mtd@lists.infradead.org S: Orphan -F: Documentation/devicetree/bindings/mtd/mtk-nand.txt +F: Documentation/devicetree/bindings/mtd/mediatek,mtk-nfc.yaml F: drivers/mtd/nand/raw/mtk_* MEDIATEK PMIC LED DRIVER From 70d3cf76f937fbef9c55eaffe1c848208e1372e2 Mon Sep 17 00:00:00 2001 From: Xiangsheng Hou Date: Wed, 1 Feb 2023 10:14:59 +0800 Subject: [PATCH 26/33] dt-bindings: mtd: mediatek,nand-ecc-engine: Add compatible for MT7986 Add dt-bindings documentation of ECC for MediaTek MT7986 SoC platform. Signed-off-by: Xiangsheng Hou Acked-by: Krzysztof Kozlowski Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230201021500.26769-5-xiangsheng.hou@mediatek.com --- .../devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml index b13d801eda76..505baf1e8830 100644 --- a/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml +++ b/Documentation/devicetree/bindings/mtd/mediatek,nand-ecc-engine.yaml @@ -18,6 +18,7 @@ properties: - mediatek,mt2701-ecc - mediatek,mt2712-ecc - mediatek,mt7622-ecc + - mediatek,mt7986-ecc reg: items: From 4d21176f48124e06c3251af38350b05a50ac4b01 Mon Sep 17 00:00:00 2001 From: Xiangsheng Hou Date: Wed, 1 Feb 2023 10:15:00 +0800 Subject: [PATCH 27/33] mtd: nand: ecc-mtk: Add ECC support fot MT7986 IC Add ECC support fot MT7986 IC, and change err_mask value with GENMASK macro. Signed-off-by: Xiangsheng Hou Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Matthias Brugger Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230201021500.26769-6-xiangsheng.hou@mediatek.com --- drivers/mtd/nand/ecc-mtk.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c index 9f9b201fe706..c75bb8b80cc1 100644 --- a/drivers/mtd/nand/ecc-mtk.c +++ b/drivers/mtd/nand/ecc-mtk.c @@ -40,6 +40,10 @@ #define ECC_IDLE_REG(op) ((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE) #define ECC_CTL_REG(op) ((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON) +#define ECC_ERRMASK_MT7622 GENMASK(4, 0) +#define ECC_ERRMASK_MT2701 GENMASK(5, 0) +#define ECC_ERRMASK_MT2712 GENMASK(6, 0) + struct mtk_ecc_caps { u32 err_mask; u32 err_shift; @@ -79,6 +83,10 @@ static const u8 ecc_strength_mt7622[] = { 4, 6, 8, 10, 12 }; +static const u8 ecc_strength_mt7986[] = { + 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 +}; + enum mtk_ecc_regs { ECC_ENCPAR00, ECC_ENCIRQ_EN, @@ -451,7 +459,7 @@ unsigned int mtk_ecc_get_parity_bits(struct mtk_ecc *ecc) EXPORT_SYMBOL(mtk_ecc_get_parity_bits); static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { - .err_mask = 0x3f, + .err_mask = ECC_ERRMASK_MT2701, .err_shift = 8, .ecc_strength = ecc_strength_mt2701, .ecc_regs = mt2701_ecc_regs, @@ -462,7 +470,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2701 = { }; static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { - .err_mask = 0x7f, + .err_mask = ECC_ERRMASK_MT2712, .err_shift = 8, .ecc_strength = ecc_strength_mt2712, .ecc_regs = mt2712_ecc_regs, @@ -473,7 +481,7 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt2712 = { }; static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { - .err_mask = 0x1f, + .err_mask = ECC_ERRMASK_MT7622, .err_shift = 5, .ecc_strength = ecc_strength_mt7622, .ecc_regs = mt7622_ecc_regs, @@ -483,6 +491,17 @@ static const struct mtk_ecc_caps mtk_ecc_caps_mt7622 = { .pg_irq_sel = 0, }; +static const struct mtk_ecc_caps mtk_ecc_caps_mt7986 = { + .err_mask = ECC_ERRMASK_MT7622, + .err_shift = 8, + .ecc_strength = ecc_strength_mt7986, + .ecc_regs = mt2712_ecc_regs, + .num_ecc_strength = 11, + .ecc_mode_shift = 5, + .parity_bits = 14, + .pg_irq_sel = 1, +}; + static const struct of_device_id mtk_ecc_dt_match[] = { { .compatible = "mediatek,mt2701-ecc", @@ -493,6 +512,9 @@ static const struct of_device_id mtk_ecc_dt_match[] = { }, { .compatible = "mediatek,mt7622-ecc", .data = &mtk_ecc_caps_mt7622, + }, { + .compatible = "mediatek,mt7986-ecc", + .data = &mtk_ecc_caps_mt7986, }, {}, }; From f0f0cfdc3a024e21161714f2e05f0df3b84d42ad Mon Sep 17 00:00:00 2001 From: Louis Rannou Date: Fri, 3 Feb 2023 09:07:54 +0200 Subject: [PATCH 28/33] mtd: spi-nor: Fix shift-out-of-bounds in spi_nor_set_erase_type spi_nor_set_erase_type() was used either to set or to mask out an erase type. When we used it to mask out an erase type a shift-out-of-bounds was hit: UBSAN: shift-out-of-bounds in drivers/mtd/spi-nor/core.c:2237:24 shift exponent 4294967295 is too large for 32-bit type 'int' The setting of the size_{shift, mask} and of the opcode are unnecessary when the erase size is zero, as throughout the code just the erase size is considered to determine whether an erase type is supported or not. Setting the opcode to 0xFF was wrong too as nobody guarantees that 0xFF is an unused opcode. Thus when masking out an erase type, just set the erase size to zero. This will fix the shift-out-of-bounds. Fixes: 5390a8df769e ("mtd: spi-nor: add support to non-uniform SFDP SPI NOR flash memories") Cc: stable@vger.kernel.org Reported-by: Alexander Stein Signed-off-by: Louis Rannou Tested-by: Alexander Stein Link: https://lore.kernel.org/r/20230203070754.50677-1-tudor.ambarus@linaro.org [ta: refine changes, new commit message, fix compilation error] Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 9 +++++++++ drivers/mtd/spi-nor/core.h | 1 + drivers/mtd/spi-nor/sfdp.c | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index b500655f7937..3012758bbafa 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2026,6 +2026,15 @@ void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, u32 size, erase->size_mask = (1 << erase->size_shift) - 1; } +/** + * spi_nor_mask_erase_type() - mask out a SPI NOR erase type + * @erase: pointer to a structure that describes a SPI NOR erase type + */ +void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase) +{ + erase->size = 0; +} + /** * spi_nor_init_uniform_erase_map() - Initialize uniform erase map * @map: the erase map of the SPI NOR diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index f6d012e1f681..25423225c29d 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -681,6 +681,7 @@ void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode, void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, u32 size, u8 opcode); +void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase); struct spi_nor_erase_region * spi_nor_region_next(struct spi_nor_erase_region *region); void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map, diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 3acc01c3a900..526c5d57b905 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -875,7 +875,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, */ for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) if (!(regions_erase_type & BIT(erase[i].idx))) - spi_nor_set_erase_type(&erase[i], 0, 0xFF); + spi_nor_mask_erase_type(&erase[i]); return 0; } @@ -1089,7 +1089,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, erase_type[i].opcode = (dwords[SFDP_DWORD(2)] >> erase_type[i].idx * 8) & 0xFF; else - spi_nor_set_erase_type(&erase_type[i], 0u, 0xFF); + spi_nor_mask_erase_type(&erase_type[i]); } /* From 893fd950c89d516a7cf365700b2bd7bb3efc15a5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Thu, 2 Feb 2023 16:46:28 +0200 Subject: [PATCH 29/33] mtd: spi-nor: Sort headers alphabetically Sort headers alphabetically - it helps locating duplicates, and makes it easier to figure out where to insert new headers. Alphabetic order should also prove that each header is self-contained, i.e. can be included without prerequisites. Link: https://lore.kernel.org/r/20230202144628.14443-1-tudor.ambarus@linaro.org Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 15 +++++++-------- drivers/mtd/spi-nor/debugfs.c | 2 +- drivers/mtd/spi-nor/sfdp.c | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 3012758bbafa..0a78045ca1d9 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -9,19 +9,18 @@ #include #include -#include -#include -#include -#include -#include -#include #include - +#include +#include +#include #include +#include +#include #include #include +#include +#include #include -#include #include "core.h" diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index ff895f6758ea..845b78c7ecc7 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include -#include #include "core.h" diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 526c5d57b905..298ab5e53a8c 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -5,9 +5,9 @@ */ #include +#include #include #include -#include #include "core.h" From 3998a4611e8be2e9b5833e7aae29619ea0305437 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 4 Feb 2023 08:35:18 -0600 Subject: [PATCH 30/33] mtd: rawnand: sunxi: Update OOB layout to match hardware When using the hardware ECC engine, the OOB data is made available in the NFC_REG_USER_DATA registers, as one 32-bit word per ECC step. Any additional bytes are only accessible through raw reads and software descrambling. For efficiency, and to match the vendor driver, ignore these extra bytes when using hardware ECC. Note that until commit 34569d869532 ("mtd: rawnand: sunxi: Fix the size of the last OOB region"), this extra free area was reported with length zero, so this is not a functional change for any stable kernel user. Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230204143520.9682-2-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index e673ac46f2e8..3c32d31f20aa 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1604,6 +1604,13 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, return 0; } + /* + * The controller does not provide access to OOB bytes + * past the end of the ECC data. + */ + if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) + return -ERANGE; + oobregion->offset = section * (ecc->bytes + 4); if (section < ecc->steps) From ac1c7072e38e492b27353a6e7b144e64cbbf9495 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 4 Feb 2023 08:35:19 -0600 Subject: [PATCH 31/33] mtd: rawnand: sunxi: Embed sunxi_nand_hw_ecc by value The sunxi_nand_hw_ecc object is not shared, and it has the same lifetime as the sunxi_nand_chip which points to it, so we can embed it in the outer structure instead of using a pointer. This removes an unnecessary memory allocation and simplifies the error handling code. Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230204143520.9682-3-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 45 +++++-------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 3c32d31f20aa..a0d0cb17c150 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -193,7 +193,7 @@ struct sunxi_nand_hw_ecc { struct sunxi_nand_chip { struct list_head node; struct nand_chip nand; - struct sunxi_nand_hw_ecc *ecc; + struct sunxi_nand_hw_ecc ecc; unsigned long clk_rate; u32 timing_cfg; u32 timing_ctl; @@ -694,7 +694,7 @@ static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) | + ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc.mode) | NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE; if (nand->ecc.size == 512) @@ -1626,11 +1626,6 @@ static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { .free = sunxi_nand_ooblayout_free, }; -static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand) -{ - kfree(sunxi_nand->ecc); -} - static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct nand_ecc_ctrl *ecc, struct device_node *np) @@ -1641,7 +1636,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, struct mtd_info *mtd = nand_to_mtd(nand); struct nand_device *nanddev = mtd_to_nanddev(mtd); int nsectors; - int ret; int i; if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { @@ -1676,10 +1670,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, if (ecc->size != 512 && ecc->size != 1024) return -EINVAL; - sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL); - if (!sunxi_nand->ecc) - return -ENOMEM; - /* Prefer 1k ECC chunk over 512 ones */ if (ecc->size == 512 && mtd->writesize > 512) { ecc->size = 1024; @@ -1700,11 +1690,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, if (i >= ARRAY_SIZE(strengths)) { dev_err(nfc->dev, "unsupported strength\n"); - ret = -ENOTSUPP; - goto err; + return -ENOTSUPP; } - sunxi_nand->ecc->mode = i; + sunxi_nand->ecc.mode = i; /* HW ECC always request ECC bytes for 1024 bytes blocks */ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); @@ -1714,10 +1703,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { - ret = -EINVAL; - goto err; - } + if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) + return -EINVAL; ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; @@ -1740,25 +1727,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->write_oob_raw = nand_write_oob_std; return 0; - -err: - kfree(sunxi_nand->ecc); - - return ret; -} - -static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand) -{ - struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc; - - switch (ecc->engine_type) { - case NAND_ECC_ENGINE_TYPE_ON_HOST: - sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand); - break; - case NAND_ECC_ENGINE_TYPE_NONE: - default: - break; - } } static int sunxi_nand_attach_chip(struct nand_chip *nand) @@ -1971,7 +1939,6 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) ret = mtd_device_unregister(nand_to_mtd(chip)); WARN_ON(ret); nand_cleanup(chip); - sunxi_nand_ecc_cleanup(sunxi_nand); list_del(&sunxi_nand->node); } } From ef3e6327ff04af8527b3558e023e99f1cc241bce Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 4 Feb 2023 08:35:20 -0600 Subject: [PATCH 32/33] mtd: rawnand: sunxi: Precompute the ECC_CTL register value The value computed by this function never changes for a given chip. Compute the whole register value once up front, instead of every time the ECC engine is enabled. Signed-off-by: Samuel Holland Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230204143520.9682-4-samuel@sholland.org --- drivers/mtd/nand/raw/sunxi_nand.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index a0d0cb17c150..13e3e0198d15 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -172,10 +172,10 @@ struct sunxi_nand_chip_sel { /** * struct sunxi_nand_hw_ecc - stores information related to HW ECC support * - * @mode: the sunxi ECC mode field deduced from ECC requirements + * @ecc_ctl: ECC_CTL register value for this NAND chip */ struct sunxi_nand_hw_ecc { - int mode; + u32 ecc_ctl; }; /** @@ -689,26 +689,15 @@ static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) { struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - u32 ecc_ctl; - ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); - ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | - NFC_ECC_BLOCK_SIZE_MSK); - ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc.mode) | - NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE; - - if (nand->ecc.size == 512) - ecc_ctl |= NFC_ECC_BLOCK_512; - - writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); + writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); } static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) { struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); - writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, - nfc->regs + NFC_REG_ECC_CTL); + writel(0, nfc->regs + NFC_REG_ECC_CTL); } static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) @@ -1693,8 +1682,6 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, return -ENOTSUPP; } - sunxi_nand->ecc.mode = i; - /* HW ECC always request ECC bytes for 1024 bytes blocks */ ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); @@ -1726,6 +1713,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, ecc->read_oob_raw = nand_read_oob_std; ecc->write_oob_raw = nand_write_oob_std; + sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION | + NFC_ECC_PIPELINE | NFC_ECC_EN; + + if (ecc->size == 512) + sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; + return 0; } From 84549c816dc317f012798e706e58669b3b013604 Mon Sep 17 00:00:00 2001 From: Francesco Dolcini Date: Tue, 24 Jan 2023 11:44:44 +0100 Subject: [PATCH 33/33] mtd: parsers: ofpart: add workaround for #size-cells 0 Add a mechanism to handle the case in which partitions are present as direct child of the nand controller node and #size-cells is set to <0>. This could happen if the nand-controller node in the DTS is supposed to have #size-cells set to 0, but for some historical reason/bug it was set to 1 in the past, and the firmware (e.g. U-Boot) is adding the partition as direct children of the nand-controller defaulting to #size-cells being to 1. This prevents a real boot failure on colibri-imx7 that happened during v6.1 development cycles. Link: https://lore.kernel.org/all/Y4dgBTGNWpM6SQXI@francesco-nb.int.toradex.com/ Link: https://lore.kernel.org/all/20221202071900.1143950-1-francesco@dolcini.it/ Signed-off-by: Francesco Dolcini Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20230124104444.330913-1-francesco@dolcini.it --- drivers/mtd/parsers/ofpart_core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index 192190c42fc8..e7b8e9d0a910 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -122,6 +122,25 @@ static int parse_fixed_partitions(struct mtd_info *master, a_cells = of_n_addr_cells(pp); s_cells = of_n_size_cells(pp); + if (!dedicated && s_cells == 0) { + /* + * This is a ugly workaround to not create + * regression on devices that are still creating + * partitions as direct children of the nand controller. + * This can happen in case the nand controller node has + * #size-cells equal to 0 and the firmware (e.g. + * U-Boot) just add the partitions there assuming + * 32-bit addressing. + * + * If you get this warning your firmware and/or DTS + * should be really fixed. + * + * This is working only for devices smaller than 4GiB. + */ + pr_warn("%s: ofpart partition %pOF (%pOF) #size-cells is wrongly set to <0>, assuming <1> for parsing partitions.\n", + master->name, pp, mtd_node); + s_cells = 1; + } if (len / 4 != a_cells + s_cells) { pr_debug("%s: ofpart partition %pOF (%pOF) error parsing reg property.\n", master->name, pp,