From 336c218dd7f0588ed8a7345f367975a00a4f003f Mon Sep 17 00:00:00 2001 From: Mirsad Todorovac Date: Fri, 12 Jul 2024 01:43:20 +0200 Subject: [PATCH 01/41] mtd: slram: insert break after errors in parsing the map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 12.3.0 compiler on linux-next next-20240709 tree found the execution path in which, due to lazy evaluation, devlength isn't initialised with the parsed string: 289 while (map) { 290 devname = devstart = devlength = NULL; 291 292 if (!(devname = strsep(&map, ","))) { 293 E("slram: No devicename specified.\n"); 294 break; 295 } 296 T("slram: devname = %s\n", devname); 297 if ((!map) || (!(devstart = strsep(&map, ",")))) { 298 E("slram: No devicestart specified.\n"); 299 } 300 T("slram: devstart = %s\n", devstart); → 301 if ((!map) || (!(devlength = strsep(&map, ",")))) { 302 E("slram: No devicelength / -end specified.\n"); 303 } → 304 T("slram: devlength = %s\n", devlength); 305 if (parse_cmdline(devname, devstart, devlength) != 0) { 306 return(-EINVAL); 307 } Parsing should be finished after map == NULL, so a break is best inserted after each E("slram: ... \n") error message. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: Miquel Raynal Cc: Richard Weinberger Cc: Vignesh Raghavendra Cc: linux-mtd@lists.infradead.org Signed-off-by: Mirsad Todorovac Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240711234319.637824-1-mtodorovac69@gmail.com --- drivers/mtd/devices/slram.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 28131a127d06..8297b366a066 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -296,10 +296,12 @@ static int __init init_slram(void) T("slram: devname = %s\n", devname); if ((!map) || (!(devstart = strsep(&map, ",")))) { E("slram: No devicestart specified.\n"); + break; } T("slram: devstart = %s\n", devstart); if ((!map) || (!(devlength = strsep(&map, ",")))) { E("slram: No devicelength / -end specified.\n"); + break; } T("slram: devlength = %s\n", devlength); if (parse_cmdline(devname, devstart, devlength) != 0) { From ea265e483eb3d9750c7cfc642dedd5be31dc22c2 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 31 Jul 2024 13:13:00 -0600 Subject: [PATCH 02/41] mtd: Use of_property_read_bool() Use of_property_read_bool() to read boolean properties rather than of_get_property(). This is part of a larger effort to remove callers of of_get_property() and similar functions. of_get_property() leaks the DT property data pointer which is a problem for dynamically allocated nodes which may be freed. Signed-off-by: Rob Herring (Arm) Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240731191312.1710417-22-robh@kernel.org --- drivers/mtd/parsers/ofpart_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/parsers/ofpart_core.c b/drivers/mtd/parsers/ofpart_core.c index e7b8e9d0a910..abfa68798918 100644 --- a/drivers/mtd/parsers/ofpart_core.c +++ b/drivers/mtd/parsers/ofpart_core.c @@ -157,10 +157,10 @@ static int parse_fixed_partitions(struct mtd_info *master, partname = of_get_property(pp, "name", &len); parts[i].name = partname; - if (of_get_property(pp, "read-only", &len)) + if (of_property_read_bool(pp, "read-only")) parts[i].mask_flags |= MTD_WRITEABLE; - if (of_get_property(pp, "lock", &len)) + if (of_property_read_bool(pp, "lock")) parts[i].mask_flags |= MTD_POWERUP_LOCK; if (of_property_read_bool(pp, "slc-mode")) From e334c01df28225139cc831e4871c590a7851b4fc Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 22 Aug 2024 17:46:32 +0100 Subject: [PATCH 03/41] mtd: parsers: bcm47xxpart: make read-only array possible_nvram_sizes static const Don't populate the read-only array possible_nvram_sizes on the stack at run time, instead make it static const. Signed-off-by: Colin Ian King Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240822164632.638171-1-colin.i.king@gmail.com --- drivers/mtd/parsers/bcm47xxpart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/parsers/bcm47xxpart.c b/drivers/mtd/parsers/bcm47xxpart.c index 13daf9bffd08..49c8e7f27f21 100644 --- a/drivers/mtd/parsers/bcm47xxpart.c +++ b/drivers/mtd/parsers/bcm47xxpart.c @@ -95,7 +95,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, uint32_t blocksize = master->erasesize; int trx_parts[2]; /* Array with indexes of TRX partitions */ int trx_num = 0; /* Number of found TRX partitions */ - int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; + static const int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; int err; /* From 175086cf4acdf4ccd3d7a6bb5c5231ececd6656b Mon Sep 17 00:00:00 2001 From: Yan Zhen Date: Fri, 23 Aug 2024 19:08:24 +0800 Subject: [PATCH 04/41] mtd: concat: Use kmemdup_array instead of kmemdup for multiple allocation When we are allocating an array, using kmemdup_array() to take care about multiplication and possible overflows. Also it makes auditing the code easier. Signed-off-by: Yan Zhen Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240823110824.3895787-1-yanzhen@vivo.com --- drivers/mtd/mtdconcat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 193428de6a4b..f56f44aa8625 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -204,7 +204,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } /* make a copy of vecs */ - vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); + vecs_copy = kmemdup_array(vecs, count, sizeof(struct kvec), GFP_KERNEL); if (!vecs_copy) return -ENOMEM; From e2a9fcb36e851adb5b25c4acea53a290fd48a636 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 5 Aug 2024 19:51:02 +0200 Subject: [PATCH 05/41] mtd: spinand: winbond: add support for W25N01KV Add support for Winbond W25N01KV 1Gbit SPI-NAND. It has 4-bit on-die ECC. Signed-off-by: Robert Marko Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240805175125.6658-1-robimarko@gmail.com --- drivers/mtd/nand/spi/winbond.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index ba7c813b9542..f3bb81d7e460 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -76,6 +76,18 @@ static int w25m02gv_select_target(struct spinand_device *spinand, return spi_mem_exec_op(spinand->spimem, &op); } +static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = 64 + (8 * section); + region->length = 7; + + return 0; +} + static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *region) { @@ -100,6 +112,11 @@ static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, return 0; } +static const struct mtd_ooblayout_ops w25n01kv_ooblayout = { + .ecc = w25n01kv_ooblayout_ecc, + .free = w25n02kv_ooblayout_free, +}; + static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { .ecc = w25n02kv_ooblayout_ecc, .free = w25n02kv_ooblayout_free, @@ -163,6 +180,15 @@ static const struct spinand_info winbond_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), + SPINAND_INFO("W25N01KV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae, 0x21), + NAND_MEMORG(1, 2048, 96, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25n01kv_ooblayout, w25n02kv_ecc_get_status)), SPINAND_INFO("W25N02KV", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), From 1824520e7477bedf76bd08c32261c755e6405cd9 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 12 Aug 2024 02:56:41 +0100 Subject: [PATCH 06/41] mtd: spinand: set bitflip_threshold to 75% of ECC strength Reporting an unclean read from SPI-NAND only when the maximum number of correctable bitflip errors has been hit seems a bit late. UBI LEB scrubbing, which depends on the lower MTD device reporting correctable bitflips, then only kicks in when it's almost too late. Set bitflip_threshold to 75% of the ECC strength, which is also the default for raw NAND. Signed-off-by: Daniel Golle Reviewed-by: Frieder Schrempf Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/2117e387260b0a96f95b8e1652ff79e0e2d71d53.1723427450.git.daniel@makrotopia.org --- drivers/mtd/nand/spi/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index e0b6715e5dfe..018c854d0619 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1287,6 +1287,7 @@ static int spinand_init(struct spinand_device *spinand) /* Propagate ECC information to mtd_info */ mtd->ecc_strength = nanddev_get_ecc_conf(nand)->strength; mtd->ecc_step_size = nanddev_get_ecc_conf(nand)->step_size; + mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4); ret = spinand_create_dirmaps(spinand); if (ret) { From ccce71013406a0a3c81850dab940f07b112349d3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Aug 2024 14:21:18 +0200 Subject: [PATCH 07/41] mtd: rawnand: davinci: make platform_data private There are no longer any users of the platform data for davinci rawnand in board files. We can remove the public pdata headers and move the structures that are still used into the driver compilation unit while removing the rest. Signed-off-by: Bartosz Golaszewski Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240814122120.13975-1-brgl@bgdev.pl --- drivers/mtd/nand/raw/davinci_nand.c | 70 +++++++++++++-- .../linux/platform_data/mtd-davinci-aemif.h | 36 -------- include/linux/platform_data/mtd-davinci.h | 88 ------------------- 3 files changed, 65 insertions(+), 129 deletions(-) delete mode 100644 include/linux/platform_data/mtd-davinci-aemif.h delete mode 100644 include/linux/platform_data/mtd-davinci.h diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 051deea768db..392678143a36 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -20,8 +20,71 @@ #include #include -#include -#include +#define NRCSR_OFFSET 0x00 +#define NANDFCR_OFFSET 0x60 +#define NANDFSR_OFFSET 0x64 +#define NANDF1ECC_OFFSET 0x70 + +/* 4-bit ECC syndrome registers */ +#define NAND_4BIT_ECC_LOAD_OFFSET 0xbc +#define NAND_4BIT_ECC1_OFFSET 0xc0 +#define NAND_4BIT_ECC2_OFFSET 0xc4 +#define NAND_4BIT_ECC3_OFFSET 0xc8 +#define NAND_4BIT_ECC4_OFFSET 0xcc +#define NAND_ERR_ADD1_OFFSET 0xd0 +#define NAND_ERR_ADD2_OFFSET 0xd4 +#define NAND_ERR_ERRVAL1_OFFSET 0xd8 +#define NAND_ERR_ERRVAL2_OFFSET 0xdc + +/* NOTE: boards don't need to use these address bits + * for ALE/CLE unless they support booting from NAND. + * They're used unless platform data overrides them. + */ +#define MASK_ALE 0x08 +#define MASK_CLE 0x10 + +struct davinci_nand_pdata { + uint32_t mask_ale; + uint32_t mask_cle; + + /* + * 0-indexed chip-select number of the asynchronous + * interface to which the NAND device has been connected. + * + * So, if you have NAND connected to CS3 of DA850, you + * will pass '1' here. Since the asynchronous interface + * on DA850 starts from CS2. + */ + uint32_t core_chipsel; + + /* for packages using two chipselects */ + uint32_t mask_chipsel; + + /* board's default static partition info */ + struct mtd_partition *parts; + unsigned int nr_parts; + + /* none == NAND_ECC_ENGINE_TYPE_NONE (strongly *not* advised!!) + * soft == NAND_ECC_ENGINE_TYPE_SOFT + * else == NAND_ECC_ENGINE_TYPE_ON_HOST, according to ecc_bits + * + * All DaVinci-family chips support 1-bit hardware ECC. + * Newer ones also support 4-bit ECC, but are awkward + * using it with large page chips. + */ + enum nand_ecc_engine_type engine_type; + enum nand_ecc_placement ecc_placement; + u8 ecc_bits; + + /* e.g. NAND_BUSWIDTH_16 */ + unsigned int options; + /* e.g. NAND_BBT_USE_FLASH */ + unsigned int bbt_options; + + /* Main and mirror bbt descriptor overrides */ + struct nand_bbt_descr *bbt_td; + struct nand_bbt_descr *bbt_md; +}; /* * This is a device driver for the NAND flash controller found on the @@ -54,8 +117,6 @@ struct davinci_nand_info { uint32_t mask_cle; uint32_t core_chipsel; - - struct davinci_aemif_timing *timing; }; static DEFINE_SPINLOCK(davinci_nand_lock); @@ -775,7 +836,6 @@ static int nand_davinci_probe(struct platform_device *pdev) info->chip.options = pdata->options; info->chip.bbt_td = pdata->bbt_td; info->chip.bbt_md = pdata->bbt_md; - info->timing = pdata->timing; info->current_cs = info->vaddr; info->core_chipsel = pdata->core_chipsel; diff --git a/include/linux/platform_data/mtd-davinci-aemif.h b/include/linux/platform_data/mtd-davinci-aemif.h deleted file mode 100644 index a49826214a39..000000000000 --- a/include/linux/platform_data/mtd-davinci-aemif.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * TI DaVinci AEMIF support - * - * Copyright 2010 (C) Texas Instruments, Inc. https://www.ti.com/ - * - * 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. - */ -#ifndef _MACH_DAVINCI_AEMIF_H -#define _MACH_DAVINCI_AEMIF_H - -#include - -#define NRCSR_OFFSET 0x00 -#define AWCCR_OFFSET 0x04 -#define A1CR_OFFSET 0x10 - -#define ACR_ASIZE_MASK 0x3 -#define ACR_EW_MASK BIT(30) -#define ACR_SS_MASK BIT(31) - -/* All timings in nanoseconds */ -struct davinci_aemif_timing { - u8 wsetup; - u8 wstrobe; - u8 whold; - - u8 rsetup; - u8 rstrobe; - u8 rhold; - - u8 ta; -}; - -#endif diff --git a/include/linux/platform_data/mtd-davinci.h b/include/linux/platform_data/mtd-davinci.h deleted file mode 100644 index dd474dd44848..000000000000 --- a/include/linux/platform_data/mtd-davinci.h +++ /dev/null @@ -1,88 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * mach-davinci/nand.h - * - * Copyright © 2006 Texas Instruments. - * - * Ported to 2.6.23 Copyright © 2008 by - * Sander Huijsen - * Troy Kisky - * Dirk Behme - * - * -------------------------------------------------------------------------- - */ - -#ifndef __ARCH_ARM_DAVINCI_NAND_H -#define __ARCH_ARM_DAVINCI_NAND_H - -#include - -#define NANDFCR_OFFSET 0x60 -#define NANDFSR_OFFSET 0x64 -#define NANDF1ECC_OFFSET 0x70 - -/* 4-bit ECC syndrome registers */ -#define NAND_4BIT_ECC_LOAD_OFFSET 0xbc -#define NAND_4BIT_ECC1_OFFSET 0xc0 -#define NAND_4BIT_ECC2_OFFSET 0xc4 -#define NAND_4BIT_ECC3_OFFSET 0xc8 -#define NAND_4BIT_ECC4_OFFSET 0xcc -#define NAND_ERR_ADD1_OFFSET 0xd0 -#define NAND_ERR_ADD2_OFFSET 0xd4 -#define NAND_ERR_ERRVAL1_OFFSET 0xd8 -#define NAND_ERR_ERRVAL2_OFFSET 0xdc - -/* NOTE: boards don't need to use these address bits - * for ALE/CLE unless they support booting from NAND. - * They're used unless platform data overrides them. - */ -#define MASK_ALE 0x08 -#define MASK_CLE 0x10 - -struct davinci_nand_pdata { /* platform_data */ - uint32_t mask_ale; - uint32_t mask_cle; - - /* - * 0-indexed chip-select number of the asynchronous - * interface to which the NAND device has been connected. - * - * So, if you have NAND connected to CS3 of DA850, you - * will pass '1' here. Since the asynchronous interface - * on DA850 starts from CS2. - */ - uint32_t core_chipsel; - - /* for packages using two chipselects */ - uint32_t mask_chipsel; - - /* board's default static partition info */ - struct mtd_partition *parts; - unsigned nr_parts; - - /* none == NAND_ECC_ENGINE_TYPE_NONE (strongly *not* advised!!) - * soft == NAND_ECC_ENGINE_TYPE_SOFT - * else == NAND_ECC_ENGINE_TYPE_ON_HOST, according to ecc_bits - * - * All DaVinci-family chips support 1-bit hardware ECC. - * Newer ones also support 4-bit ECC, but are awkward - * using it with large page chips. - */ - enum nand_ecc_engine_type engine_type; - enum nand_ecc_placement ecc_placement; - u8 ecc_bits; - - /* e.g. NAND_BUSWIDTH_16 */ - unsigned options; - /* e.g. NAND_BBT_USE_FLASH */ - unsigned bbt_options; - - /* Main and mirror bbt descriptor overrides */ - struct nand_bbt_descr *bbt_td; - struct nand_bbt_descr *bbt_md; - - /* Access timings */ - struct davinci_aemif_timing *timing; -}; - -#endif /* __ARCH_ARM_DAVINCI_NAND_H */ From d43b24f50da293009050e172e7c3769571db79c6 Mon Sep 17 00:00:00 2001 From: Chen Ridong Date: Mon, 26 Aug 2024 02:43:39 +0000 Subject: [PATCH 08/41] mtd: rawnand: denali: Fix missing pci_release_regions in probe and remove The pci_release_regions was miss at error case, just add it. Signed-off-by: Chen Ridong Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826024339.476921-1-chenridong@huawei.com --- drivers/mtd/nand/raw/denali_pci.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/denali_pci.c b/drivers/mtd/nand/raw/denali_pci.c index de7e722d3826..e22094e39546 100644 --- a/drivers/mtd/nand/raw/denali_pci.c +++ b/drivers/mtd/nand/raw/denali_pci.c @@ -77,18 +77,20 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) denali->reg = devm_ioremap(denali->dev, csr_base, csr_len); if (!denali->reg) { dev_err(&dev->dev, "Spectra: Unable to remap memory region\n"); - return -ENOMEM; + ret = -ENOMEM; + goto regions_release; } denali->host = devm_ioremap(denali->dev, mem_base, mem_len); if (!denali->host) { dev_err(&dev->dev, "Spectra: ioremap failed!"); - return -ENOMEM; + ret = -ENOMEM; + goto regions_release; } ret = denali_init(denali); if (ret) - return ret; + goto regions_release; nsels = denali->nbanks; @@ -116,6 +118,8 @@ static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) out_remove_denali: denali_remove(denali); +regions_release: + pci_release_regions(dev); return ret; } @@ -123,6 +127,7 @@ static void denali_pci_remove(struct pci_dev *dev) { struct denali_controller *denali = pci_get_drvdata(dev); + pci_release_regions(dev); denali_remove(denali); } From 3c0e167c21721901413e8bc2293642f4ee6f983f Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 16:04:08 +0800 Subject: [PATCH 09/41] mtd: rawnand: denali: Use the devm_clk_get_enabled() helper function The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This simplifies the code. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826080408.2522978-1-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/denali_dt.c | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index edac8749bb93..2f5666511fda 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -145,15 +145,15 @@ static int denali_dt_probe(struct platform_device *pdev) if (IS_ERR(denali->host)) return PTR_ERR(denali->host); - dt->clk = devm_clk_get(dev, "nand"); + dt->clk = devm_clk_get_enabled(dev, "nand"); if (IS_ERR(dt->clk)) return PTR_ERR(dt->clk); - dt->clk_x = devm_clk_get(dev, "nand_x"); + dt->clk_x = devm_clk_get_enabled(dev, "nand_x"); if (IS_ERR(dt->clk_x)) return PTR_ERR(dt->clk_x); - dt->clk_ecc = devm_clk_get(dev, "ecc"); + dt->clk_ecc = devm_clk_get_enabled(dev, "ecc"); if (IS_ERR(dt->clk_ecc)) return PTR_ERR(dt->clk_ecc); @@ -165,18 +165,6 @@ static int denali_dt_probe(struct platform_device *pdev) if (IS_ERR(dt->rst_reg)) return PTR_ERR(dt->rst_reg); - ret = clk_prepare_enable(dt->clk); - if (ret) - return ret; - - ret = clk_prepare_enable(dt->clk_x); - if (ret) - goto out_disable_clk; - - ret = clk_prepare_enable(dt->clk_ecc); - if (ret) - goto out_disable_clk_x; - denali->clk_rate = clk_get_rate(dt->clk); denali->clk_x_rate = clk_get_rate(dt->clk_x); @@ -187,7 +175,7 @@ static int denali_dt_probe(struct platform_device *pdev) */ ret = reset_control_deassert(dt->rst_reg); if (ret) - goto out_disable_clk_ecc; + return ret; ret = reset_control_deassert(dt->rst); if (ret) @@ -222,12 +210,6 @@ out_assert_rst: reset_control_assert(dt->rst); out_assert_rst_reg: reset_control_assert(dt->rst_reg); -out_disable_clk_ecc: - clk_disable_unprepare(dt->clk_ecc); -out_disable_clk_x: - clk_disable_unprepare(dt->clk_x); -out_disable_clk: - clk_disable_unprepare(dt->clk); return ret; } @@ -239,9 +221,6 @@ static void denali_dt_remove(struct platform_device *pdev) denali_remove(&dt->controller); reset_control_assert(dt->rst); reset_control_assert(dt->rst_reg); - clk_disable_unprepare(dt->clk_ecc); - clk_disable_unprepare(dt->clk_x); - clk_disable_unprepare(dt->clk); } static struct platform_driver denali_dt_driver = { From 7021a797689d288a06cb48d0da44880d1dcaff8d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:19 +0800 Subject: [PATCH 10/41] mtd: rawnand: arasan: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-2-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/arasan-nand-controller.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index 2ff1d2b13e3c..5436ec4a8fde 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -1360,7 +1360,7 @@ static void anfc_chips_cleanup(struct arasan_nfc *nfc) static int anfc_chips_init(struct arasan_nfc *nfc) { - struct device_node *np = nfc->dev->of_node, *nand_np; + struct device_node *np = nfc->dev->of_node; int nchips = of_get_child_count(np); int ret; @@ -1370,10 +1370,9 @@ static int anfc_chips_init(struct arasan_nfc *nfc) return -EINVAL; } - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = anfc_chip_init(nfc, nand_np); if (ret) { - of_node_put(nand_np); anfc_chips_cleanup(nfc); break; } From fc214e50e292550dcd77e459e59a2a385bda4735 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:20 +0800 Subject: [PATCH 11/41] mtd: rawnand: cadence: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop by using for_each_child_of_node_scoped(). Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-3-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/cadence-nand-controller.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c index ff92c17def83..3bc89b356963 100644 --- a/drivers/mtd/nand/raw/cadence-nand-controller.c +++ b/drivers/mtd/nand/raw/cadence-nand-controller.c @@ -2836,7 +2836,6 @@ static void cadence_nand_chips_cleanup(struct cdns_nand_ctrl *cdns_ctrl) static int cadence_nand_chips_init(struct cdns_nand_ctrl *cdns_ctrl) { struct device_node *np = cdns_ctrl->dev->of_node; - struct device_node *nand_np; int max_cs = cdns_ctrl->caps2.max_banks; int nchips, ret; @@ -2849,10 +2848,9 @@ static int cadence_nand_chips_init(struct cdns_nand_ctrl *cdns_ctrl) return -EINVAL; } - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = cadence_nand_chip_init(cdns_ctrl, nand_np); if (ret) { - of_node_put(nand_np); cadence_nand_chips_cleanup(cdns_ctrl); return ret; } From e2e4eddf7b16a0cb431878e95e8bb80d893e4077 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:21 +0800 Subject: [PATCH 12/41] mtd: rawnand: pl353: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop by using for_each_child_of_node_scoped(). Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-4-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/pl35x-nand-controller.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 1c76ee98efb7..2570fd0beea0 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -1111,7 +1111,7 @@ static void pl35x_nand_chips_cleanup(struct pl35x_nandc *nfc) static int pl35x_nand_chips_init(struct pl35x_nandc *nfc) { - struct device_node *np = nfc->dev->of_node, *nand_np; + struct device_node *np = nfc->dev->of_node; int nchips = of_get_child_count(np); int ret; @@ -1121,10 +1121,9 @@ static int pl35x_nand_chips_init(struct pl35x_nandc *nfc) return -EINVAL; } - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = pl35x_nand_chip_init(nfc, nand_np); if (ret) { - of_node_put(nand_np); pl35x_nand_chips_cleanup(nfc); break; } From 707f2d0720ed6831d736cca084b62ba97eb7fa23 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:22 +0800 Subject: [PATCH 13/41] mtd: rawnand: marvell: drm/rockchip: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-5-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/marvell_nand.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 5b0f5a9cef81..26648b72e691 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -2771,7 +2771,6 @@ static void marvell_nand_chips_cleanup(struct marvell_nfc *nfc) static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc) { struct device_node *np = dev->of_node; - struct device_node *nand_np; int max_cs = nfc->caps->max_cs_nb; int nchips; int ret; @@ -2798,20 +2797,15 @@ static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc) return ret; } - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = marvell_nand_chip_init(dev, nfc, nand_np); if (ret) { - of_node_put(nand_np); - goto cleanup_chips; + marvell_nand_chips_cleanup(nfc); + return ret; } } return 0; - -cleanup_chips: - marvell_nand_chips_cleanup(nfc); - - return ret; } static int marvell_nfc_init_dma(struct marvell_nfc *nfc) From 0d5c32b5c877d61afc954c760dc58f3b8626ec04 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:23 +0800 Subject: [PATCH 14/41] mtd: rawnand: rockchip: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-6-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/rockchip-nand-controller.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index 55580447633b..51c9cf9013dc 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -1211,7 +1211,7 @@ static void rk_nfc_chips_cleanup(struct rk_nfc *nfc) static int rk_nfc_nand_chips_init(struct device *dev, struct rk_nfc *nfc) { - struct device_node *np = dev->of_node, *nand_np; + struct device_node *np = dev->of_node; int nchips = of_get_child_count(np); int ret; @@ -1221,10 +1221,9 @@ static int rk_nfc_nand_chips_init(struct device *dev, struct rk_nfc *nfc) return -EINVAL; } - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = rk_nfc_nand_chip_init(dev, nfc, nand_np); if (ret) { - of_node_put(nand_np); rk_nfc_chips_cleanup(nfc); return ret; } From ee06c7821d0426d0f998593cbc442757247e7cd6 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:24 +0800 Subject: [PATCH 15/41] mtd: rawnand: meson: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-7-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/meson_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 9eb5470344d0..8806a06462ac 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1495,14 +1495,12 @@ static int meson_nfc_nand_chips_init(struct device *dev, struct meson_nfc *nfc) { struct device_node *np = dev->of_node; - struct device_node *nand_np; int ret; - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); if (ret) { meson_nfc_nand_chip_cleanup(nfc); - of_node_put(nand_np); return ret; } } From 8795952679494b111b7b2ba08bb54ac408daca3b Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:25 +0800 Subject: [PATCH 16/41] mtd: rawnand: mtk: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-8-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/mtk_nand.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index 17477bb2d48f..d65e6371675b 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1432,15 +1432,12 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) { struct device_node *np = dev->of_node; - struct device_node *nand_np; int ret; - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); - if (ret) { - of_node_put(nand_np); + if (ret) return ret; - } } return 0; From f3b3c47ca41f696623e71596416d34fe1671d229 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:26 +0800 Subject: [PATCH 17/41] mtd: rawnand: renesas: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Reviewed-by: Geert Uytterhoeven Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-9-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/renesas-nand-controller.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c index c9a01feff8df..0e92d50c5249 100644 --- a/drivers/mtd/nand/raw/renesas-nand-controller.c +++ b/drivers/mtd/nand/raw/renesas-nand-controller.c @@ -1297,23 +1297,17 @@ static void rnandc_chips_cleanup(struct rnandc *rnandc) static int rnandc_chips_init(struct rnandc *rnandc) { - struct device_node *np; int ret; - for_each_child_of_node(rnandc->dev->of_node, np) { + for_each_child_of_node_scoped(rnandc->dev->of_node, np) { ret = rnandc_chip_init(rnandc, np); if (ret) { - of_node_put(np); - goto cleanup_chips; + rnandc_chips_cleanup(rnandc); + return ret; } } return 0; - -cleanup_chips: - rnandc_chips_cleanup(rnandc); - - return ret; } static int rnandc_probe(struct platform_device *pdev) From f5b30c7f47f25751b4c35b69d5ad42b1ed81adac Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:27 +0800 Subject: [PATCH 18/41] mtd: rawnand: stm32_fmc2: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-10-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/stm32_fmc2_nand.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 264556939a00..0f67e96cc240 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1851,7 +1851,6 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc, static int stm32_fmc2_nfc_parse_dt(struct stm32_fmc2_nfc *nfc) { struct device_node *dn = nfc->dev->of_node; - struct device_node *child; int nchips = of_get_child_count(dn); int ret = 0; @@ -1865,12 +1864,10 @@ static int stm32_fmc2_nfc_parse_dt(struct stm32_fmc2_nfc *nfc) return -EINVAL; } - for_each_child_of_node(dn, child) { + for_each_child_of_node_scoped(dn, child) { ret = stm32_fmc2_nfc_parse_child(nfc, child); - if (ret < 0) { - of_node_put(child); + if (ret < 0) return ret; - } } return ret; From b59fdc7f38812fa811a1f8d63ca2d42c9728c09c Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 26 Aug 2024 17:43:28 +0800 Subject: [PATCH 19/41] mtd: rawnand: sunxi: Use for_each_child_of_node_scoped() Avoids the need for manual cleanup of_node_put() in early exits from the loop. Signed-off-by: Jinjie Ruan Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826094328.2991664-11-ruanjinjie@huawei.com --- drivers/mtd/nand/raw/sunxi_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 4ec17c8bce5a..c28634e20abf 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -2025,13 +2025,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, 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 ret; - for_each_child_of_node(np, nand_np) { + for_each_child_of_node_scoped(np, nand_np) { ret = sunxi_nand_chip_init(dev, nfc, nand_np); if (ret) { - of_node_put(nand_np); sunxi_nand_chips_cleanup(nfc); return ret; } From 3f4c0ad490cc55db5b375a2e19c67159cb255795 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:04 +0200 Subject: [PATCH 20/41] mtd: nand: Rename the NAND IO iteration helper Soon a helper for iterating over blocks will be needed (for continuous read purposes). In order to clarify the intend of this helper, let's rename it with the "page" wording inside. While at it, improve the doc and fix a typo. Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-2-miquel.raynal@bootlin.com --- include/linux/mtd/nand.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index b2996dc987ff..92e06ac33815 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -906,19 +906,19 @@ static inline void nanddev_pos_next_page(struct nand_device *nand, } /** - * nand_io_iter_init - Initialize a NAND I/O iterator + * nand_io_page_iter_init - Initialize a NAND I/O iterator * @nand: NAND device * @offs: absolute offset * @req: MTD request * @iter: NAND I/O iterator * * Initializes a NAND iterator based on the information passed by the MTD - * layer. + * layer for page jumps. */ -static inline void nanddev_io_iter_init(struct nand_device *nand, - enum nand_page_io_req_type reqtype, - loff_t offs, struct mtd_oob_ops *req, - struct nand_io_iter *iter) +static inline void nanddev_io_page_iter_init(struct nand_device *nand, + enum nand_page_io_req_type reqtype, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) { struct mtd_info *mtd = nanddev_to_mtd(nand); @@ -990,10 +990,10 @@ static inline bool nanddev_io_iter_end(struct nand_device *nand, * @req: MTD I/O request * @iter: NAND I/O iterator * - * Should be used for iterate over pages that are contained in an MTD request. + * Should be used for iterating over pages that are contained in an MTD request. */ #define nanddev_io_for_each_page(nand, type, start, req, iter) \ - for (nanddev_io_iter_init(nand, type, start, req, iter); \ + for (nanddev_io_page_iter_init(nand, type, start, req, iter); \ !nanddev_io_iter_end(nand, iter); \ nanddev_io_iter_next_page(nand, iter)) From 8adf1ac24ba8ee34b8fd36ebf159004ec472fca0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:05 +0200 Subject: [PATCH 21/41] mtd: nand: Introduce a block iterator In order to be able to iterate easily across eraseblocks rather than pages, let's introduce a block iterator inspired from the page iterator. The main usage of this iterator will be for continuous/sequential reads, where it is interesting to use a single request rather than split the requests in smaller chunks (ie. pages) that can be hardly optimized. So a "continuous" boolean get's added for this purpose. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-3-miquel.raynal@bootlin.com --- include/linux/mtd/nand.h | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 92e06ac33815..1e4208040956 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -103,6 +103,8 @@ enum nand_page_io_req_type { * @ooblen: the number of OOB bytes to read from/write to this page * @oobbuf: buffer to store OOB data in or get OOB data from * @mode: one of the %MTD_OPS_XXX mode + * @continuous: no need to start over the operation at the end of each page, the + * NAND device will automatically prepare the next one * * This object is used to pass per-page I/O requests to NAND sub-layers. This * way all useful information are already formatted in a useful way and @@ -125,6 +127,7 @@ struct nand_page_io_req { void *in; } oobbuf; int mode; + bool continuous; }; const struct mtd_ooblayout_ops *nand_get_small_page_ooblayout(void); @@ -937,6 +940,43 @@ static inline void nanddev_io_page_iter_init(struct nand_device *nand, iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page - iter->req.ooboffs, iter->oobleft); + iter->req.continuous = false; +} + +/** + * nand_io_block_iter_init - Initialize a NAND I/O iterator + * @nand: NAND device + * @offs: absolute offset + * @req: MTD request + * @iter: NAND I/O iterator + * + * Initializes a NAND iterator based on the information passed by the MTD + * layer for block jumps (no OOB) + * + * In practice only reads may leverage this iterator. + */ +static inline void nanddev_io_block_iter_init(struct nand_device *nand, + enum nand_page_io_req_type reqtype, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) +{ + unsigned int offs_in_eb; + + iter->req.type = reqtype; + iter->req.mode = req->mode; + iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); + iter->req.ooboffs = 0; + iter->oobbytes_per_page = 0; + iter->dataleft = req->len; + iter->oobleft = 0; + iter->req.databuf.in = req->datbuf; + offs_in_eb = (nand->memorg.pagesize * iter->req.pos.page) + iter->req.dataoffs; + iter->req.datalen = min_t(unsigned int, + nanddev_eraseblock_size(nand) - offs_in_eb, + iter->dataleft); + iter->req.oobbuf.in = NULL; + iter->req.ooblen = 0; + iter->req.continuous = true; } /** @@ -962,6 +1002,25 @@ static inline void nanddev_io_iter_next_page(struct nand_device *nand, iter->oobleft); } +/** + * nand_io_iter_next_block - Move to the next block + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Updates the @iter to point to the next block. + * No OOB handling available. + */ +static inline void nanddev_io_iter_next_block(struct nand_device *nand, + struct nand_io_iter *iter) +{ + nanddev_pos_next_eraseblock(nand, &iter->req.pos); + iter->dataleft -= iter->req.datalen; + iter->req.databuf.in += iter->req.datalen; + iter->req.dataoffs = 0; + iter->req.datalen = min_t(unsigned int, nanddev_eraseblock_size(nand), + iter->dataleft); +} + /** * nand_io_iter_end - Should end iteration or not * @nand: NAND device @@ -997,6 +1056,21 @@ static inline bool nanddev_io_iter_end(struct nand_device *nand, !nanddev_io_iter_end(nand, iter); \ nanddev_io_iter_next_page(nand, iter)) +/** + * nand_io_for_each_block - Iterate over all NAND pages contained in an MTD I/O + * request, one block at a time + * @nand: NAND device + * @start: start address to read/write from + * @req: MTD I/O request + * @iter: NAND I/O iterator + * + * Should be used for iterating over blocks that are contained in an MTD request. + */ +#define nanddev_io_for_each_block(nand, type, start, req, iter) \ + for (nanddev_io_block_iter_init(nand, type, start, req, iter); \ + !nanddev_io_iter_end(nand, iter); \ + nanddev_io_iter_next_block(nand, iter)) + bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos); bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos); int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); From 79da17072e22a802a321ca44c9082ee2e855e72b Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:06 +0200 Subject: [PATCH 22/41] mtd: spi-nand: Isolate the MTD read logic in a helper There is currently only a single path for performing page reads as requested by the MTD layer. Soon there will be two: - a "regular" page read - a continuous page read Let's extract the page read logic in a dedicated helper, so the introduction of continuous page reads will be as easy as checking whether continuous reads shall/can be used and calling one helper or the other. There is not behavioral change intended. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-4-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 018c854d0619..1f468ed93c8e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -630,25 +630,20 @@ static int spinand_write_page(struct spinand_device *spinand, return nand_ecc_finish_io_req(nand, (struct nand_page_io_req *)req); } -static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) +static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops, + unsigned int *max_bitflips) { struct spinand_device *spinand = mtd_to_spinand(mtd); struct nand_device *nand = mtd_to_nanddev(mtd); - struct mtd_ecc_stats old_stats; - unsigned int max_bitflips = 0; struct nand_io_iter iter; bool disable_ecc = false; bool ecc_failed = false; - int ret = 0; + int ret; - if (ops->mode == MTD_OPS_RAW || !spinand->eccinfo.ooblayout) + if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout) disable_ecc = true; - mutex_lock(&spinand->lock); - - old_stats = mtd->ecc_stats; - nanddev_io_for_each_page(nand, NAND_PAGE_READ, from, ops, &iter) { if (disable_ecc) iter.req.mode = MTD_OPS_RAW; @@ -664,13 +659,33 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, if (ret == -EBADMSG) ecc_failed = true; else - max_bitflips = max_t(unsigned int, max_bitflips, ret); + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); ret = 0; ops->retlen += iter.req.datalen; ops->oobretlen += iter.req.ooblen; } + if (ecc_failed && !ret) + ret = -EBADMSG; + + return ret; +} + +static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct mtd_ecc_stats old_stats; + unsigned int max_bitflips = 0; + int ret; + + mutex_lock(&spinand->lock); + + old_stats = mtd->ecc_stats; + + ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); + if (ops->stats) { ops->stats->uncorrectable_errors += mtd->ecc_stats.failed - old_stats.failed; @@ -680,9 +695,6 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, mutex_unlock(&spinand->lock); - if (ecc_failed && !ret) - ret = -EBADMSG; - return ret ? ret : max_bitflips; } From 631cfdd0520d19b7f4fc13b834fd9c8b46c6dbac Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:07 +0200 Subject: [PATCH 23/41] mtd: spi-nand: Add continuous read support A regular page read consist in: - Asking one page of content from the NAND array to be loaded in the chip's SRAM, - Waiting for the operation to be done, - Retrieving the data (I/O phase) from the chip's SRAM. When reading several sequential pages, the above operation is repeated over and over. There is however a way to optimize these accesses, by enabling continuous reads. The feature requires the NAND chip to have a second internal SRAM area plus a bit of additional internal logic to trigger another internal transfer between the NAND array and the second SRAM area while the I/O phase is ongoing. Once the first I/O phase is done, the host can continue reading more data, continuously, as the chip will automatically switch to the second SRAM content (which has already been loaded) and in turns trigger the next load into the first SRAM area again. From an instruction perspective, the command op-codes are different, but the same cycles are required. The only difference is that after a continuous read (which is stopped by a CS deassert), the host must observe a delay of tRST. However, because there is no guarantee in Linux regarding the actual state of the CS pin after a transfer (in order to speed-up the next transfer if targeting the same device), it was necessary to manually end the continuous read with a configuration register write operation. Continuous reads have two main drawbacks: * They only work on full pages (column address ignored) * Only the main data area is pulled, out-of-band bytes are not accessible. Said otherwise, the feature can only be useful with on-die ECC engines. Performance wise, measures have been performed on a Zynq platform using Macronix SPI-NAND controller with a Macronix chip (based on the flash_speed tool modified for testing sequential reads): - 1-1-1 mode: performances improved from +3% (2-pages) up to +10% after a dozen pages. - 1-1-4 mode: performances improved from +15% (2-pages) up to +40% after a dozen pages. This series is based on a previous work from Macronix engineer Jaime Liao. Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-5-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 176 ++++++++++++++++++++++++++++++++++-- include/linux/mtd/spinand.h | 16 ++++ 2 files changed, 184 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 1f468ed93c8e..04041287b129 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -200,6 +200,12 @@ static int spinand_ecc_enable(struct spinand_device *spinand, enable ? CFG_ECC_ENABLE : 0); } +static int spinand_cont_read_enable(struct spinand_device *spinand, + bool enable) +{ + return spinand->set_cont_read(spinand, enable); +} + static int spinand_check_ecc_status(struct spinand_device *spinand, u8 status) { struct nand_device *nand = spinand_to_nand(spinand); @@ -311,10 +317,22 @@ static int spinand_ondie_ecc_finish_io_req(struct nand_device *nand, /* Finish a page read: check the status, report errors/bitflips */ ret = spinand_check_ecc_status(spinand, engine_conf->status); - if (ret == -EBADMSG) + if (ret == -EBADMSG) { mtd->ecc_stats.failed++; - else if (ret > 0) - mtd->ecc_stats.corrected += ret; + } else if (ret > 0) { + unsigned int pages; + + /* + * Continuous reads don't allow us to get the detail, + * so we may exagerate the actual number of corrected bitflips. + */ + if (!req->continuous) + pages = 1; + else + pages = req->datalen / nanddev_page_size(nand); + + mtd->ecc_stats.corrected += ret * pages; + } return ret; } @@ -369,7 +387,11 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, if (req->datalen) { buf = spinand->databuf; - nbytes = nanddev_page_size(nand); + if (!req->continuous) + nbytes = nanddev_page_size(nand); + else + nbytes = round_up(req->dataoffs + req->datalen, + nanddev_page_size(nand)); column = 0; } @@ -397,6 +419,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, nbytes -= ret; column += ret; buf += ret; + + /* + * Dirmap accesses are allowed to toggle the CS. + * Toggling the CS during a continuous read is forbidden. + */ + if (nbytes && req->continuous) + return -EIO; } if (req->datalen) @@ -672,6 +701,125 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from, return ret; } +static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops, + unsigned int *max_bitflips) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + struct nand_device *nand = mtd_to_nanddev(mtd); + struct nand_io_iter iter; + u8 status; + int ret; + + ret = spinand_cont_read_enable(spinand, true); + if (ret) + return ret; + + /* + * The cache is divided into two halves. While one half of the cache has + * the requested data, the other half is loaded with the next chunk of data. + * Therefore, the host can read out the data continuously from page to page. + * Each data read must be a multiple of 4-bytes and full pages should be read; + * otherwise, the data output might get out of sequence from one read command + * to another. + */ + nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) { + ret = spinand_select_target(spinand, iter.req.pos.target); + if (ret) + goto end_cont_read; + + ret = nand_ecc_prepare_io_req(nand, &iter.req); + if (ret) + goto end_cont_read; + + ret = spinand_load_page_op(spinand, &iter.req); + if (ret) + goto end_cont_read; + + ret = spinand_wait(spinand, SPINAND_READ_INITIAL_DELAY_US, + SPINAND_READ_POLL_DELAY_US, NULL); + if (ret < 0) + goto end_cont_read; + + ret = spinand_read_from_cache_op(spinand, &iter.req); + if (ret) + goto end_cont_read; + + ops->retlen += iter.req.datalen; + + ret = spinand_read_status(spinand, &status); + if (ret) + goto end_cont_read; + + spinand_ondie_ecc_save_status(nand, status); + + ret = nand_ecc_finish_io_req(nand, &iter.req); + if (ret < 0) + goto end_cont_read; + + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + ret = 0; + } + +end_cont_read: + /* + * Once all the data has been read out, the host can either pull CS# + * high and wait for tRST or manually clear the bit in the configuration + * register to terminate the continuous read operation. We have no + * guarantee the SPI controller drivers will effectively deassert the CS + * when we expect them to, so take the register based approach. + */ + spinand_cont_read_enable(spinand, false); + + return ret; +} + +static void spinand_cont_read_init(struct spinand_device *spinand) +{ + struct nand_device *nand = spinand_to_nand(spinand); + enum nand_ecc_engine_type engine_type = nand->ecc.ctx.conf.engine_type; + + /* OOBs cannot be retrieved so external/on-host ECC engine won't work */ + if (spinand->set_cont_read && + (engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE || + engine_type == NAND_ECC_ENGINE_TYPE_NONE)) { + spinand->cont_read_possible = true; + } +} + +static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_device *nand = mtd_to_nanddev(mtd); + struct spinand_device *spinand = nand_to_spinand(nand); + struct nand_pos start_pos, end_pos; + + if (!spinand->cont_read_possible) + return false; + + /* OOBs won't be retrieved */ + if (ops->ooblen || ops->oobbuf) + return false; + + nanddev_offs_to_pos(nand, from, &start_pos); + nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos); + + /* + * Continuous reads never cross LUN boundaries. Some devices don't + * support crossing planes boundaries. Some devices don't even support + * crossing blocks boundaries. The common case being to read through UBI, + * we will very rarely read two consequent blocks or more, so it is safer + * and easier (can be improved) to only enable continuous reads when + * reading within the same erase block. + */ + if (start_pos.target != end_pos.target || + start_pos.plane != end_pos.plane || + start_pos.eraseblock != end_pos.eraseblock) + return false; + + return start_pos.page < end_pos.page; +} + static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { @@ -684,7 +832,10 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from, old_stats = mtd->ecc_stats; - ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); + if (spinand_use_cont_read(mtd, from, ops)) + ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips); + else + ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips); if (ops->stats) { ops->stats->uncorrectable_errors += @@ -874,6 +1025,9 @@ static int spinand_create_dirmap(struct spinand_device *spinand, }; struct spi_mem_dirmap_desc *desc; + if (spinand->cont_read_possible) + info.length = nanddev_eraseblock_size(nand); + /* The plane number is passed in MSB just above the column address */ info.offset = plane << fls(nand->memorg.pagesize); @@ -1107,6 +1261,7 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->flags = table[i].flags; spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; + spinand->set_cont_read = table[i].set_cont_read; op = spinand_select_op_variant(spinand, info->op_variants.read_cache); @@ -1248,9 +1403,8 @@ static int spinand_init(struct spinand_device *spinand) * may use this buffer for DMA access. * Memory allocated by devm_ does not guarantee DMA-safe alignment. */ - spinand->databuf = kzalloc(nanddev_page_size(nand) + - nanddev_per_page_oobsize(nand), - GFP_KERNEL); + spinand->databuf = kzalloc(nanddev_eraseblock_size(nand), + GFP_KERNEL); if (!spinand->databuf) { ret = -ENOMEM; goto err_free_bufs; @@ -1279,6 +1433,12 @@ static int spinand_init(struct spinand_device *spinand) if (ret) goto err_cleanup_nanddev; + /* + * Continuous read can only be enabled with an on-die ECC engine, so the + * ECC initialization must have happened previously. + */ + spinand_cont_read_init(spinand); + mtd->_read_oob = spinand_mtd_read; mtd->_write_oob = spinand_mtd_write; mtd->_block_isbad = spinand_mtd_block_isbad; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5c19ead60499..14dce347dc46 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -336,6 +336,7 @@ struct spinand_ondie_ecc_conf { * @op_variants.update_cache: variants of the update-cache operation * @select_target: function used to select a target/die. Required only for * multi-die chips + * @set_cont_read: enable/disable continuous cached reads * * Each SPI NAND manufacturer driver should have a spinand_info table * describing all the chips supported by the driver. @@ -354,6 +355,8 @@ struct spinand_info { } op_variants; int (*select_target)(struct spinand_device *spinand, unsigned int target); + int (*set_cont_read)(struct spinand_device *spinand, + bool enable); }; #define SPINAND_ID(__method, ...) \ @@ -379,6 +382,9 @@ struct spinand_info { #define SPINAND_SELECT_TARGET(__func) \ .select_target = __func, +#define SPINAND_CONT_READ(__set_cont_read) \ + .set_cont_read = __set_cont_read, + #define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ __flags, ...) \ { \ @@ -422,6 +428,12 @@ struct spinand_dirmap { * passed in spi_mem_op be DMA-able, so we can't based the bufs on * the stack * @manufacturer: SPI NAND manufacturer information + * @cont_read_possible: Field filled by the core once the whole system + * configuration is known to tell whether continuous reads are + * suitable to use or not in general with this chip/configuration. + * A per-transfer check must of course be done to ensure it is + * actually relevant to enable this feature. + * @set_cont_read: Enable/disable the continuous read feature * @priv: manufacturer private data */ struct spinand_device { @@ -451,6 +463,10 @@ struct spinand_device { u8 *scratchbuf; const struct spinand_manufacturer *manufacturer; void *priv; + + bool cont_read_possible; + int (*set_cont_read)(struct spinand_device *spinand, + bool enable); }; /** From a06f2e7cc4de104b14971e08d37f9d08c256b053 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:08 +0200 Subject: [PATCH 24/41] mtd: spi-nand: Expose spinand_write_reg_op() This helper function will soon be used from a vendor driver, let's export it through the spinand.h header. No need for any export, as there is currently no reason for any module to need it. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-6-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/core.c | 2 +- include/linux/mtd/spinand.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 04041287b129..b5c6d755ade1 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -34,7 +34,7 @@ static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) return 0; } -static int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) +int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) { struct spi_mem_op op = SPINAND_SET_FEATURE_OP(reg, spinand->scratchbuf); diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 14dce347dc46..8dbf67ca710a 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -533,6 +533,7 @@ int spinand_match_and_init(struct spinand_device *spinand, enum spinand_readid_method rdid_method); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); #endif /* __LINUX_MTD_SPINAND_H */ From ed148d30eaf4ff55c95ae495ad9b9ffb970f7826 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:09 +0200 Subject: [PATCH 25/41] mtd: spi-nand: macronix: Fix helper name Use "macronix_" instead of "mx35lf1ge4ab_" as common prefix for the ->get_status() callback name. This callback is used by many different families, there is no variation in the implementation so far. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-7-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/macronix.c | 49 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 3f9e9c572854..7b1fb4b32340 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -49,7 +49,7 @@ static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { .free = mx35lfxge4ab_ooblayout_free, }; -static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) +static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr) { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), SPI_MEM_OP_NO_ADDR, @@ -64,8 +64,8 @@ static int mx35lf1ge4ab_get_eccsr(struct spinand_device *spinand, u8 *eccsr) return 0; } -static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int macronix_ecc_get_status(struct spinand_device *spinand, + u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 eccsr; @@ -83,7 +83,7 @@ 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, spinand->scratchbuf)) + if (macronix_get_eccsr(spinand, spinand->scratchbuf)) return nanddev_get_ecc_conf(nand)->strength; eccsr = *spinand->scratchbuf; @@ -110,7 +110,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35LF2GE4AB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), @@ -129,7 +129,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35LF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37, 0x03), NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), @@ -139,7 +139,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35LF1G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -194,7 +194,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX31UF1GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -204,7 +204,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35LF2G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20), @@ -215,7 +215,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF4G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 2, 1, 1), @@ -225,7 +225,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xf5, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -235,7 +235,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7, 0x03), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), @@ -245,7 +245,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), @@ -255,7 +255,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), @@ -265,7 +265,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe4, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -275,7 +275,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF2GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6, 0x03), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), @@ -285,7 +285,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF2GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2, 0x01), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -295,7 +295,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF1G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -305,7 +305,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF1G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -315,7 +315,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF1GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -325,7 +325,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX35UF1GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -335,8 +335,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), - + macronix_ecc_get_status)), SPINAND_INFO("MX31LF2GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2e), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -346,7 +345,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), SPINAND_INFO("MX3UF2GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xae), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -356,7 +355,7 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - mx35lf1ge4ab_ecc_get_status)), + macronix_ecc_get_status)), }; static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { From 18073e395cd6d0de1427c4248380ccdbf937375d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:10 +0200 Subject: [PATCH 26/41] mtd: spi-nand: macronix: Extract the bitflip retrieval logic With GET_STATUS commands, SPI-NAND devices can tell the status of the last read operation, in particular if there was: - no bitflips - corrected bitflips - uncorrectable bitflips The next step then to read an ECC status register and retrieve the amount of bitflips, when relevant, if possible. The logic used here works well for now, but will no longer apply to continuous reads. In order to prepare the introduction of continuous reads, let's factorize out the code that is specific to single-page reads. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-8-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/macronix.c | 37 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 7b1fb4b32340..d26e8a8c5850 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -64,12 +64,29 @@ static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr) return 0; } -static int macronix_ecc_get_status(struct spinand_device *spinand, - u8 status) +static int macronix_get_bf(struct spinand_device *spinand, u8 status) { struct nand_device *nand = spinand_to_nand(spinand); u8 eccsr; + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (macronix_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; + + return eccsr; +} + +static int macronix_ecc_get_status(struct spinand_device *spinand, + u8 status) +{ switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: return 0; @@ -78,21 +95,7 @@ static int macronix_ecc_get_status(struct spinand_device *spinand, return -EBADMSG; case STATUS_ECC_HAS_BITFLIPS: - /* - * Let's try to retrieve the real maximum number of bitflips - * in order to avoid forcing the wear-leveling layer to move - * data around if it's not necessary. - */ - if (macronix_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; - - return eccsr; - + return macronix_get_bf(spinand, status); default: break; } From e1f251e1aad8b1cfb322cbd6057300e4613534f0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:11 +0200 Subject: [PATCH 27/41] mtd: spi-nand: macronix: Add a possible bitflip status flag Macronix SPI-NANDs encode the ECC status into two bits. There are three standard situations (no bitflip, bitflips, error), and an additional possible situation which is only triggered when configuring the 0x10 configuration register, allowing to know, if there have been bitflips, whether the maximum amount of bitflips was above a configurable threshold or not. In all cases, for now, s this configuration register is unset, it means the same as "there are bitflips". This value is maybe standard, maybe not. For now, let's define it in the Macronix driver, we can safely move it to a shared place later if that is relevant. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-9-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/macronix.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index d26e8a8c5850..a4feeb030258 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -12,6 +12,8 @@ #define SPINAND_MFR_MACRONIX 0xC2 #define MACRONIX_ECCSR_MASK 0x0F +#define STATUS_ECC_HAS_BITFLIPS_THRESHOLD (3 << 4) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), @@ -95,6 +97,7 @@ static int macronix_ecc_get_status(struct spinand_device *spinand, return -EBADMSG; case STATUS_ECC_HAS_BITFLIPS: + case STATUS_ECC_HAS_BITFLIPS_THRESHOLD: return macronix_get_bf(spinand, status); default: break; From 11813857864f4326c4f23ea5b2e3f276f83af5f7 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 12:14:12 +0200 Subject: [PATCH 28/41] mtd: spi-nand: macronix: Continuous read support Enabling continuous read support implies several changes which must be done atomically in order to keep the code base consistent and bisectable. 1/ Retrieving bitflips differently Improve the helper retrieving the number of bitflips to support the case where many pages have been read instead of just one. In this case, if there is one page with bitflips, we cannot know the detail and just get the information of the maximum number of bitflips corrected in the most corrupted chunk. Compatible Macronix flashes return: - the ECC status for the last page read (bits 0-3), - the amount of bitflips for the whole read operation (bits 4-7). Hence, when reading two consecutive pages, if there was 2 bits corrected at most in one chunk, we return this amount times (arbitrary) the number read pages. It is probably a very pessimistic calculation in most cases, but still less pessimistic than if we multiplied this amount by the number of chunks. Anyway, this is just for statistics, the important data is the maximum amount of bitflips, which leads to wear leveling. 2/ Configuring, enabling and disabling the feature Create an init function for allocating a vendor structure. Use this vendor structure to cache the internal continuous read state. The state is being used to discriminate between the two bitflips retrieval methods. Finally, helpers for enabling and disabling sequential reads are also created. 3/ Fill the chips table Flag all the chips supporting the feature with the ->set_cont_read() helper. In order to validate the changes, I modified the mtd-utils test suite with extended versions of nandbiterrs, nanddump and flash_speed in order to support, test and benchmark continuous reads. I also ran all the UBI tests successfully. The nandbiterrs tool allows to track the ECC efficiency and feedback. Here is its default output (stripped): Successfully corrected 0 bit errors per subpage Read reported 1 corrected bit errors Successfully corrected 1 bit errors per subpage Read reported 2 corrected bit errors Successfully corrected 2 bit errors per subpage Read reported 3 corrected bit errors Successfully corrected 3 bit errors per subpage Read reported 4 corrected bit errors Successfully corrected 4 bit errors per subpage Read reported 5 corrected bit errors Successfully corrected 5 bit errors per subpage Read reported 6 corrected bit errors Successfully corrected 6 bit errors per subpage Read reported 7 corrected bit errors Successfully corrected 7 bit errors per subpage Read reported 8 corrected bit errors Successfully corrected 8 bit errors per subpage Failed to recover 1 bitflips Read error after 9 bit errors per page The output using the continuous option over two pages (the second page is kept intact): Successfully corrected 0 bit errors per subpage Read reported 2 corrected bit errors Successfully corrected 1 bit errors per subpage Read reported 4 corrected bit errors Successfully corrected 2 bit errors per subpage Read reported 6 corrected bit errors Successfully corrected 3 bit errors per subpage Read reported 8 corrected bit errors Successfully corrected 4 bit errors per subpage Read reported 10 corrected bit errors Successfully corrected 5 bit errors per subpage Read reported 12 corrected bit errors Successfully corrected 6 bit errors per subpage Read reported 14 corrected bit errors Successfully corrected 7 bit errors per subpage Read reported 16 corrected bit errors Successfully corrected 8 bit errors per subpage Failed to recover 1 bitflips Read error after 9 bit errors per page Regarding the throughput improvements, tests have been conducted in 1-1-1 and 1-1-4 modes, reading a full block X pages at a time, X ranging from 1 to 64 (size of a block with the tested device). The percent value on the right is the comparison of the same test conducted without the continuous read feature, ie. reading X pages in one single user request, which got naturally split by the core whit the continuous read optimization disabled into single-page reads. * 1-1-1 result: 1 page read speed is 2634 KiB/s 2 page read speed is 2704 KiB/s (+3%) 3 page read speed is 2747 KiB/s (+5%) 4 page read speed is 2804 KiB/s (+7%) 5 page read speed is 2782 KiB/s 6 page read speed is 2826 KiB/s 7 page read speed is 2834 KiB/s 8 page read speed is 2821 KiB/s 9 page read speed is 2846 KiB/s 10 page read speed is 2819 KiB/s 11 page read speed is 2871 KiB/s (+10%) 12 page read speed is 2823 KiB/s 13 page read speed is 2880 KiB/s 14 page read speed is 2842 KiB/s 15 page read speed is 2862 KiB/s 16 page read speed is 2837 KiB/s 32 page read speed is 2879 KiB/s 64 page read speed is 2842 KiB/s * 1-1-4 result: 1 page read speed is 7562 KiB/s 2 page read speed is 8904 KiB/s (+15%) 3 page read speed is 9655 KiB/s (+25%) 4 page read speed is 10118 KiB/s (+30%) 5 page read speed is 10084 KiB/s 6 page read speed is 10300 KiB/s 7 page read speed is 10434 KiB/s (+35%) 8 page read speed is 10406 KiB/s 9 page read speed is 10769 KiB/s (+40%) 10 page read speed is 10666 KiB/s 11 page read speed is 10757 KiB/s 12 page read speed is 10835 KiB/s 13 page read speed is 10976 KiB/s 14 page read speed is 11200 KiB/s 15 page read speed is 11009 KiB/s 16 page read speed is 11082 KiB/s 32 page read speed is 11352 KiB/s (+45%) 64 page read speed is 11403 KiB/s This work has received support and could be achieved thanks to Alvin Zhou . Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240826101412.20644-10-miquel.raynal@bootlin.com --- drivers/mtd/nand/spi/macronix.c | 115 ++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index a4feeb030258..9c0c72b913ed 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -5,15 +5,26 @@ * Author: Boris Brezillon */ +#include #include #include #include #define SPINAND_MFR_MACRONIX 0xC2 -#define MACRONIX_ECCSR_MASK 0x0F +#define MACRONIX_ECCSR_BF_LAST_PAGE(eccsr) FIELD_GET(GENMASK(3, 0), eccsr) +#define MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(eccsr) FIELD_GET(GENMASK(7, 4), eccsr) +#define MACRONIX_CFG_CONT_READ BIT(2) #define STATUS_ECC_HAS_BITFLIPS_THRESHOLD (3 << 4) +/* Bitflip theshold configuration register */ +#define REG_CFG_BFT 0x10 +#define CFG_BFT(x) FIELD_PREP(GENMASK(7, 4), (x)) + +struct macronix_priv { + bool cont_read; +}; + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), @@ -53,6 +64,7 @@ static const struct mtd_ooblayout_ops mx35lfxge4ab_ooblayout = { static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr) { + struct macronix_priv *priv = spinand->priv; struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x7c, 1), SPI_MEM_OP_NO_ADDR, SPI_MEM_OP_DUMMY(1, 1), @@ -62,33 +74,25 @@ static int macronix_get_eccsr(struct spinand_device *spinand, u8 *eccsr) if (ret) return ret; - *eccsr &= MACRONIX_ECCSR_MASK; - return 0; -} - -static int macronix_get_bf(struct spinand_device *spinand, u8 status) -{ - struct nand_device *nand = spinand_to_nand(spinand); - u8 eccsr; - /* - * Let's try to retrieve the real maximum number of bitflips - * in order to avoid forcing the wear-leveling layer to move - * data around if it's not necessary. + * ECCSR exposes the number of bitflips for the last read page in bits [3:0]. + * Continuous read compatible chips also expose the maximum number of + * bitflips for the whole (continuous) read operation in bits [7:4]. */ - if (macronix_get_eccsr(spinand, spinand->scratchbuf)) - return nanddev_get_ecc_conf(nand)->strength; + if (!priv->cont_read) + *eccsr = MACRONIX_ECCSR_BF_LAST_PAGE(*eccsr); + else + *eccsr = MACRONIX_ECCSR_BF_ACCUMULATED_PAGES(*eccsr); - eccsr = *spinand->scratchbuf; - if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength || !eccsr)) - return nanddev_get_ecc_conf(nand)->strength; - - return eccsr; + return 0; } static int macronix_ecc_get_status(struct spinand_device *spinand, u8 status) { + struct nand_device *nand = spinand_to_nand(spinand); + u8 eccsr; + switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: return 0; @@ -97,8 +101,19 @@ static int macronix_ecc_get_status(struct spinand_device *spinand, return -EBADMSG; case STATUS_ECC_HAS_BITFLIPS: - case STATUS_ECC_HAS_BITFLIPS_THRESHOLD: - return macronix_get_bf(spinand, status); + /* + * Let's try to retrieve the real maximum number of bitflips + * in order to avoid forcing the wear-leveling layer to move + * data around if it's not necessary. + */ + if (macronix_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; + + return eccsr; default: break; } @@ -106,6 +121,21 @@ static int macronix_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } +static int macronix_set_cont_read(struct spinand_device *spinand, bool enable) +{ + struct macronix_priv *priv = spinand->priv; + int ret; + + ret = spinand_upd_cfg(spinand, MACRONIX_CFG_CONT_READ, + enable ? MACRONIX_CFG_CONT_READ : 0); + if (ret) + return ret; + + priv->cont_read = enable; + + return 0; +} + static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO("MX35LF1GE4AB", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), @@ -135,7 +165,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35LF4GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37, 0x03), NAND_MEMORG(1, 4096, 128, 64, 2048, 40, 1, 1, 1), @@ -145,7 +176,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35LF1G24AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14, 0x03), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), @@ -251,7 +283,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35UF2G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), @@ -291,7 +324,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35UF2GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2, 0x01), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -301,7 +335,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35UF1G14AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -331,7 +366,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX35UF1GE4AC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92, 0x01), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), @@ -341,7 +377,8 @@ static const struct spinand_info macronix_spinand_table[] = { &update_cache_variants), SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, - macronix_ecc_get_status)), + macronix_ecc_get_status), + SPINAND_CONT_READ(macronix_set_cont_read)), SPINAND_INFO("MX31LF2GE4BC", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2e), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), @@ -364,7 +401,27 @@ static const struct spinand_info macronix_spinand_table[] = { macronix_ecc_get_status)), }; +static int macronix_spinand_init(struct spinand_device *spinand) +{ + struct macronix_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spinand->priv = priv; + + return 0; +} + +static void macronix_spinand_cleanup(struct spinand_device *spinand) +{ + kfree(spinand->priv); +} + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { + .init = macronix_spinand_init, + .cleanup = macronix_spinand_cleanup, }; const struct spinand_manufacturer macronix_spinand_manufacturer = { From 9ab52d9800b0d71deafb0f9a3ed4d29b69089ad2 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 17:31:58 +0200 Subject: [PATCH 29/41] mtd: rawnand: meson: Fix typo in function name There is a reason why sometime we write "NAND chip" with an 's'. It usually means several chips can be managed by the same controller. So when initializing a single chip at a time, the wording "chip" must be used, otherwise when talking about all the chips managed by the controller, we want to use "chips". Fix the function name to clarify the meson_nfc_nand_chip*s*_cleanup() helper intend. Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/linux-mtd/20240826153158.67334-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/meson_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 8806a06462ac..fbb06aa305cb 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1475,7 +1475,7 @@ meson_nfc_nand_chip_init(struct device *dev, return 0; } -static void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) +static void meson_nfc_nand_chips_cleanup(struct meson_nfc *nfc) { struct meson_nfc_nand_chip *meson_chip; struct mtd_info *mtd; @@ -1500,7 +1500,7 @@ static int meson_nfc_nand_chips_init(struct device *dev, for_each_child_of_node_scoped(np, nand_np) { ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); if (ret) { - meson_nfc_nand_chip_cleanup(nfc); + meson_nfc_nand_chips_cleanup(nfc); return ret; } } @@ -1614,7 +1614,7 @@ static void meson_nfc_remove(struct platform_device *pdev) { struct meson_nfc *nfc = platform_get_drvdata(pdev); - meson_nfc_nand_chip_cleanup(nfc); + meson_nfc_nand_chips_cleanup(nfc); meson_nfc_disable_clk(nfc); } From 9b3c395096dcfd180877776d492173419a5251d2 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Wed, 28 Aug 2024 08:37:06 +0200 Subject: [PATCH 30/41] mtd: rawnand: atmel: Add message on DMA usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like for other atmel drivers (serial, crypto, mmc, …), too. Signed-off-by: Alexander Dahl Acked-by: Nicolas Ferre Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240828063707.73869-1-ada@thorsis.com --- drivers/mtd/nand/raw/atmel/nand-controller.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index dc75d50d52e8..f9ccfd02e804 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2049,7 +2049,10 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc, dma_cap_set(DMA_MEMCPY, mask); nc->dmac = dma_request_channel(mask, NULL, NULL); - if (!nc->dmac) + if (nc->dmac) + dev_info(nc->dev, "using %s for DMA transfers\n", + dma_chan_name(nc->dmac)); + else dev_err(nc->dev, "Failed to request DMA channel\n"); } From 81cb3be3261e766a1f8efab9e3154a4f4fd9d03d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 17:30:18 +0200 Subject: [PATCH 31/41] mtd: rawnand: mtk: Factorize out the logic cleaning mtk chips There are some un-freed resources in one of the error path which would benefit from a helper going through all the registered mtk chips one by one and perform all the necessary cleanup. This is precisely what the remove path does, so let's extract the logic in a helper. There is no functional change. Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/linux-mtd/20240826153019.67106-1-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/mtk_nand.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index d65e6371675b..bf845dd16737 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1429,6 +1429,23 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, return 0; } +static void mtk_nfc_nand_chips_cleanup(struct mtk_nfc *nfc) +{ + struct mtk_nfc_nand_chip *mtk_chip; + struct nand_chip *chip; + int ret; + + while (!list_empty(&nfc->chips)) { + mtk_chip = list_first_entry(&nfc->chips, + struct mtk_nfc_nand_chip, node); + chip = &mtk_chip->nand; + ret = mtd_device_unregister(nand_to_mtd(chip)); + WARN_ON(ret); + nand_cleanup(chip); + list_del(&mtk_chip->node); + } +} + static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) { struct device_node *np = dev->of_node; @@ -1567,20 +1584,8 @@ release_ecc: static void mtk_nfc_remove(struct platform_device *pdev) { struct mtk_nfc *nfc = platform_get_drvdata(pdev); - struct mtk_nfc_nand_chip *mtk_chip; - struct nand_chip *chip; - int ret; - - while (!list_empty(&nfc->chips)) { - mtk_chip = list_first_entry(&nfc->chips, - struct mtk_nfc_nand_chip, node); - chip = &mtk_chip->nand; - ret = mtd_device_unregister(nand_to_mtd(chip)); - WARN_ON(ret); - nand_cleanup(chip); - list_del(&mtk_chip->node); - } + mtk_nfc_nand_chips_cleanup(nfc); mtk_ecc_release(nfc->ecc); } From 395999829880a106bb95f0ce34e6e4c2b43c6a5d Mon Sep 17 00:00:00 2001 From: Charles Han Date: Wed, 28 Aug 2024 17:24:27 +0800 Subject: [PATCH 32/41] mtd: powernv: Add check devm_kasprintf() returned value devm_kasprintf() can return a NULL pointer on failure but this returned value is not checked. Fixes: acfe63ec1c59 ("mtd: Convert to using %pOFn instead of device_node.name") Signed-off-by: Charles Han Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240828092427.128177-1-hanchunchao@inspur.com --- drivers/mtd/devices/powernv_flash.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c index 66044f4f5bad..10cd1d9b4885 100644 --- a/drivers/mtd/devices/powernv_flash.c +++ b/drivers/mtd/devices/powernv_flash.c @@ -207,6 +207,9 @@ static int powernv_flash_set_driver_info(struct device *dev, * get them */ mtd->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node); + if (!mtd->name) + return -ENOMEM; + mtd->type = MTD_NORFLASH; mtd->flags = MTD_WRITEABLE; mtd->size = size; From 2073ae37d550ea32e8545edaa94ef10b4fef7235 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 26 Aug 2024 17:30:19 +0200 Subject: [PATCH 33/41] mtd: rawnand: mtk: Fix init error path Reviewing a series converting the for_each_chil_of_node() loops into their _scoped variants made me realize there was no cleanup of the already registered NAND devices upon error which may leak memory on systems with more than a chip when this error occurs. We should call the _nand_chips_cleanup() function when this happens. Fixes: 1d6b1e464950 ("mtd: mediatek: driver for MTK Smart Device") Signed-off-by: Miquel Raynal Reviewed-by: Pratyush Yadav Reviewed-by: Matthias Brugger Link: https://lore.kernel.org/linux-mtd/20240826153019.67106-2-miquel.raynal@bootlin.com --- drivers/mtd/nand/raw/mtk_nand.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index bf845dd16737..586868b4139f 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -1453,8 +1453,10 @@ static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) for_each_child_of_node_scoped(np, nand_np) { ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); - if (ret) + if (ret) { + mtk_nfc_nand_chips_cleanup(nfc); return ret; + } } return 0; From d53c35931b95b707df3b6cff061ff975843837d5 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 3 Sep 2024 19:29:57 +0300 Subject: [PATCH 34/41] dt-bindings: mtd: ti, gpmc-nand: support partitions node Allow fixed-partitions to be specified through a partitions node. Fixes the below dtbs_check warning: arch/arm64/boot/dts/ti/k3-am642-evm-nand.dtb: nand@0,0: Unevaluated properties are not allowed ('partitions' was unexpected) Signed-off-by: Roger Quadros Reviewed-by: Rob Herring (Arm) Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240903-gpmc-dtb-v2-1-8046c1915b96@kernel.org --- Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml b/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml index 115682fa81b7..00540302bcae 100644 --- a/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml @@ -61,12 +61,9 @@ properties: GPIO connection to R/B signal from NAND chip maxItems: 1 -patternProperties: - "@[0-9a-f]+$": - $ref: /schemas/mtd/partitions/partition.yaml - allOf: - $ref: /schemas/memory-controllers/ti,gpmc-child.yaml + - $ref: mtd.yaml# required: - compatible From ca229bdbef29be207c8666f106acc3bf50736c05 Mon Sep 17 00:00:00 2001 From: Cheng Ming Lin Date: Mon, 9 Sep 2024 17:26:42 +0800 Subject: [PATCH 35/41] mtd: spinand: Add support for setting plane select bits Add two flags for inserting the Plane Select bit into the column address during the write_to_cache and the read_from_cache operation. Add the SPINAND_HAS_PROG_PLANE_SELECT_BIT flag for serial NAND flash that require inserting the Plane Select bit into the column address during the write_to_cache operation. Add the SPINAND_HAS_READ_PLANE_SELECT_BIT flag for serial NAND flash that require inserting the Plane Select bit into the column address during the read_from_cache operation. Signed-off-by: Cheng Ming Lin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240909092643.2434479-2-linchengming884@gmail.com --- drivers/mtd/nand/spi/core.c | 6 ++++++ include/linux/mtd/spinand.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index b5c6d755ade1..4d76f9f71a0e 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -408,6 +408,9 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand, else rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc; + if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT) + column |= req->pos.plane << fls(nanddev_page_size(nand)); + while (nbytes) { ret = spi_mem_dirmap_read(rdesc, column, nbytes, buf); if (ret < 0) @@ -489,6 +492,9 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand, else wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc; + if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT) + column |= req->pos.plane << fls(nanddev_page_size(nand)); + while (nbytes) { ret = spi_mem_dirmap_write(wdesc, column, nbytes, buf); if (ret < 0) diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 8dbf67ca710a..702e5fb13dae 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -312,6 +312,8 @@ struct spinand_ecc_info { #define SPINAND_HAS_QE_BIT BIT(0) #define SPINAND_HAS_CR_FEAT_BIT BIT(1) +#define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2) +#define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3) /** * struct spinand_ondie_ecc_conf - private SPI-NAND on-die ECC engine structure From 475aadeba5df687524bd9bc5c77cba44c1736e08 Mon Sep 17 00:00:00 2001 From: Cheng Ming Lin Date: Mon, 9 Sep 2024 17:26:43 +0800 Subject: [PATCH 36/41] mtd: spinand: macronix: Flag parts needing explicit plane select Macronix serial NAND flash with a two-plane structure requires insertion of the Plane Select bit into the column address during the write_to_cache operation. Additionally, for MX35{U,F}2G14AC and MX35LF2GE4AB, insertion of the Plane Select bit into the column address is required during the read_from_cache operation. Signed-off-by: Cheng Ming Lin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20240909092643.2434479-3-linchengming884@gmail.com --- drivers/mtd/nand/spi/macronix.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 9c0c72b913ed..d277c3220fdc 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -154,7 +154,9 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT | + SPINAND_HAS_READ_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), SPINAND_INFO("MX35LF2GE4AD", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26, 0x03), @@ -194,7 +196,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), SPINAND_INFO("MX35LF2G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x64, 0x03), @@ -212,7 +215,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), SPINAND_INFO("MX35LF4G24AD-Z4I8", SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x75, 0x03), @@ -251,7 +255,9 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT | + SPINAND_HAS_READ_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF4G24AD", @@ -261,7 +267,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF4G24AD-Z4I8", @@ -292,7 +299,9 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT | + SPINAND_HAS_READ_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G24AD", @@ -302,7 +311,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_INFO_OP_VARIANTS(&read_cache_variants, &write_cache_variants, &update_cache_variants), - SPINAND_HAS_QE_BIT, + SPINAND_HAS_QE_BIT | + SPINAND_HAS_PROG_PLANE_SELECT_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, macronix_ecc_get_status)), SPINAND_INFO("MX35UF2G24AD-Z4I8", From 18bcb4aa54eab75dce41e5c176a1c2bff94f0f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cs=C3=B3k=C3=A1s=2C=20Bence?= Date: Wed, 10 Jul 2024 11:14:01 +0200 Subject: [PATCH 37/41] mtd: spi-nor: sst: Factor out common write operation to `sst_nor_write_data()` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing to the Flash in `sst_nor_write()` is a 3-step process: first an optional one-byte write to get 2-byte-aligned, then the bulk of the data is written out in vendor-specific 2-byte writes. Finally, if there's a byte left over, another one-byte write. This was implemented 3 times in the body of `sst_nor_write()`. To reduce code duplication, factor out these sub-steps to their own function. Signed-off-by: Csókás, Bence Reviewed-by: Pratyush Yadav [pratyush@kernel.org: fixup whitespace, use %zu instead of %i in WARN()] Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20240710091401.1282824-1-csokas.bence@prolan.hu --- drivers/mtd/spi-nor/sst.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 180b7390690c..b5ad7118c49a 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -167,6 +167,21 @@ static const struct flash_info sst_nor_parts[] = { } }; +static int sst_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, + const u_char *buf) +{ + u8 op = (len == 1) ? SPINOR_OP_BP : SPINOR_OP_AAI_WP; + int ret; + + nor->program_opcode = op; + ret = spi_nor_write_data(nor, to, 1, buf); + if (ret < 0) + return ret; + WARN(ret != len, "While writing %zu byte written %i bytes\n", len, ret); + + return spi_nor_wait_till_ready(nor); +} + static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -188,16 +203,10 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, /* Start write from odd address. */ if (to % 2) { - nor->program_opcode = SPINOR_OP_BP; - /* write one byte. */ - ret = spi_nor_write_data(nor, to, 1, buf); + ret = sst_nor_write_data(nor, to, 1, buf); if (ret < 0) goto out; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; to++; actual++; @@ -205,16 +214,11 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, /* Write out most of the data here. */ for (; actual < len - 1; actual += 2) { - nor->program_opcode = SPINOR_OP_AAI_WP; - /* write two bytes. */ - ret = spi_nor_write_data(nor, to, 2, buf + actual); + ret = sst_nor_write_data(nor, to, 2, buf + actual); if (ret < 0) goto out; - WARN(ret != 2, "While writing 2 bytes written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; + to += 2; nor->sst_write_second = true; } @@ -234,14 +238,9 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, if (ret) goto out; - nor->program_opcode = SPINOR_OP_BP; - ret = spi_nor_write_data(nor, to, 1, buf + actual); + ret = sst_nor_write_data(nor, to, 1, buf + actual); if (ret < 0) goto out; - WARN(ret != 1, "While writing 1 byte written %i bytes\n", ret); - ret = spi_nor_wait_till_ready(nor); - if (ret) - goto out; actual += 1; From 86fd0e6410b453fed93cf8085de1e5b0cfdbb6b9 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 26 Jul 2024 11:58:18 -0700 Subject: [PATCH 38/41] mtd: spi-nor: micron-st: Add n25q064a WP support These flash chips are used on Google / TP-Link / ASUS OnHub devices, and OnHub devices are write-protected by default (same as any other ChromeOS/Chromebook system). I've referred to datasheets, and tested on OnHub devices. Signed-off-by: Brian Norris Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20240726185825.142733-1-computersforpeace@gmail.com --- drivers/mtd/spi-nor/micron-st.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 3c6499fdb712..e6bab2d00c92 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -436,6 +436,8 @@ static const struct flash_info st_nor_parts[] = { .id = SNOR_ID(0x20, 0xbb, 0x17), .name = "n25q064a", .size = SZ_8M, + .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | + SPI_NOR_BP3_SR_BIT6, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, }, { .id = SNOR_ID(0x20, 0xbb, 0x18), From a84d45217c8fbfab3f522dc84767f7f08ab1a85e Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 5 Aug 2024 00:15:35 +0200 Subject: [PATCH 39/41] mtd: spi-nor: winbond: add Zetta ZD25Q128C support Zetta normally uses BAh as its vendor ID. But for the ZD25Q128C they took the one from Winbond and messed up the size parameters in SFDP. Most functions seem compatible with the W25Q128, we just have to fix up the size. Link: http://www.zettadevice.com/upload/file/20150821/DS_Zetta_25Q128_RevA.pdf Link: https://www.lcsc.com/datasheet/lcsc_datasheet_2312081757_Zetta-ZD25Q128CSIGT_C19626875.pdf Signed-off-by: Michael Walle Reviewed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20240804221535.291923-1-mwalle@kernel.org --- drivers/mtd/spi-nor/winbond.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index e065e4fd42a3..9f7ce5763e71 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -17,6 +17,31 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) +static int +w25q128_post_bfpt_fixups(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + /* + * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded + * size is really wrong. It seems that they confused Mbit with MiB. + * Thus the flash is discovered as a 2MiB device. + */ + if (bfpt_header->major == SFDP_JESD216_MAJOR && + bfpt_header->minor == SFDP_JESD216_MINOR && + nor->params->size == SZ_2M && + nor->params->erase_map.regions[0].size == SZ_2M) { + nor->params->size = SZ_16M; + nor->params->erase_map.regions[0].size = SZ_16M; + } + + return 0; +} + +static const struct spi_nor_fixups w25q128_fixups = { + .post_bfpt = w25q128_post_bfpt_fixups, +}; + static int w25q256_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -108,6 +133,7 @@ static const struct flash_info winbond_nor_parts[] = { .size = SZ_16M, .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, + .fixups = &w25q128_fixups, }, { .id = SNOR_ID(0xef, 0x40, 0x19), .name = "w25q256", From 1dc6cd4f94ad5c9432dc7c7a1e1ed6c52ec856ad Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Fri, 30 Aug 2024 17:04:28 +0900 Subject: [PATCH 40/41] mtd: spi-nor: spansion: Add support for S28HS256T Infineon S28HS256T is 256Mb Octal SPI device which has same functionalities with 512Mb and 1Gb parts. Link: https://www.infineon.com/dgdl/Infineon-S28HS256T_S28HL256T_256Mb_SEMPER_Flash_Octal_interface_1_8V_3-DataSheet-v02_00-EN.pdf?fileId=8ac78c8c8fc2dd9c018fc66787aa0657 Signed-off-by: Takahiro Kuwano Reviewed-by: Michael Walle Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20240830080428.6994-1-Takahiro.Kuwano@infineon.com --- drivers/mtd/spi-nor/spansion.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 6cc237c24e07..d6c92595f6bc 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -966,6 +966,10 @@ static const struct flash_info spansion_nor_parts[] = { .name = "s28hl01gt", .mfr_flags = USE_CLPEF, .fixups = &s28hx_t_fixups, + }, { + .id = SNOR_ID(0x34, 0x5b, 0x19), + .mfr_flags = USE_CLPEF, + .fixups = &s28hx_t_fixups, }, { .id = SNOR_ID(0x34, 0x5b, 0x1a), .name = "s28hs512t", From ac5bfa968b60fba409942ab594ad98479b4e1223 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 9 Sep 2024 09:28:54 +0200 Subject: [PATCH 41/41] mtd: spi-nor: fix flash probing Fix flash probing by name. Flash entries without a name are allowed since commit 15eb8303bb42 ("mtd: spi-nor: mark the flash name as obsolete"). But it was just until recently that a flash entry without a name was actually introduced. This triggers a bug in the legacy probe by name path. Skip entries without a name to fix it. Fixes: 2095e7da8049 ("mtd: spi-nor: spansion: Add support for S28HS256T") Reported-by: Jon Hunter Closes: https://lore.kernel.org/r/66c8ebb0-1324-4ad9-9926-8d4eb7e1e63a@nvidia.com/ Tested-by: Jon Hunter Signed-off-by: Michael Walle Reviewed-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20240909072854.812206-1-mwalle@kernel.org --- drivers/mtd/spi-nor/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index e0c4efc424f4..9d6e85bf227b 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3281,7 +3281,8 @@ static const struct flash_info *spi_nor_match_name(struct spi_nor *nor, for (i = 0; i < ARRAY_SIZE(manufacturers); i++) { for (j = 0; j < manufacturers[i]->nparts; j++) { - if (!strcmp(name, manufacturers[i]->parts[j].name)) { + if (manufacturers[i]->parts[j].name && + !strcmp(name, manufacturers[i]->parts[j].name)) { nor->manufacturer = manufacturers[i]; return &manufacturers[i]->parts[j]; }