From 33ec82a6d2b119938f26e5c8040ed5d92378eb54 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 23 May 2022 18:02:05 +0400 Subject: mtd: maps: Fix refcount leak in of_flash_probe_versatile of_find_matching_node_and_match() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: b0afd44bc192 ("mtd: physmap_of: add a hook for Versatile write protection") Signed-off-by: Miaoqian Lin Reviewed-by: Linus Walleij Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220523140205.48625-1-linmq006@gmail.com --- drivers/mtd/maps/physmap-versatile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/physmap-versatile.c b/drivers/mtd/maps/physmap-versatile.c index ad7cd9cfaee0..297a50957356 100644 --- a/drivers/mtd/maps/physmap-versatile.c +++ b/drivers/mtd/maps/physmap-versatile.c @@ -207,6 +207,7 @@ int of_flash_probe_versatile(struct platform_device *pdev, versatile_flashprot = (enum versatile_flashprot)devid->data; rmap = syscon_node_to_regmap(sysnp); + of_node_put(sysnp); if (IS_ERR(rmap)) return PTR_ERR(rmap); -- cgit v1.2.3-58-ga151 From 77087a04c8fd554134bddcb8a9ff87b21f357926 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 23 May 2022 18:32:55 +0400 Subject: mtd: maps: Fix refcount leak in ap_flash_init of_find_matching_node() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: b0afd44bc192 ("mtd: physmap_of: add a hook for Versatile write protection") Signed-off-by: Miaoqian Lin Reviewed-by: Linus Walleij Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220523143255.4376-1-linmq006@gmail.com --- drivers/mtd/maps/physmap-versatile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/maps/physmap-versatile.c b/drivers/mtd/maps/physmap-versatile.c index 297a50957356..a1b8b7b25f88 100644 --- a/drivers/mtd/maps/physmap-versatile.c +++ b/drivers/mtd/maps/physmap-versatile.c @@ -93,6 +93,7 @@ static int ap_flash_init(struct platform_device *pdev) return -ENODEV; } ebi_base = of_iomap(ebi, 0); + of_node_put(ebi); if (!ebi_base) return -ENODEV; -- cgit v1.2.3-58-ga151 From 18178e03b124b0c6be17abbbca914157642f5d7a Mon Sep 17 00:00:00 2001 From: Peng Wu Date: Fri, 20 May 2022 08:44:25 +0000 Subject: mtd: rawnand: cafe: fix drivers probe/remove methods Driver should call pci_disable_device() if it returns from cafe_nand_probe() with error. Meanwhile, the driver calls pci_enable_device() in cafe_nand_probe(), but never calls pci_disable_device() during removal. Signed-off-by: Peng Wu Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220520084425.116686-1-wupeng58@huawei.com --- drivers/mtd/nand/raw/cafe_nand.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/raw/cafe_nand.c b/drivers/mtd/nand/raw/cafe_nand.c index 9dbf031716a6..af119e376352 100644 --- a/drivers/mtd/nand/raw/cafe_nand.c +++ b/drivers/mtd/nand/raw/cafe_nand.c @@ -679,8 +679,10 @@ static int cafe_nand_probe(struct pci_dev *pdev, pci_set_master(pdev); cafe = kzalloc(sizeof(*cafe), GFP_KERNEL); - if (!cafe) - return -ENOMEM; + if (!cafe) { + err = -ENOMEM; + goto out_disable_device; + } mtd = nand_to_mtd(&cafe->nand); mtd->dev.parent = &pdev->dev; @@ -801,6 +803,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, pci_iounmap(pdev, cafe->mmio); out_free_mtd: kfree(cafe); + out_disable_device: + pci_disable_device(pdev); out: return err; } @@ -822,6 +826,7 @@ static void cafe_nand_remove(struct pci_dev *pdev) pci_iounmap(pdev, cafe->mmio); dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); kfree(cafe); + pci_disable_device(pdev); } static const struct pci_device_id cafe_nand_tbl[] = { -- cgit v1.2.3-58-ga151 From ec0da06337751b18f6dee06b6526e0f0d6e80369 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 20 May 2022 18:41:40 +0200 Subject: mtd: rawnand: meson: Fix a potential double free issue When meson_nfc_nand_chip_cleanup() is called, it will call: meson_nfc_free_buffer(&meson_chip->nand); nand_cleanup(&meson_chip->nand); nand_cleanup() in turn will call nand_detach() which calls the .detach_chip() which is here meson_nand_detach_chip(). meson_nand_detach_chip() already calls meson_nfc_free_buffer(), so we could double free some memory. Fix it by removing the unneeded explicit call to meson_nfc_free_buffer(). Fixes: 8fae856c5350 ("mtd: rawnand: meson: add support for Amlogic NAND flash controller") Signed-off-by: Christophe JAILLET Acked-by: Liang Yang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/ec15c358b8063f7c50ff4cd628cf0d2e14e43f49.1653064877.git.christophe.jaillet@wanadoo.fr --- drivers/mtd/nand/raw/meson_nand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index ac3be92872d0..032180183339 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1307,7 +1307,6 @@ static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) if (ret) return ret; - meson_nfc_free_buffer(&meson_chip->nand); nand_cleanup(&meson_chip->nand); list_del(&meson_chip->node); } -- cgit v1.2.3-58-ga151 From 99c1734f318c97492083c16910cbda2583830eb1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 25 May 2022 15:59:46 -0500 Subject: dt-bindings: mtd: mxc-nand: Drop undocumented properties from example With unevaluatedProperties issues fixed, 'nand-bus-width' and 'nand-ecc-mode' are flagged as undocumented. Removing them from the example is the easiest solution to silence the warnings. Signed-off-by: Rob Herring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220525205947.2487165-1-robh@kernel.org --- Documentation/devicetree/bindings/mtd/mxc-nand.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/mxc-nand.yaml b/Documentation/devicetree/bindings/mtd/mxc-nand.yaml index 73b86f2226c7..66da1b476ab7 100644 --- a/Documentation/devicetree/bindings/mtd/mxc-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/mxc-nand.yaml @@ -37,6 +37,4 @@ examples: compatible = "fsl,imx27-nand"; reg = <0xd8000000 0x1000>; interrupts = <29>; - nand-bus-width = <8>; - nand-ecc-mode = "hw"; }; -- cgit v1.2.3-58-ga151 From fc602b4f692cb83c937b5f79628bca32b60c4402 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 4 Jun 2022 12:32:50 +0100 Subject: mtd: spinand: Add support for ATO25D1GA Add support for the ATO25D1GA SPI NAND flash. Datasheet: - https://atta.szlcsc.com/upload/public/pdf/source/20191212/C469320_04599D67B03B078044EB65FF5AEDDDE9.pdf Signed-off-by: Aidan MacDonald Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220604113250.4745-1-aidanmacdonald.0x0@gmail.com --- drivers/mtd/nand/spi/Makefile | 2 +- drivers/mtd/nand/spi/ato.c | 86 +++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/spi/core.c | 1 + include/linux/mtd/spinand.h | 1 + 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/spi/ato.c diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile index 80dabe6ff0f3..b520fe634041 100644 --- a/drivers/mtd/nand/spi/Makefile +++ b/drivers/mtd/nand/spi/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o +spinand-objs := core.o ato.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o obj-$(CONFIG_MTD_SPI_NAND) += spinand.o diff --git a/drivers/mtd/nand/spi/ato.c b/drivers/mtd/nand/spi/ato.c new file mode 100644 index 000000000000..82b377c06812 --- /dev/null +++ b/drivers/mtd/nand/spi/ato.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Aidan MacDonald + * + * Author: Aidan MacDonald + */ + +#include +#include +#include + + +#define SPINAND_MFR_ATO 0x9b + + +static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); + +static SPINAND_OP_VARIANTS(update_cache_variants, + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + + +static int ato25d1ga_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + region->offset = (16 * section) + 8; + region->length = 8; + return 0; +} + +static int ato25d1ga_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) +{ + if (section > 3) + return -ERANGE; + + if (section) { + region->offset = (16 * section); + region->length = 8; + } else { + /* first byte of section 0 is reserved for the BBM */ + region->offset = 1; + region->length = 7; + } + + return 0; +} + +static const struct mtd_ooblayout_ops ato25d1ga_ooblayout = { + .ecc = ato25d1ga_ooblayout_ecc, + .free = ato25d1ga_ooblayout_free, +}; + + +static const struct spinand_info ato_spinand_table[] = { + SPINAND_INFO("ATO25D1GA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x12), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&ato25d1ga_ooblayout, NULL)), +}; + +static const struct spinand_manufacturer_ops ato_spinand_manuf_ops = { +}; + +const struct spinand_manufacturer ato_spinand_manufacturer = { + .id = SPINAND_MFR_ATO, + .name = "ATO", + .chips = ato_spinand_table, + .nchips = ARRAY_SIZE(ato_spinand_table), + .ops = &ato_spinand_manuf_ops, +}; diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index d5b685d1605e..9d73910a7ae8 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -927,6 +927,7 @@ static const struct nand_ops spinand_ops = { }; static const struct spinand_manufacturer *spinand_manufacturers[] = { + &ato_spinand_manufacturer, &gigadevice_spinand_manufacturer, ¯onix_spinand_manufacturer, µn_spinand_manufacturer, diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5584d3bb6556..6d3392a7edc6 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -260,6 +260,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer ato_spinand_manufacturer; extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; -- cgit v1.2.3-58-ga151 From a61528d997619a518ee8c51cf0ef0513021afaff Mon Sep 17 00:00:00 2001 From: Duoming Zhou Date: Tue, 24 May 2022 12:48:41 +0800 Subject: mtd: sm_ftl: Fix deadlock caused by cancel_work_sync in sm_release There is a deadlock between sm_release and sm_cache_flush_work which is a work item. The cancel_work_sync in sm_release will not return until sm_cache_flush_work is finished. If we hold mutex_lock and use cancel_work_sync to wait the work item to finish, the work item also requires mutex_lock. As a result, the sm_release will be blocked forever. The race condition is shown below: (Thread 1) | (Thread 2) sm_release | mutex_lock(&ftl->mutex) | sm_cache_flush_work | mutex_lock(&ftl->mutex) cancel_work_sync | ... This patch moves del_timer_sync and cancel_work_sync out of mutex_lock in order to mitigate deadlock. Fixes: 7d17c02a01a1 ("mtd: Add new SmartMedia/xD FTL") Signed-off-by: Duoming Zhou Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220524044841.10517-1-duoming@zju.edu.cn --- drivers/mtd/sm_ftl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 0cff2cda1b5a..7f955fade838 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -1111,9 +1111,9 @@ static void sm_release(struct mtd_blktrans_dev *dev) { struct sm_ftl *ftl = dev->priv; - mutex_lock(&ftl->mutex); del_timer_sync(&ftl->timer); cancel_work_sync(&ftl->flush_work); + mutex_lock(&ftl->mutex); sm_cache_flush(ftl); mutex_unlock(&ftl->mutex); } -- cgit v1.2.3-58-ga151 From 9f7e62815cf3cbbcb1b8cb21649fb4dfdb3aa016 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Thu, 26 May 2022 15:06:49 +0400 Subject: mtd: partitions: Fix refcount leak in parse_redboot_of of_get_child_by_name() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: 237960880960 ("mtd: partitions: redboot: seek fis-index-block in the right node") Signed-off-by: Miaoqian Lin Reviewed-by: Linus Walleij Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220526110652.64849-1-linmq006@gmail.com --- drivers/mtd/parsers/redboot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c index feb44a573d44..a16b42a88581 100644 --- a/drivers/mtd/parsers/redboot.c +++ b/drivers/mtd/parsers/redboot.c @@ -58,6 +58,7 @@ static void parse_redboot_of(struct mtd_info *master) return; ret = of_property_read_u32(npart, "fis-index-block", &dirblock); + of_node_put(npart); if (ret) return; -- cgit v1.2.3-58-ga151 From 9b78ef0c7997052e9eaa0f7a4513d546fa17358c Mon Sep 17 00:00:00 2001 From: Mikhail Zhilkin Date: Sun, 29 May 2022 11:07:14 +0000 Subject: mtd: parsers: add support for Sercomm partitions This adds an MTD partition parser for the Sercomm partition table that is used in some Beeline, Netgear and Sercomm routers. The Sercomm partition map table contains real partition offsets, which may differ from device to device depending on the number and location of bad blocks on NAND. Original patch (proposed by NOGUCHI Hiroshi): Link: https://github.com/openwrt/openwrt/pull/1318#issuecomment-420607394 Signed-off-by: NOGUCHI Hiroshi Signed-off-by: Mikhail Zhilkin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220529110714.189732-1-csharper2005@gmail.com --- drivers/mtd/parsers/Kconfig | 9 ++ drivers/mtd/parsers/Makefile | 1 + drivers/mtd/parsers/scpart.c | 248 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 drivers/mtd/parsers/scpart.c diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig index 23763d16e4f9..b43df73927a0 100644 --- a/drivers/mtd/parsers/Kconfig +++ b/drivers/mtd/parsers/Kconfig @@ -186,3 +186,12 @@ config MTD_QCOMSMEM_PARTS help This provides support for parsing partitions from Shared Memory (SMEM) for NAND and SPI flash on Qualcomm platforms. + +config MTD_SERCOMM_PARTS + tristate "Sercomm partition table parser" + depends on MTD && RALINK + help + This provides partitions table parser for devices with Sercomm + partition map. This partition table contains real partition + offsets, which may differ from device to device depending on the + number and location of bad blocks on NAND. diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile index 2e98aa048278..2fcf0ab9e7da 100644 --- a/drivers/mtd/parsers/Makefile +++ b/drivers/mtd/parsers/Makefile @@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o +obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_QCOMSMEM_PARTS) += qcomsmempart.o diff --git a/drivers/mtd/parsers/scpart.c b/drivers/mtd/parsers/scpart.c new file mode 100644 index 000000000000..bc40e25dc105 --- /dev/null +++ b/drivers/mtd/parsers/scpart.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/mtd/scpart.c: Sercomm Partition Parser + * + * Copyright (C) 2018 NOGUCHI Hiroshi + * Copyright (C) 2022 Mikhail Zhilkin + */ + +#include +#include +#include +#include +#include + +#define MOD_NAME "scpart" + +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define pr_fmt(fmt) MOD_NAME ": " fmt + +#define ID_ALREADY_FOUND 0xffffffffUL + +#define MAP_OFFS_IN_BLK 0x800 +#define MAP_MIRROR_NUM 2 + +static const char sc_part_magic[] = { + 'S', 'C', 'F', 'L', 'M', 'A', 'P', 'O', 'K', '\0', +}; +#define PART_MAGIC_LEN sizeof(sc_part_magic) + +/* assumes that all fields are set by CPU native endian */ +struct sc_part_desc { + uint32_t part_id; + uint32_t part_offs; + uint32_t part_bytes; +}; + +static uint32_t scpart_desc_is_valid(struct sc_part_desc *pdesc) +{ + return ((pdesc->part_id != 0xffffffffUL) && + (pdesc->part_offs != 0xffffffffUL) && + (pdesc->part_bytes != 0xffffffffUL)); +} + +static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs, + struct sc_part_desc **ppdesc) +{ + int cnt = 0; + int res = 0; + int res2; + loff_t offs; + size_t retlen; + struct sc_part_desc *pdesc = NULL; + struct sc_part_desc *tmpdesc; + uint8_t *buf; + + buf = kzalloc(master->erasesize, GFP_KERNEL); + if (!buf) { + res = -ENOMEM; + goto out; + } + + res2 = mtd_read(master, partmap_offs, master->erasesize, &retlen, buf); + if (res2 || retlen != master->erasesize) { + res = -EIO; + goto free; + } + + for (offs = MAP_OFFS_IN_BLK; + offs < master->erasesize - sizeof(*tmpdesc); + offs += sizeof(*tmpdesc)) { + tmpdesc = (struct sc_part_desc *)&buf[offs]; + if (!scpart_desc_is_valid(tmpdesc)) + break; + cnt++; + } + + if (cnt > 0) { + int bytes = cnt * sizeof(*pdesc); + + pdesc = kcalloc(cnt, sizeof(*pdesc), GFP_KERNEL); + if (!pdesc) { + res = -ENOMEM; + goto free; + } + memcpy(pdesc, &(buf[MAP_OFFS_IN_BLK]), bytes); + + *ppdesc = pdesc; + res = cnt; + } + +free: + kfree(buf); + +out: + return res; +} + +static int scpart_find_partmap(struct mtd_info *master, + struct sc_part_desc **ppdesc) +{ + int magic_found = 0; + int res = 0; + int res2; + loff_t offs = 0; + size_t retlen; + uint8_t rdbuf[PART_MAGIC_LEN]; + + while ((magic_found < MAP_MIRROR_NUM) && + (offs < master->size) && + !mtd_block_isbad(master, offs)) { + res2 = mtd_read(master, offs, PART_MAGIC_LEN, &retlen, rdbuf); + if (res2 || retlen != PART_MAGIC_LEN) { + res = -EIO; + goto out; + } + if (!memcmp(rdbuf, sc_part_magic, PART_MAGIC_LEN)) { + pr_debug("Signature found at 0x%llx\n", offs); + magic_found++; + res = scpart_scan_partmap(master, offs, ppdesc); + if (res > 0) + goto out; + } + offs += master->erasesize; + } + +out: + if (res > 0) + pr_info("Valid 'SC PART MAP' (%d partitions) found at 0x%llx\n", res, offs); + else + pr_info("No valid 'SC PART MAP' was found\n"); + + return res; +} + +static int scpart_parse(struct mtd_info *master, + const struct mtd_partition **pparts, + struct mtd_part_parser_data *data) +{ + const char *partname; + int n; + int nr_scparts; + int nr_parts = 0; + int res = 0; + struct sc_part_desc *scpart_map = NULL; + struct mtd_partition *parts = NULL; + struct device_node *mtd_node; + struct device_node *ofpart_node; + struct device_node *pp; + + mtd_node = mtd_get_of_node(master); + if (!mtd_node) { + res = -ENOENT; + goto out; + } + + ofpart_node = of_get_child_by_name(mtd_node, "partitions"); + if (!ofpart_node) { + pr_info("%s: 'partitions' subnode not found on %pOF.\n", + master->name, mtd_node); + res = -ENOENT; + goto out; + } + + nr_scparts = scpart_find_partmap(master, &scpart_map); + if (nr_scparts <= 0) { + pr_info("No any partitions was found in 'SC PART MAP'.\n"); + res = -ENOENT; + goto free; + } + + parts = kcalloc(of_get_child_count(ofpart_node), sizeof(*parts), + GFP_KERNEL); + if (!parts) { + res = -ENOMEM; + goto free; + } + + for_each_child_of_node(ofpart_node, pp) { + u32 scpart_id; + + if (of_property_read_u32(pp, "sercomm,scpart-id", &scpart_id)) + continue; + + for (n = 0 ; n < nr_scparts ; n++) + if ((scpart_map[n].part_id != ID_ALREADY_FOUND) && + (scpart_id == scpart_map[n].part_id)) + break; + if (n >= nr_scparts) + /* not match */ + continue; + + /* add the partition found in OF into MTD partition array */ + parts[nr_parts].offset = scpart_map[n].part_offs; + parts[nr_parts].size = scpart_map[n].part_bytes; + parts[nr_parts].of_node = pp; + + if (!of_property_read_string(pp, "label", &partname)) + parts[nr_parts].name = partname; + if (of_property_read_bool(pp, "read-only")) + parts[nr_parts].mask_flags |= MTD_WRITEABLE; + if (of_property_read_bool(pp, "lock")) + parts[nr_parts].mask_flags |= MTD_POWERUP_LOCK; + + /* mark as 'done' */ + scpart_map[n].part_id = ID_ALREADY_FOUND; + + nr_parts++; + } + + if (nr_parts > 0) { + *pparts = parts; + res = nr_parts; + } else + pr_info("No partition in OF matches partition ID with 'SC PART MAP'.\n"); + + of_node_put(pp); + +free: + kfree(scpart_map); + if (res <= 0) + kfree(parts); + +out: + return res; +} + +static const struct of_device_id scpart_parser_of_match_table[] = { + { .compatible = "sercomm,sc-partitions" }, + {}, +}; +MODULE_DEVICE_TABLE(of, scpart_parser_of_match_table); + +static struct mtd_part_parser scpart_parser = { + .parse_fn = scpart_parse, + .name = "scpart", + .of_match_table = scpart_parser_of_match_table, +}; +module_mtd_part_parser(scpart_parser); + +/* mtd parsers will request the module by parser name */ +MODULE_ALIAS("scpart"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("NOGUCHI Hiroshi "); +MODULE_AUTHOR("Mikhail Zhilkin "); +MODULE_DESCRIPTION("Sercomm partition parser"); -- cgit v1.2.3-58-ga151 From e607879b0da18c451de5e91daf239cc2f2f8ff2d Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Sun, 5 Jun 2022 11:07:23 +0400 Subject: mtd: parsers: ofpart: Fix refcount leak in bcm4908_partitions_fw_offset of_find_node_by_path() returns a node pointer with refcount incremented, we should use of_node_put() on it when not need anymore. Add missing of_node_put() to avoid refcount leak. Fixes: bb17230c61a6 ("mtd: parsers: ofpart: support BCM4908 fixed partitions") Signed-off-by: Miaoqian Lin Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220605070726.5979-1-linmq006@gmail.com --- drivers/mtd/parsers/ofpart_bcm4908.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/parsers/ofpart_bcm4908.c b/drivers/mtd/parsers/ofpart_bcm4908.c index 0eddef4c198e..bb072a0940e4 100644 --- a/drivers/mtd/parsers/ofpart_bcm4908.c +++ b/drivers/mtd/parsers/ofpart_bcm4908.c @@ -35,12 +35,15 @@ static long long bcm4908_partitions_fw_offset(void) err = kstrtoul(s + len + 1, 0, &offset); if (err) { pr_err("failed to parse %s\n", s + len + 1); + of_node_put(root); return err; } + of_node_put(root); return offset << 10; } + of_node_put(root); return -ENOENT; } -- cgit v1.2.3-58-ga151 From a1eda864c04cf24ea1130334963c6199318f6f95 Mon Sep 17 00:00:00 2001 From: Michał Kępień Date: Mon, 16 May 2022 09:06:00 +0200 Subject: mtdchar: prevent integer overflow in a safety check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 6420ac0af95d ("mtdchar: prevent unbounded allocation in MEMWRITE ioctl") added a safety check to mtdchar_write_ioctl() which attempts to ensure that the write request sent by user space does not extend beyond the MTD device's size. However, that check contains an addition of two struct mtd_write_req fields, 'start' and 'len', both of which are u64 variables. The result of that addition can overflow, allowing the safety check to be bypassed. The arguably simplest fix - changing the data types of the relevant struct mtd_write_req fields - is not feasible as it would break user space. Fix by making mtdchar_write_ioctl() truncate the value provided by user space in the 'len' field of struct mtd_write_req, so that only the lower 32 bits of that field are used, preventing the overflow. While the 'ooblen' field of struct mtd_write_req is not currently used in any similarly flawed safety check, also truncate it to 32 bits, for consistency with the 'len' field and with other MTD routines handling OOB data. Update include/uapi/mtd/mtd-abi.h accordingly. Suggested-by: Richard Weinberger Signed-off-by: Michał Kępień Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220516070601.11428-2-kernel@kempniu.pl --- drivers/mtd/mtdchar.c | 3 +++ include/uapi/mtd/mtd-abi.h | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index d0f9c4b0285c..b2700f8467ff 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -615,6 +615,9 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, if (!usr_oob) req.ooblen = 0; + req.len &= 0xffffffff; + req.ooblen &= 0xffffffff; + if (req.start + req.len > mtd->size) return -EINVAL; diff --git a/include/uapi/mtd/mtd-abi.h b/include/uapi/mtd/mtd-abi.h index b869990c2db2..890d9e5b76d7 100644 --- a/include/uapi/mtd/mtd-abi.h +++ b/include/uapi/mtd/mtd-abi.h @@ -69,8 +69,8 @@ enum { * struct mtd_write_req - data structure for requesting a write operation * * @start: start address - * @len: length of data buffer - * @ooblen: length of OOB buffer + * @len: length of data buffer (only lower 32 bits are used) + * @ooblen: length of OOB buffer (only lower 32 bits are used) * @usr_data: user-provided data buffer * @usr_oob: user-provided OOB buffer * @mode: MTD mode (see "MTD operation modes") -- cgit v1.2.3-58-ga151 From 83208e106a8e8a859110ebb04a5e927ced911afb Mon Sep 17 00:00:00 2001 From: Michał Kępień Date: Mon, 16 May 2022 09:06:01 +0200 Subject: mtdchar: use kvmalloc() for potentially large allocations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mtdchar_write_ioctl() calls kmalloc() with the 'size' argument set to the smaller of two values: the write request's data/OOB length provided by user space and the erase block size of the MTD device. If the latter is large, kmalloc() may not be able to serve such allocation requests. Use kvmalloc() instead. Correspondingly, replace kfree() calls with kvfree() calls. Suggested-by: Richard Weinberger Signed-off-by: Michał Kępień Acked-by: Richard Weinberger Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220516070601.11428-3-kernel@kempniu.pl --- drivers/mtd/mtdchar.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b2700f8467ff..05860288a7af 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -623,16 +623,16 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, datbuf_len = min_t(size_t, req.len, mtd->erasesize); if (datbuf_len > 0) { - datbuf = kmalloc(datbuf_len, GFP_KERNEL); + datbuf = kvmalloc(datbuf_len, GFP_KERNEL); if (!datbuf) return -ENOMEM; } oobbuf_len = min_t(size_t, req.ooblen, mtd->erasesize); if (oobbuf_len > 0) { - oobbuf = kmalloc(oobbuf_len, GFP_KERNEL); + oobbuf = kvmalloc(oobbuf_len, GFP_KERNEL); if (!oobbuf) { - kfree(datbuf); + kvfree(datbuf); return -ENOMEM; } } @@ -682,8 +682,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, usr_oob += ops.oobretlen; } - kfree(datbuf); - kfree(oobbuf); + kvfree(datbuf); + kvfree(oobbuf); return ret; } -- cgit v1.2.3-58-ga151 From 0c90466a7985d39355f743e9cd2139da3e86c4d8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:45 +0200 Subject: mtd: hyperbus: Make hyperbus_unregister_device() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only thing that could theoretically fail in that function is mtd_device_unregister(). However it's not supposed to fail and when used correctly it doesn't. So wail loudly if it does anyhow. This matches how other drivers (e.g. nand/raw/nandsim.c) use mtd_device_unregister(). This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-2-u.kleine-koenig@pengutronix.de --- drivers/mtd/hyperbus/hbmc-am654.c | 6 +++--- drivers/mtd/hyperbus/hyperbus-core.c | 8 ++------ drivers/mtd/hyperbus/rpc-if.c | 5 +++-- include/linux/mtd/hyperbus.h | 4 +--- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/hyperbus/hbmc-am654.c b/drivers/mtd/hyperbus/hbmc-am654.c index a3439b791eeb..a6161ce340d4 100644 --- a/drivers/mtd/hyperbus/hbmc-am654.c +++ b/drivers/mtd/hyperbus/hbmc-am654.c @@ -233,16 +233,16 @@ static int am654_hbmc_remove(struct platform_device *pdev) { struct am654_hbmc_priv *priv = platform_get_drvdata(pdev); struct am654_hbmc_device_priv *dev_priv = priv->hbdev.priv; - int ret; - ret = hyperbus_unregister_device(&priv->hbdev); + hyperbus_unregister_device(&priv->hbdev); + if (priv->mux_ctrl) mux_control_deselect(priv->mux_ctrl); if (dev_priv->rx_chan) dma_release_channel(dev_priv->rx_chan); - return ret; + return 0; } static const struct of_device_id am654_hbmc_dt_ids[] = { diff --git a/drivers/mtd/hyperbus/hyperbus-core.c b/drivers/mtd/hyperbus/hyperbus-core.c index 2f9fc4e17d53..4d8047d43e48 100644 --- a/drivers/mtd/hyperbus/hyperbus-core.c +++ b/drivers/mtd/hyperbus/hyperbus-core.c @@ -126,16 +126,12 @@ int hyperbus_register_device(struct hyperbus_device *hbdev) } EXPORT_SYMBOL_GPL(hyperbus_register_device); -int hyperbus_unregister_device(struct hyperbus_device *hbdev) +void hyperbus_unregister_device(struct hyperbus_device *hbdev) { - int ret = 0; - if (hbdev && hbdev->mtd) { - ret = mtd_device_unregister(hbdev->mtd); + WARN_ON(mtd_device_unregister(hbdev->mtd)); map_destroy(hbdev->mtd); } - - return ret; } EXPORT_SYMBOL_GPL(hyperbus_unregister_device); diff --git a/drivers/mtd/hyperbus/rpc-if.c b/drivers/mtd/hyperbus/rpc-if.c index 6e08ec1d4f09..15a0be63ede1 100644 --- a/drivers/mtd/hyperbus/rpc-if.c +++ b/drivers/mtd/hyperbus/rpc-if.c @@ -153,11 +153,12 @@ static int rpcif_hb_probe(struct platform_device *pdev) static int rpcif_hb_remove(struct platform_device *pdev) { struct rpcif_hyperbus *hyperbus = platform_get_drvdata(pdev); - int error = hyperbus_unregister_device(&hyperbus->hbdev); + + hyperbus_unregister_device(&hyperbus->hbdev); rpcif_disable_rpm(&hyperbus->rpc); - return error; + return 0; } static struct platform_driver rpcif_platform_driver = { diff --git a/include/linux/mtd/hyperbus.h b/include/linux/mtd/hyperbus.h index 0ce612428aea..bb6b7121a542 100644 --- a/include/linux/mtd/hyperbus.h +++ b/include/linux/mtd/hyperbus.h @@ -89,9 +89,7 @@ int hyperbus_register_device(struct hyperbus_device *hbdev); /** * hyperbus_unregister_device - deregister HyperBus slave memory device * @hbdev: hyperbus_device to be unregistered - * - * Return: 0 for success, others for failure. */ -int hyperbus_unregister_device(struct hyperbus_device *hbdev); +void hyperbus_unregister_device(struct hyperbus_device *hbdev); #endif /* __LINUX_MTD_HYPERBUS_H__ */ -- cgit v1.2.3-58-ga151 From 90914b4e8e2fb630d21d430f8844f7de583a1ac3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:47 +0200 Subject: mtd: powernv_flash: Warn about failure to unregister mtd device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mtd_device_unregister() shouldn't fail. Wail loudly if it does anyhow. This matches how other drivers (e.g. nand/raw/nandsim.c) use mtd_device_unregister(). By returning 0 in the platform remove callback a generic error message by the device core is suppressed, nothing else changes. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-4-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/powernv_flash.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/powernv_flash.c b/drivers/mtd/devices/powernv_flash.c index 6950a8764815..36e060386e59 100644 --- a/drivers/mtd/devices/powernv_flash.c +++ b/drivers/mtd/devices/powernv_flash.c @@ -270,7 +270,9 @@ static int powernv_flash_release(struct platform_device *pdev) struct powernv_flash *data = dev_get_drvdata(&(pdev->dev)); /* All resources should be freed automatically */ - return mtd_device_unregister(&(data->mtd)); + WARN_ON(mtd_device_unregister(&data->mtd)); + + return 0; } static const struct of_device_id powernv_flash_match[] = { -- cgit v1.2.3-58-ga151 From 0aaa0b5f84a0908fb2de30e8fea10811b154bf56 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:49 +0200 Subject: mtd: lpddr2_nvm: Warn about failure to unregister mtd device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mtd_device_unregister() shouldn't fail. Wail loudly if it does anyhow. This matches how other drivers (e.g. nand/raw/nandsim.c) use mtd_device_unregister(). By returning 0 in the platform remove callback a generic error message by the device core is suppressed, nothing else changes. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-6-u.kleine-koenig@pengutronix.de --- drivers/mtd/lpddr/lpddr2_nvm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c index 72f5c7b30079..367e2d906de0 100644 --- a/drivers/mtd/lpddr/lpddr2_nvm.c +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -478,7 +478,9 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) */ static int lpddr2_nvm_remove(struct platform_device *pdev) { - return mtd_device_unregister(dev_get_drvdata(&pdev->dev)); + WARN_ON(mtd_device_unregister(dev_get_drvdata(&pdev->dev))); + + return 0; } /* Initialize platform_driver data structure for lpddr2_nvm */ -- cgit v1.2.3-58-ga151 From 0057568b391488a5940635cbda562ea397bf4bdd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:50 +0200 Subject: mtd: spear_smi: Don't skip cleanup after mtd_device_unregister() failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If mtd_device_unregister() fails (which it doesn't when used correctly), the resources bound by the nand chip should be freed anyhow as returning an error value doesn't prevent the device getting unbound. Instead use WARN_ON on the return value similar to how other drivers do it. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-7-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/spear_smi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 24073518587f..f6febe6662db 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -1045,7 +1045,7 @@ static int spear_smi_remove(struct platform_device *pdev) { struct spear_smi *dev; struct spear_snor_flash *flash; - int ret, i; + int i; dev = platform_get_drvdata(pdev); if (!dev) { @@ -1060,9 +1060,7 @@ static int spear_smi_remove(struct platform_device *pdev) continue; /* clean up mtd stuff */ - ret = mtd_device_unregister(&flash->mtd); - if (ret) - dev_err(&pdev->dev, "error removing mtd\n"); + WARN_ON(mtd_device_unregister(&flash->mtd)); } clk_disable_unprepare(dev->clk); -- cgit v1.2.3-58-ga151 From 279d719be39d8edb37c9178c15e167a94c7bc0a0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:51 +0200 Subject: mtd: spear_smi: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove callback is only called after probe completed successfully. In this case platform_set_drvdata() was called with a non-NULL argument and so dev is never NULL. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-8-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/spear_smi.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index f6febe6662db..f58742486d3d 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -1048,10 +1048,6 @@ static int spear_smi_remove(struct platform_device *pdev) int i; dev = platform_get_drvdata(pdev); - if (!dev) { - dev_err(&pdev->dev, "dev is null\n"); - return -ENODEV; - } /* clean up for all nor flash */ for (i = 0; i < dev->num_flashes; i++) { -- cgit v1.2.3-58-ga151 From 37e00244047c5675a7f3f1ec0358f8c53cd12b75 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:53 +0200 Subject: mtd: rawnand: omap2: Suppress error message after WARN in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returning an error value in a platform remove callback results in an error message being emitted by the platform core, but otherwise it doesn't make a difference. After the WARN splat this generic error message doesn't add any value, so return 0 unconditionally Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-10-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/omap2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index 58c32a11792e..4a9f2b6c772d 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -2278,16 +2278,14 @@ static int omap_nand_remove(struct platform_device *pdev) struct mtd_info *mtd = platform_get_drvdata(pdev); struct nand_chip *nand_chip = mtd_to_nand(mtd); struct omap_nand_info *info = mtd_to_omap(mtd); - int ret; rawnand_sw_bch_cleanup(nand_chip); if (info->dma) dma_release_channel(info->dma); - ret = mtd_device_unregister(mtd); - WARN_ON(ret); + WARN_ON(mtd_device_unregister(mtd)); nand_cleanup(nand_chip); - return ret; + return 0; } /* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */ -- cgit v1.2.3-58-ga151 From 413948cc6208c7b47dd9fc8c56cbb04c3b2381b3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:54 +0200 Subject: mtd: rawnand: tegra: Don't skip cleanup after mtd_device_unregister() failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If mtd_device_unregister() fails (which it doesn't when used correctly), the resources bound by the nand chip should be freed anyhow as returning an error value doesn't prevent the device getting unbound. Instead use WARN_ON on the return value similar to how other drivers do it. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-11-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/tegra_nand.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/tegra_nand.c b/drivers/mtd/nand/raw/tegra_nand.c index b36e5260ae27..e12f9f580a15 100644 --- a/drivers/mtd/nand/raw/tegra_nand.c +++ b/drivers/mtd/nand/raw/tegra_nand.c @@ -1223,11 +1223,8 @@ static int tegra_nand_remove(struct platform_device *pdev) struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev); struct nand_chip *chip = ctrl->chip; struct mtd_info *mtd = nand_to_mtd(chip); - int ret; - ret = mtd_device_unregister(mtd); - if (ret) - return ret; + WARN_ON(mtd_device_unregister(mtd)); nand_cleanup(chip); -- cgit v1.2.3-58-ga151 From 7beae6946255711d7914abe7c4914ff2691c4b8e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:55 +0200 Subject: mtd: rawnand: meson: Don't skip cleanup after mtd_device_unregister() failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If mtd_device_unregister() fails (which it doesn't when used correctly), the resources bound by the nand chip should be freed anyhow as returning an error value doesn't prevent the device getting unbound. Instead use WARN_ON on the return value similar to how other drivers do it. Then meson_nfc_nand_chip_cleanup() returns 0 unconditionally and can be changed to return void which allows further simplification in the remove callback. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-12-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/meson_nand.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index ac3be92872d0..748e115b0bb7 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1293,26 +1293,21 @@ meson_nfc_nand_chip_init(struct device *dev, return 0; } -static int meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) +static void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc) { struct meson_nfc_nand_chip *meson_chip; struct mtd_info *mtd; - int ret; while (!list_empty(&nfc->chips)) { meson_chip = list_first_entry(&nfc->chips, struct meson_nfc_nand_chip, node); mtd = nand_to_mtd(&meson_chip->nand); - ret = mtd_device_unregister(mtd); - if (ret) - return ret; + WARN_ON(mtd_device_unregister(mtd)); meson_nfc_free_buffer(&meson_chip->nand); nand_cleanup(&meson_chip->nand); list_del(&meson_chip->node); } - - return 0; } static int meson_nfc_nand_chips_init(struct device *dev, @@ -1445,11 +1440,8 @@ err_clk: static int meson_nfc_remove(struct platform_device *pdev) { struct meson_nfc *nfc = platform_get_drvdata(pdev); - int ret; - ret = meson_nfc_nand_chip_cleanup(nfc); - if (ret) - return ret; + meson_nfc_nand_chip_cleanup(nfc); meson_nfc_disable_clk(nfc); -- cgit v1.2.3-58-ga151 From 43adab7fd4131ccdeda7abb9bf66afcfa0915a82 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:56 +0200 Subject: mtd: rawnand: meson: Drop cleaning platform data in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver core cares for unsetting platform data (see device_unbind_cleanup()) on remove. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-13-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/meson_nand.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index 748e115b0bb7..4e6d1a6a19c7 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -1445,8 +1445,6 @@ static int meson_nfc_remove(struct platform_device *pdev) meson_nfc_disable_clk(nfc); - platform_set_drvdata(pdev, NULL); - return 0; } -- cgit v1.2.3-58-ga151 From bb8236541f496e2a32772071fdb127dce6ac1643 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:57 +0200 Subject: mtd: physmap: Don't skip cleanup after mtd_device_unregister() failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If mtd_device_unregister() fails (which it doesn't when used correctly), the resources bound by the nand chip should be freed anyhow as returning an error value doesn't prevent the device getting unbound. Instead use WARN_ON on the return value similar to how other drivers do it. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-14-u.kleine-koenig@pengutronix.de --- drivers/mtd/maps/physmap-core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 4f63b8430c71..d433df84a394 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -66,7 +66,7 @@ static int physmap_flash_remove(struct platform_device *dev) { struct physmap_flash_info *info; struct physmap_flash_data *physmap_data; - int i, err = 0; + int i; info = platform_get_drvdata(dev); if (!info) { @@ -75,9 +75,7 @@ static int physmap_flash_remove(struct platform_device *dev) } if (info->cmtd) { - err = mtd_device_unregister(info->cmtd); - if (err) - goto out; + WARN_ON(mtd_device_unregister(info->cmtd)); if (info->cmtd != info->mtds[0]) mtd_concat_destroy(info->cmtd); @@ -92,10 +90,9 @@ static int physmap_flash_remove(struct platform_device *dev) if (physmap_data && physmap_data->exit) physmap_data->exit(dev); -out: pm_runtime_put(&dev->dev); pm_runtime_disable(&dev->dev); - return err; + return 0; } static void physmap_set_vpp(struct map_info *map, int state) -- cgit v1.2.3-58-ga151 From f88c97c1332d6d760d193ce3d76765f8105b1cda Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 3 Jun 2022 23:07:58 +0200 Subject: mtd: physmap: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remove callback is only called after probe completed successfully. In this case platform_set_drvdata() was called with a non-NULL argument and so info is never NULL. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220603210758.148493-15-u.kleine-koenig@pengutronix.de --- drivers/mtd/maps/physmap-core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index d433df84a394..85eca6a192e6 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -69,10 +69,6 @@ static int physmap_flash_remove(struct platform_device *dev) int i; info = platform_get_drvdata(dev); - if (!info) { - err = -EINVAL; - goto out; - } if (info->cmtd) { WARN_ON(mtd_device_unregister(info->cmtd)); -- cgit v1.2.3-58-ga151 From 1cc82e09fc20dbb1f3aa41e9924c48e8194da754 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 7 Jun 2022 08:25:03 +0200 Subject: mtd: rawnand: atmel: Warn about failure to unregister mtd device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Linux device core doesn't intend remove callbacks to fail. If an error code is returned the device is removed anyhow. So wail loudly if the atmel specific remove callback fails and return 0 anyhow to suppress the generic (and little helpful) error message by the device core. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607062503.211345-1-u.kleine-koenig@pengutronix.de --- drivers/mtd/nand/raw/atmel/nand-controller.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 6ef14442c71a..c9ac3baf68c0 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2629,7 +2629,9 @@ static int atmel_nand_controller_remove(struct platform_device *pdev) { struct atmel_nand_controller *nc = platform_get_drvdata(pdev); - return nc->caps->ops->remove(nc); + WARN_ON(nc->caps->ops->remove(nc)); + + return 0; } static __maybe_unused int atmel_nand_controller_resume(struct device *dev) -- cgit v1.2.3-58-ga151 From 28607b426c3d050714f250d0faeb99d2e9106e90 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 7 Jun 2022 17:24:55 +0200 Subject: mtd: st_spi_fsm: Add a clk_disable_unprepare() in .probe()'s error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For all but one error path clk_disable_unprepare() is already there. Add it to the one location where it's missing. Fixes: 481815a6193b ("mtd: st_spi_fsm: Handle clk_prepare_enable/clk_disable_unprepare.") Fixes: 69d5af8d016c ("mtd: st_spi_fsm: Obtain and use EMI clock") Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607152458.232847-2-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/st_spi_fsm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index d3377b10fc0f..52a799cae402 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2115,10 +2115,12 @@ static int stfsm_probe(struct platform_device *pdev) (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); - return mtd_device_register(&fsm->mtd, NULL, 0); - + ret = mtd_device_register(&fsm->mtd, NULL, 0); + if (ret) { err_clk_unprepare: - clk_disable_unprepare(fsm->clk); + clk_disable_unprepare(fsm->clk); + } + return ret; } -- cgit v1.2.3-58-ga151 From cfa7847f150c4343903d8ff2cd219418c1768205 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 7 Jun 2022 17:24:56 +0200 Subject: mtd: st_spi_fsm: Warn about failure to unregister mtd device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mtd_device_unregister() shouldn't fail. Wail loudly if it does anyhow. This matches how other drivers (e.g. nand/raw/nandsim.c) use mtd_device_unregister(). By returning 0 in the platform remove callback a generic error message by the device core is suppressed, nothing else changes. This is a preparation for making platform remove callbacks return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607152458.232847-3-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/st_spi_fsm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 52a799cae402..a5a4b612480c 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2130,7 +2130,9 @@ static int stfsm_remove(struct platform_device *pdev) clk_disable_unprepare(fsm->clk); - return mtd_device_unregister(&fsm->mtd); + WARN_ON(mtd_device_unregister(&fsm->mtd)); + + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3-58-ga151 From cd043c613e21bb6f039057043da759471706adf5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 7 Jun 2022 17:24:57 +0200 Subject: mtd: st_spi_fsm: Disable clock only after device was unregistered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until mtd_device_unregister() returns the device is expected to be operational. So only disable the clock after the mtd is unregistered. Fixes: 1fefc8ecb834 ("mtd: st_spi_fsm: add missing clk_disable_unprepare() in stfsm_remove()") Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607152458.232847-4-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/st_spi_fsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index a5a4b612480c..9f6d4dd8bade 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2128,10 +2128,10 @@ static int stfsm_remove(struct platform_device *pdev) { struct stfsm *fsm = platform_get_drvdata(pdev); - clk_disable_unprepare(fsm->clk); - WARN_ON(mtd_device_unregister(&fsm->mtd)); + clk_disable_unprepare(fsm->clk); + return 0; } -- cgit v1.2.3-58-ga151 From 6f6536a0e309177882975222d73984d1b5ad3bdd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 7 Jun 2022 17:24:58 +0200 Subject: mtd: st_spi_fsm: Simplify error checking in .probe() a bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of ending each if branch with the same check, do it once unconditionally after the if block. Signed-off-by: Uwe Kleine-König Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220607152458.232847-5-u.kleine-koenig@pengutronix.de --- drivers/mtd/devices/st_spi_fsm.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 9f6d4dd8bade..54861d889c30 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2084,15 +2084,12 @@ static int stfsm_probe(struct platform_device *pdev) * Configure READ/WRITE/ERASE sequences according to platform and * device flags. */ - if (info->config) { + if (info->config) ret = info->config(fsm); - if (ret) - goto err_clk_unprepare; - } else { + else ret = stfsm_prepare_rwe_seqs_default(fsm); - if (ret) - goto err_clk_unprepare; - } + if (ret) + goto err_clk_unprepare; fsm->mtd.name = info->name; fsm->mtd.dev.parent = &pdev->dev; -- cgit v1.2.3-58-ga151 From 278811d5a7b2af4e737c88ab3137d3ccc0732ac1 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 17 Jun 2022 09:40:08 +0800 Subject: mtd: parsers: scpart: add missing of_node_put() in scpart_parse() of_get_child_by_name() will increase the refcount of 'ofpart_node', so add of_node_put() after using it to avoid refcount leak. Fixes: 9b78ef0c7997 ("mtd: parsers: add support for Sercomm partitions") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220617014008.851583-1-yangyingliang@huawei.com --- drivers/mtd/parsers/scpart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/parsers/scpart.c b/drivers/mtd/parsers/scpart.c index bc40e25dc105..02601bb33de4 100644 --- a/drivers/mtd/parsers/scpart.c +++ b/drivers/mtd/parsers/scpart.c @@ -219,6 +219,7 @@ static int scpart_parse(struct mtd_info *master, of_node_put(pp); free: + of_node_put(ofpart_node); kfree(scpart_map); if (res <= 0) kfree(parts); -- cgit v1.2.3-58-ga151 From b360514edb4743cbf86fc377699c75e98b1264c7 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 16 Jun 2022 02:18:33 +0200 Subject: mtd: nand: raw: qcom_nandc: reorder qcom_nand_host struct Reorder structs in nandc driver to save holes. Signed-off-by: Christian Marangi Reviewed-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-2-ansuelsmth@gmail.com --- drivers/mtd/nand/raw/qcom_nandc.c | 107 ++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 048b255faa76..cd5d2292b7b1 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -238,6 +238,9 @@ nandc_set_reg(chip, reg, \ * @bam_ce - the array of BAM command elements * @cmd_sgl - sgl for NAND BAM command pipe * @data_sgl - sgl for NAND BAM consumer/producer pipe + * @last_data_desc - last DMA desc in data channel (tx/rx). + * @last_cmd_desc - last DMA desc in command channel. + * @txn_done - completion for NAND transfer. * @bam_ce_pos - the index in bam_ce which is available for next sgl * @bam_ce_start - the index in bam_ce which marks the start position ce * for current sgl. It will be used for size calculation @@ -250,14 +253,14 @@ nandc_set_reg(chip, reg, \ * @rx_sgl_start - start index in data sgl for rx. * @wait_second_completion - wait for second DMA desc completion before making * the NAND transfer completion. - * @txn_done - completion for NAND transfer. - * @last_data_desc - last DMA desc in data channel (tx/rx). - * @last_cmd_desc - last DMA desc in command channel. */ struct bam_transaction { struct bam_cmd_element *bam_ce; struct scatterlist *cmd_sgl; struct scatterlist *data_sgl; + struct dma_async_tx_descriptor *last_data_desc; + struct dma_async_tx_descriptor *last_cmd_desc; + struct completion txn_done; u32 bam_ce_pos; u32 bam_ce_start; u32 cmd_sgl_pos; @@ -267,25 +270,23 @@ struct bam_transaction { u32 rx_sgl_pos; u32 rx_sgl_start; bool wait_second_completion; - struct completion txn_done; - struct dma_async_tx_descriptor *last_data_desc; - struct dma_async_tx_descriptor *last_cmd_desc; }; /* * This data type corresponds to the nand dma descriptor + * @dma_desc - low level DMA engine descriptor * @list - list for desc_info - * @dir - DMA transfer direction + * * @adm_sgl - sgl which will be used for single sgl dma descriptor. Only used by * ADM * @bam_sgl - sgl which will be used for dma descriptor. Only used by BAM * @sgl_cnt - number of SGL in bam_sgl. Only used by BAM - * @dma_desc - low level DMA engine descriptor + * @dir - DMA transfer direction */ struct desc_info { + struct dma_async_tx_descriptor *dma_desc; struct list_head node; - enum dma_data_direction dir; union { struct scatterlist adm_sgl; struct { @@ -293,7 +294,7 @@ struct desc_info { int sgl_cnt; }; }; - struct dma_async_tx_descriptor *dma_desc; + enum dma_data_direction dir; }; /* @@ -337,52 +338,64 @@ struct nandc_regs { /* * NAND controller data struct * - * @controller: base controller structure - * @host_list: list containing all the chips attached to the - * controller * @dev: parent device + * * @base: MMIO base - * @base_phys: physical base address of controller registers - * @base_dma: dma base address of controller registers + * * @core_clk: controller clock * @aon_clk: another controller clock * + * @regs: a contiguous chunk of memory for DMA register + * writes. contains the register values to be + * written to controller + * + * @props: properties of current NAND controller, + * initialized via DT match data + * + * @controller: base controller structure + * @host_list: list containing all the chips attached to the + * controller + * * @chan: dma channel * @cmd_crci: ADM DMA CRCI for command flow control * @data_crci: ADM DMA CRCI for data flow control + * * @desc_list: DMA descriptor list (list of desc_infos) * * @data_buffer: our local DMA buffer for page read/writes, * used when we can't use the buffer provided * by upper layers directly - * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf - * functions * @reg_read_buf: local buffer for reading back registers via DMA + * + * @base_phys: physical base address of controller registers + * @base_dma: dma base address of controller registers * @reg_read_dma: contains dma address for register read buffer - * @reg_read_pos: marker for data read in reg_read_buf * - * @regs: a contiguous chunk of memory for DMA register - * writes. contains the register values to be - * written to controller - * @cmd1/vld: some fixed controller register values - * @props: properties of current NAND controller, - * initialized via DT match data + * @buf_size/count/start: markers for chip->legacy.read_buf/write_buf + * functions * @max_cwperpage: maximum QPIC codewords required. calculated * from all connected NAND devices pagesize + * + * @reg_read_pos: marker for data read in reg_read_buf + * + * @cmd1/vld: some fixed controller register values */ struct qcom_nand_controller { - struct nand_controller controller; - struct list_head host_list; - struct device *dev; void __iomem *base; - phys_addr_t base_phys; - dma_addr_t base_dma; struct clk *core_clk; struct clk *aon_clk; + struct nandc_regs *regs; + struct bam_transaction *bam_txn; + + const struct qcom_nandc_props *props; + + struct nand_controller controller; + struct list_head host_list; + union { /* will be used only by QPIC for BAM DMA */ struct { @@ -400,22 +413,22 @@ struct qcom_nand_controller { }; struct list_head desc_list; - struct bam_transaction *bam_txn; u8 *data_buffer; + __le32 *reg_read_buf; + + phys_addr_t base_phys; + dma_addr_t base_dma; + dma_addr_t reg_read_dma; + int buf_size; int buf_count; int buf_start; unsigned int max_cwperpage; - __le32 *reg_read_buf; - dma_addr_t reg_read_dma; int reg_read_pos; - struct nandc_regs *regs; - u32 cmd1, vld; - const struct qcom_nandc_props *props; }; /* @@ -431,19 +444,21 @@ struct qcom_nand_controller { * and reserved bytes * @cw_data: the number of bytes within a codeword protected * by ECC - * @use_ecc: request the controller to use ECC for the - * upcoming read/write - * @bch_enabled: flag to tell whether BCH ECC mode is used * @ecc_bytes_hw: ECC bytes used by controller hardware for this * chip - * @status: value to be returned if NAND_CMD_STATUS command - * is executed + * * @last_command: keeps track of last command on this chip. used * for reading correct status * * @cfg0, cfg1, cfg0_raw..: NANDc register configurations needed for * ecc/non-ecc mode for the current nand flash * device + * + * @status: value to be returned if NAND_CMD_STATUS command + * is executed + * @use_ecc: request the controller to use ECC for the + * upcoming read/write + * @bch_enabled: flag to tell whether BCH ECC mode is used */ struct qcom_nand_host { struct nand_chip chip; @@ -452,12 +467,10 @@ struct qcom_nand_host { int cs; int cw_size; int cw_data; - bool use_ecc; - bool bch_enabled; int ecc_bytes_hw; int spare_bytes; int bbm_size; - u8 status; + int last_command; u32 cfg0, cfg1; @@ -466,23 +479,27 @@ struct qcom_nand_host { u32 ecc_bch_cfg; u32 clrflashstatus; u32 clrreadstatus; + + u8 status; + bool use_ecc; + bool bch_enabled; }; /* * This data type corresponds to the NAND controller properties which varies * among different NAND controllers. * @ecc_modes - ecc mode for NAND + * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset * @is_bam - whether NAND controller is using BAM * @is_qpic - whether NAND CTRL is part of qpic IP * @qpic_v2 - flag to indicate QPIC IP version 2 - * @dev_cmd_reg_start - NAND_DEV_CMD_* registers starting offset */ struct qcom_nandc_props { u32 ecc_modes; + u32 dev_cmd_reg_start; bool is_bam; bool is_qpic; bool qpic_v2; - u32 dev_cmd_reg_start; }; /* Frees the BAM transaction memory */ -- cgit v1.2.3-58-ga151 From 862bdedd7f4b8aebf00fdb422062e64896e97809 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 16 Jun 2022 02:18:34 +0200 Subject: mtd: nand: raw: qcom_nandc: add support for unprotected spare data pages IPQ8064 nand have special pages where a different layout scheme is used. These special page are used by boot partition and on reading them lots of warning are reported about wrong ECC data and if written to results in broken data and not bootable device. The layout scheme used by these special page consist in using 512 bytes as the codeword size (even for the last codeword) while writing to CFG0 register. This forces the NAND controller to unprotect the 4 bytes of spare data. Since the kernel is unaware of this different layout for these special page, it does try to protect the spare data too during read/write and warn about CRC errors. Add support for this by permitting the user to declare these special pages in dts by declaring offset and size of the partition. The driver internally will convert these value to nand pages. On user read/write the page is checked and if it's a boot page the correct layout is used. Signed-off-by: Christian Marangi Reviewed-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-3-ansuelsmth@gmail.com --- drivers/mtd/nand/raw/qcom_nandc.c | 199 +++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index cd5d2292b7b1..8f80019a9f01 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -80,8 +80,10 @@ #define DISABLE_STATUS_AFTER_WRITE 4 #define CW_PER_PAGE 6 #define UD_SIZE_BYTES 9 +#define UD_SIZE_BYTES_MASK GENMASK(18, 9) #define ECC_PARITY_SIZE_BYTES_RS 19 #define SPARE_SIZE_BYTES 23 +#define SPARE_SIZE_BYTES_MASK GENMASK(26, 23) #define NUM_ADDR_CYCLES 27 #define STATUS_BFR_READ 30 #define SET_RD_MODE_AFTER_STATUS 31 @@ -102,6 +104,7 @@ #define ECC_MODE 4 #define ECC_PARITY_SIZE_BYTES_BCH 8 #define ECC_NUM_DATA_BYTES 16 +#define ECC_NUM_DATA_BYTES_MASK GENMASK(25, 16) #define ECC_FORCE_CLK_OPEN 30 /* NAND_DEV_CMD1 bits */ @@ -431,13 +434,32 @@ struct qcom_nand_controller { u32 cmd1, vld; }; +/* + * NAND special boot partitions + * + * @page_offset: offset of the partition where spare data is not protected + * by ECC (value in pages) + * @page_offset: size of the partition where spare data is not protected + * by ECC (value in pages) + */ +struct qcom_nand_boot_partition { + u32 page_offset; + u32 page_size; +}; + /* * NAND chip structure * + * @boot_partitions: array of boot partitions where offset and size of the + * boot partitions are stored + * * @chip: base NAND chip structure * @node: list node to add itself to host_list in * qcom_nand_controller * + * @nr_boot_partitions: count of the boot partitions where spare data is not + * protected by ECC + * * @cs: chip select value for this chip * @cw_size: the number of bytes in a single step/codeword * of a page, consisting of all data, ecc, spare @@ -456,14 +478,20 @@ struct qcom_nand_controller { * * @status: value to be returned if NAND_CMD_STATUS command * is executed + * @codeword_fixup: keep track of the current layout used by + * the driver for read/write operation. * @use_ecc: request the controller to use ECC for the * upcoming read/write * @bch_enabled: flag to tell whether BCH ECC mode is used */ struct qcom_nand_host { + struct qcom_nand_boot_partition *boot_partitions; + struct nand_chip chip; struct list_head node; + int nr_boot_partitions; + int cs; int cw_size; int cw_data; @@ -481,6 +509,7 @@ struct qcom_nand_host { u32 clrreadstatus; u8 status; + bool codeword_fixup; bool use_ecc; bool bch_enabled; }; @@ -493,6 +522,7 @@ struct qcom_nand_host { * @is_bam - whether NAND controller is using BAM * @is_qpic - whether NAND CTRL is part of qpic IP * @qpic_v2 - flag to indicate QPIC IP version 2 + * @use_codeword_fixup - whether NAND has different layout for boot partitions */ struct qcom_nandc_props { u32 ecc_modes; @@ -500,6 +530,7 @@ struct qcom_nandc_props { bool is_bam; bool is_qpic; bool qpic_v2; + bool use_codeword_fixup; }; /* Frees the BAM transaction memory */ @@ -1718,7 +1749,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; - if (qcom_nandc_is_last_cw(ecc, cw)) { + if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) { data_size2 = ecc->size - data_size1 - ((ecc->steps - 1) * 4); oob_size2 = (ecc->steps * 4) + host->ecc_bytes_hw + @@ -1799,7 +1830,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, } for_each_set_bit(cw, &uncorrectable_cws, ecc->steps) { - if (qcom_nandc_is_last_cw(ecc, cw)) { + if (qcom_nandc_is_last_cw(ecc, cw) && !host->codeword_fixup) { data_size = ecc->size - ((ecc->steps - 1) * 4); oob_size = (ecc->steps * 4) + host->ecc_bytes_hw; } else { @@ -1957,7 +1988,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; - if (qcom_nandc_is_last_cw(ecc, i)) { + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + host->spare_bytes; @@ -2054,6 +2085,69 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) return ret; } +static bool qcom_nandc_is_boot_partition(struct qcom_nand_host *host, int page) +{ + struct qcom_nand_boot_partition *boot_partition; + u32 start, end; + int i; + + /* + * Since the frequent access will be to the non-boot partitions like rootfs, + * optimize the page check by: + * + * 1. Checking if the page lies after the last boot partition. + * 2. Checking from the boot partition end. + */ + + /* First check the last boot partition */ + boot_partition = &host->boot_partitions[host->nr_boot_partitions - 1]; + start = boot_partition->page_offset; + end = start + boot_partition->page_size; + + /* Page is after the last boot partition end. This is NOT a boot partition */ + if (page > end) + return false; + + /* Actually check if it's a boot partition */ + if (page < end && page >= start) + return true; + + /* Check the other boot partitions starting from the second-last partition */ + for (i = host->nr_boot_partitions - 2; i >= 0; i--) { + boot_partition = &host->boot_partitions[i]; + start = boot_partition->page_offset; + end = start + boot_partition->page_size; + + if (page < end && page >= start) + return true; + } + + return false; +} + +static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page) +{ + bool codeword_fixup = qcom_nandc_is_boot_partition(host, page); + + /* Skip conf write if we are already in the correct mode */ + if (codeword_fixup == host->codeword_fixup) + return; + + host->codeword_fixup = codeword_fixup; + + host->cw_data = codeword_fixup ? 512 : 516; + host->spare_bytes = host->cw_size - host->ecc_bytes_hw - + host->bbm_size - host->cw_data; + + host->cfg0 &= ~(SPARE_SIZE_BYTES_MASK | UD_SIZE_BYTES_MASK); + host->cfg0 |= host->spare_bytes << SPARE_SIZE_BYTES | + host->cw_data << UD_SIZE_BYTES; + + host->ecc_bch_cfg &= ~ECC_NUM_DATA_BYTES_MASK; + host->ecc_bch_cfg |= host->cw_data << ECC_NUM_DATA_BYTES; + host->ecc_buf_cfg = (host->cw_data - 1) << NUM_STEPS; +} + /* implements ecc->read_page() */ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, int oob_required, int page) @@ -2062,6 +2156,9 @@ static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); u8 *data_buf, *oob_buf = NULL; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + nand_read_page_op(chip, page, 0, NULL, 0); data_buf = buf; oob_buf = oob_required ? chip->oob_poi : NULL; @@ -2081,6 +2178,9 @@ static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf, int cw, ret; u8 *data_buf = buf, *oob_buf = chip->oob_poi; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + for (cw = 0; cw < ecc->steps; cw++) { ret = qcom_nandc_read_cw_raw(mtd, chip, data_buf, oob_buf, page, cw); @@ -2101,6 +2201,9 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -2121,6 +2224,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, u8 *data_buf, *oob_buf; int i, ret; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); clear_read_regs(nandc); @@ -2136,7 +2242,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, for (i = 0; i < ecc->steps; i++) { int data_size, oob_size; - if (qcom_nandc_is_last_cw(ecc, i)) { + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { data_size = ecc->size - ((ecc->steps - 1) << 2); oob_size = (ecc->steps << 2) + host->ecc_bytes_hw + host->spare_bytes; @@ -2193,6 +2299,9 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, u8 *data_buf, *oob_buf; int i, ret; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + nand_prog_page_begin_op(chip, page, 0, NULL, 0); clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -2211,7 +2320,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1); oob_size1 = host->bbm_size; - if (qcom_nandc_is_last_cw(ecc, i)) { + if (qcom_nandc_is_last_cw(ecc, i) && !host->codeword_fixup) { data_size2 = ecc->size - data_size1 - ((ecc->steps - 1) << 2); oob_size2 = (ecc->steps << 2) + host->ecc_bytes_hw + @@ -2271,6 +2380,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) int data_size, oob_size; int ret; + if (host->nr_boot_partitions) + qcom_nandc_codeword_fixup(host, page); + host->use_ecc = true; clear_bam_transaction(nandc); @@ -2932,6 +3044,74 @@ static int qcom_nandc_setup(struct qcom_nand_controller *nandc) static const char * const probes[] = { "cmdlinepart", "ofpart", "qcomsmem", NULL }; +static int qcom_nand_host_parse_boot_partitions(struct qcom_nand_controller *nandc, + struct qcom_nand_host *host, + struct device_node *dn) +{ + struct nand_chip *chip = &host->chip; + struct mtd_info *mtd = nand_to_mtd(chip); + struct qcom_nand_boot_partition *boot_partition; + struct device *dev = nandc->dev; + int partitions_count, i, j, ret; + + if (!of_find_property(dn, "qcom,boot-partitions", NULL)) + return 0; + + partitions_count = of_property_count_u32_elems(dn, "qcom,boot-partitions"); + if (partitions_count <= 0) { + dev_err(dev, "Error parsing boot partition\n"); + return partitions_count ? partitions_count : -EINVAL; + } + + host->nr_boot_partitions = partitions_count / 2; + host->boot_partitions = devm_kcalloc(dev, host->nr_boot_partitions, + sizeof(*host->boot_partitions), GFP_KERNEL); + if (!host->boot_partitions) { + host->nr_boot_partitions = 0; + return -ENOMEM; + } + + for (i = 0, j = 0; i < host->nr_boot_partitions; i++, j += 2) { + boot_partition = &host->boot_partitions[i]; + + ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j, + &boot_partition->page_offset); + if (ret) { + dev_err(dev, "Error parsing boot partition offset at index %d\n", i); + host->nr_boot_partitions = 0; + return ret; + } + + if (boot_partition->page_offset % mtd->writesize) { + dev_err(dev, "Boot partition offset not multiple of writesize at index %i\n", + i); + host->nr_boot_partitions = 0; + return -EINVAL; + } + /* Convert offset to nand pages */ + boot_partition->page_offset /= mtd->writesize; + + ret = of_property_read_u32_index(dn, "qcom,boot-partitions", j + 1, + &boot_partition->page_size); + if (ret) { + dev_err(dev, "Error parsing boot partition size at index %d\n", i); + host->nr_boot_partitions = 0; + return ret; + } + + if (boot_partition->page_size % mtd->writesize) { + dev_err(dev, "Boot partition size not multiple of writesize at index %i\n", + i); + host->nr_boot_partitions = 0; + return -EINVAL; + } + /* Convert size to nand pages */ + boot_partition->page_size /= mtd->writesize; + } + + return 0; +} + static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, struct qcom_nand_host *host, struct device_node *dn) @@ -2989,6 +3169,14 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, if (ret) nand_cleanup(chip); + if (nandc->props->use_codeword_fixup) { + ret = qcom_nand_host_parse_boot_partitions(nandc, host, dn); + if (ret) { + nand_cleanup(chip); + return ret; + } + } + return ret; } @@ -3154,6 +3342,7 @@ static int qcom_nandc_remove(struct platform_device *pdev) static const struct qcom_nandc_props ipq806x_nandc_props = { .ecc_modes = (ECC_RS_4BIT | ECC_BCH_8BIT), .is_bam = false, + .use_codeword_fixup = true, .dev_cmd_reg_start = 0x0, }; -- cgit v1.2.3-58-ga151 From 5278cc93a97f7b7b9e69632e52503e956153d944 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 16 Jun 2022 02:18:35 +0200 Subject: dt-bindings: mtd: qcom_nandc: document qcom,boot-partitions binding Document new qcom,boot-partition binding used to apply special read/write layout to boot partitions. QCOM apply a special layout where spare data is not protected by ECC for some special pages (used for boot partition). Add Documentation on how to declare these special pages. Signed-off-by: Christian Marangi Reviewed-by: Rob Herring Reviewed-by: Manivannan Sadhasivam Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220616001835.24393-4-ansuelsmth@gmail.com --- .../devicetree/bindings/mtd/qcom,nandc.yaml | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml b/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml index 84ad7ff30121..482a2c068740 100644 --- a/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml +++ b/Documentation/devicetree/bindings/mtd/qcom,nandc.yaml @@ -102,6 +102,31 @@ allOf: - const: rx - const: cmd + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq806x-nand + + then: + properties: + qcom,boot-partitions: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: offset + - description: size + description: + Boot partition use a different layout where the 4 bytes of spare + data are not protected by ECC. Use this to declare these special + partitions by defining first the offset and then the size. + + It's in the form of + and should be declared in ascending order. + + Refer to the ipq8064 example on how to use this special binding. + required: - compatible - reg @@ -135,6 +160,8 @@ examples: nand-ecc-strength = <4>; nand-bus-width = <8>; + qcom,boot-partitions = <0x0 0x58a0000>; + partitions { compatible = "fixed-partitions"; #address-cells = <1>; -- cgit v1.2.3-58-ga151 From 431eae20a093fe3a47cf617b01b911ef39ce3a83 Mon Sep 17 00:00:00 2001 From: Jiang Jian Date: Thu, 23 Jun 2022 00:05:11 +0800 Subject: mtd: rawnand: sm_common: drop unexpected word 'is' in the comments there is an unexpected word 'is' in the comments that need to be dropped file: drivers/mtd/nand/raw/sm_common.c line: 55 /* NOTE: This layout is is not compatabable with SmartMedia, */ changed to: /* NOTE: This layout is not compatabable with SmartMedia, */ Signed-off-by: Jiang Jian Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220622160511.11679-1-jiangjian@cdjrlc.com --- drivers/mtd/nand/raw/sm_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/sm_common.c b/drivers/mtd/nand/raw/sm_common.c index ba24cb36d0b9..b2b42dd1a2de 100644 --- a/drivers/mtd/nand/raw/sm_common.c +++ b/drivers/mtd/nand/raw/sm_common.c @@ -52,7 +52,7 @@ static const struct mtd_ooblayout_ops oob_sm_ops = { .free = oob_sm_ooblayout_free, }; -/* NOTE: This layout is is not compatabable with SmartMedia, */ +/* NOTE: This layout is not compatabable with SmartMedia, */ /* because the 256 byte devices have page depenent oob layout */ /* However it does preserve the bad block markers */ /* If you use smftl, it will bypass this and work correctly */ -- cgit v1.2.3-58-ga151 From c223a38d62e57aa60a890ea7247e3c58a54478e6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 17 Jun 2022 11:26:51 +0200 Subject: mtd: hyperbus: rpc-if: Fix RPM imbalance in probe error path If rpcif_hw_init() fails, Runtime PM is left enabled. Fixes: b04cc0d912eb80d3 ("memory: renesas-rpc-if: Add support for RZ/G2L") Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Reviewed-by: Lad Prabhakar Reviewed-by: Sergey Shtylyov Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/f3070e1af480cb252ae183d479a593dbbf947685.1655457790.git.geert+renesas@glider.be --- drivers/mtd/hyperbus/rpc-if.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/hyperbus/rpc-if.c b/drivers/mtd/hyperbus/rpc-if.c index 15a0be63ede1..d00d30243403 100644 --- a/drivers/mtd/hyperbus/rpc-if.c +++ b/drivers/mtd/hyperbus/rpc-if.c @@ -134,7 +134,7 @@ static int rpcif_hb_probe(struct platform_device *pdev) error = rpcif_hw_init(&hyperbus->rpc, true); if (error) - return error; + goto out_disable_rpm; hyperbus->hbdev.map.size = hyperbus->rpc.size; hyperbus->hbdev.map.virt = hyperbus->rpc.dirmap; @@ -145,8 +145,12 @@ static int rpcif_hb_probe(struct platform_device *pdev) hyperbus->hbdev.np = of_get_next_child(pdev->dev.parent->of_node, NULL); error = hyperbus_register_device(&hyperbus->hbdev); if (error) - rpcif_disable_rpm(&hyperbus->rpc); + goto out_disable_rpm; + + return 0; +out_disable_rpm: + rpcif_disable_rpm(&hyperbus->rpc); return error; } -- cgit v1.2.3-58-ga151 From ac4f83482afbfd927d0fe118151b747cf175e724 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Jun 2022 16:23:13 +0100 Subject: mtd: dataflash: Add SPI ID table Currently autoloading for SPI devices does not use the DT ID table, it uses SPI modalises. Supporting OF modalises is going to be difficult if not impractical, an attempt was made but has been reverted, so ensure that module autoloading works for this driver by adding an id_table listing the SPI IDs for everything. Fixes: 96c8395e2166 ("spi: Revert modalias changes") Signed-off-by: Mark Brown Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220620152313.708768-1-broonie@kernel.org --- drivers/mtd/devices/mtd_dataflash.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 134e27328597..25bad4318305 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -112,6 +112,13 @@ static const struct of_device_id dataflash_dt_ids[] = { MODULE_DEVICE_TABLE(of, dataflash_dt_ids); #endif +static const struct spi_device_id dataflash_spi_ids[] = { + { .name = "at45", }, + { .name = "dataflash", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, dataflash_spi_ids); + /* ......................................................................... */ /* @@ -936,6 +943,7 @@ static struct spi_driver dataflash_driver = { .probe = dataflash_probe, .remove = dataflash_remove, + .id_table = dataflash_spi_ids, /* FIXME: investigate suspend and resume... */ }; -- cgit v1.2.3-58-ga151 From 77d4ac6d38487c2ab2688335b2a904469f5b16a4 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 18 Apr 2022 13:26:50 +0200 Subject: mtd: spi-nor: move SECT_4K_PMC special handling The SECT_4K_PMC flag will set a device specific opcode for the 4k sector erase. Instead of handling it in the core, we can move it to a late_init(). In that late init, loop over all erase types, look for the 4k size and replace the opcode. Signed-off-by: Michael Walle Signed-off-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220418112650.2791459-1-michael@walle.cc --- drivers/mtd/spi-nor/core.c | 7 +------ drivers/mtd/spi-nor/core.h | 2 -- drivers/mtd/spi-nor/issi.c | 23 +++++++++++++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 502967c76c5f..ce5d69317d46 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2382,12 +2382,7 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) */ erase_mask = 0; i = 0; - if (no_sfdp_flags & SECT_4K_PMC) { - erase_mask |= BIT(i); - spi_nor_set_erase_type(&map->erase_type[i], 4096u, - SPINOR_OP_BE_4K_PMC); - i++; - } else if (no_sfdp_flags & SECT_4K) { + if (no_sfdp_flags & SECT_4K) { erase_mask |= BIT(i); spi_nor_set_erase_type(&map->erase_type[i], 4096u, SPINOR_OP_BE_4K); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 3f841ec36e56..61886868cd02 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -457,7 +457,6 @@ struct spi_nor_fixups { * flags are used together with the SPI_NOR_SKIP_SFDP flag. * SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables. * SECT_4K: SPINOR_OP_BE_4K works uniformly. - * SECT_4K_PMC: SPINOR_OP_BE_4K_PMC works uniformly. * SPI_NOR_DUAL_READ: flash supports Dual Read. * SPI_NOR_QUAD_READ: flash supports Quad Read. * SPI_NOR_OCTAL_READ: flash supports Octal Read. @@ -505,7 +504,6 @@ struct flash_info { u8 no_sfdp_flags; #define SPI_NOR_SKIP_SFDP BIT(0) #define SECT_4K BIT(1) -#define SECT_4K_PMC BIT(2) #define SPI_NOR_DUAL_READ BIT(3) #define SPI_NOR_QUAD_READ BIT(4) #define SPI_NOR_OCTAL_READ BIT(5) diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index c012bc2486e1..3c7d51d2b050 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -29,6 +29,21 @@ static const struct spi_nor_fixups is25lp256_fixups = { .post_bfpt = is25lp256_post_bfpt_fixups, }; +static void pm25lv_nor_late_init(struct spi_nor *nor) +{ + struct spi_nor_erase_map *map = &nor->params->erase_map; + int i; + + /* The PM25LV series has a different 4k sector erase opcode */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) + if (map->erase_type[i].size == 4096) + map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC; +} + +static const struct spi_nor_fixups pm25lv_nor_fixups = { + .late_init = pm25lv_nor_late_init, +}; + static const struct flash_info issi_nor_parts[] = { /* ISSI */ { "is25cd512", INFO(0x7f9d20, 0, 32 * 1024, 2) @@ -62,9 +77,13 @@ static const struct flash_info issi_nor_parts[] = { /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2) - NO_SFDP_FLAGS(SECT_4K_PMC) }, + NO_SFDP_FLAGS(SECT_4K) + .fixups = &pm25lv_nor_fixups + }, { "pm25lv010", INFO(0, 0, 32 * 1024, 4) - NO_SFDP_FLAGS(SECT_4K_PMC) }, + NO_SFDP_FLAGS(SECT_4K) + .fixups = &pm25lv_nor_fixups + }, { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64) NO_SFDP_FLAGS(SECT_4K) }, }; -- cgit v1.2.3-58-ga151 From 118f3fbe517f49e17877f34fd677f7374970d92e Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 22 Jun 2022 03:06:26 +0200 Subject: dt-bindings: mtd: partitions: support label/name only partition Document new partition nodes that declare only the label/name instead of the reg used to provide an OF node for partition registred at runtime by parsers. This is required for nvmem system to declare and detect nvmem-cells. With these special partitions, the reg / offset is not required and a 'partition-' prefix is needed. The node name with the 'partition-' prefix stripped, is used to match the partition allocated by the parser at runtime and the parser will provide reg and offset of the mtd. If the partition to match contains invalid char for a node name, the label binding can be used to declare the partition name. NVMEM will use the data from the parser and provide the NVMEM cells declared in the DTS, "connecting" the dynamic partition with a static declaration of cells in them. Signed-off-by: Christian Marangi Reviewed-by: Rob Herring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220622010628.30414-2-ansuelsmth@gmail.com --- .../bindings/mtd/partitions/partition.yaml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mtd/partitions/partition.yaml b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml index e1ac08064425..f1a02d840b12 100644 --- a/Documentation/devicetree/bindings/mtd/partitions/partition.yaml +++ b/Documentation/devicetree/bindings/mtd/partitions/partition.yaml @@ -11,6 +11,17 @@ description: | relative offset and size specified. Depending on partition function extra properties can be used. + A partition may be dynamically allocated by a specific parser at runtime. + In this specific case, a specific suffix is required to the node name. + Everything after 'partition-' will be used as the partition name to compare + with the one dynamically allocated by the specific parser. + If the partition contains invalid char a label can be provided that will + be used instead of the node name to make the comparison. + This is used to assign an OF node to the dynamiccally allocated partition + so that subsystem like NVMEM can provide an OF node and declare NVMEM cells. + The OF node will be assigned only if the partition label declared match the + one assigned by the parser at runtime. + maintainers: - Rafał Miłecki @@ -41,7 +52,12 @@ properties: immune to paired-pages corruptions type: boolean -required: - - reg +if: + not: + required: [ reg ] +then: + properties: + $nodename: + pattern: '^partition-.*$' additionalProperties: true -- cgit v1.2.3-58-ga151 From dd638202dfb6507cd529751b6c39680c292e245e Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 22 Jun 2022 03:06:27 +0200 Subject: dt-bindings: mtd: partitions: add additional example for qcom,smem-part Add additional example for qcom,smem-part to declare a dynamic partition to provide NVMEM cells for the commonly ART partition provided by this parser. Signed-off-by: Christian Marangi Reviewed-by: Rob Herring Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220622010628.30414-3-ansuelsmth@gmail.com --- .../bindings/mtd/partitions/qcom,smem-part.yaml | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/devicetree/bindings/mtd/partitions/qcom,smem-part.yaml b/Documentation/devicetree/bindings/mtd/partitions/qcom,smem-part.yaml index cf3f8c1e035d..dc07909af023 100644 --- a/Documentation/devicetree/bindings/mtd/partitions/qcom,smem-part.yaml +++ b/Documentation/devicetree/bindings/mtd/partitions/qcom,smem-part.yaml @@ -19,6 +19,10 @@ properties: compatible: const: qcom,smem-part +patternProperties: + "^partition-[0-9a-z]+$": + $ref: partition.yaml# + required: - compatible @@ -31,3 +35,26 @@ examples: compatible = "qcom,smem-part"; }; }; + + - | + /* Example declaring dynamic partition */ + flash { + partitions { + compatible = "qcom,smem-part"; + + partition-art { + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + label = "0:art"; + + macaddr_art_0: macaddr@0 { + reg = <0x0 0x6>; + }; + + macaddr_art_6: macaddr@6 { + reg = <0x6 0x6>; + }; + }; + }; + }; -- cgit v1.2.3-58-ga151 From ad9b10d1eaada169bd764abcab58f08538877e26 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Wed, 22 Jun 2022 03:06:28 +0200 Subject: mtd: core: introduce of support for dynamic partitions We have many parser that register mtd partitions at runtime. One example is the cmdlinepart or the smem-part parser where the compatible is defined in the dts and the partitions gets detected and registered by the parser. This is problematic for the NVMEM subsystem that requires an OF node to detect NVMEM cells. To fix this problem, introduce an additional logic that will try to assign an OF node to the MTD if declared. On MTD addition, it will be checked if the MTD has an OF node and if not declared will check if a partition with the same label / node name is declared in DTS. If an exact match is found, the partition dynamically allocated by the parser will have a connected OF node. The NVMEM subsystem will detect the OF node and register any NVMEM cells declared statically in the DTS. Signed-off-by: Christian Marangi Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220622010628.30414-4-ansuelsmth@gmail.com --- drivers/mtd/mtdcore.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 9eb0680db312..6fafea80fd98 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -546,6 +546,66 @@ static int mtd_nvmem_add(struct mtd_info *mtd) return 0; } +static void mtd_check_of_node(struct mtd_info *mtd) +{ + struct device_node *partitions, *parent_dn, *mtd_dn = NULL; + const char *pname, *prefix = "partition-"; + int plen, mtd_name_len, offset, prefix_len; + struct mtd_info *parent; + bool found = false; + + /* Check if MTD already has a device node */ + if (dev_of_node(&mtd->dev)) + return; + + /* Check if a partitions node exist */ + parent = mtd->parent; + parent_dn = dev_of_node(&parent->dev); + if (!parent_dn) + return; + + partitions = of_get_child_by_name(parent_dn, "partitions"); + if (!partitions) + goto exit_parent; + + prefix_len = strlen(prefix); + mtd_name_len = strlen(mtd->name); + + /* Search if a partition is defined with the same name */ + for_each_child_of_node(partitions, mtd_dn) { + offset = 0; + + /* Skip partition with no/wrong prefix */ + if (!of_node_name_prefix(mtd_dn, "partition-")) + continue; + + /* Label have priority. Check that first */ + if (of_property_read_string(mtd_dn, "label", &pname)) { + of_property_read_string(mtd_dn, "name", &pname); + offset = prefix_len; + } + + plen = strlen(pname) - offset; + if (plen == mtd_name_len && + !strncmp(mtd->name, pname + offset, plen)) { + found = true; + break; + } + } + + if (!found) + goto exit_partitions; + + /* Set of_node only for nvmem */ + if (of_device_is_compatible(mtd_dn, "nvmem-cells")) + mtd_set_of_node(mtd, mtd_dn); + +exit_partitions: + of_node_put(partitions); +exit_parent: + of_node_put(parent_dn); +} + /** * add_mtd_device - register an MTD device * @mtd: pointer to new MTD device info structure @@ -658,6 +718,7 @@ int add_mtd_device(struct mtd_info *mtd) mtd->dev.devt = MTD_DEVT(i); dev_set_name(&mtd->dev, "mtd%d", i); dev_set_drvdata(&mtd->dev, mtd); + mtd_check_of_node(mtd); of_node_get(mtd_get_of_node(mtd)); error = device_register(&mtd->dev); if (error) -- cgit v1.2.3-58-ga151 From 7499bfeedb47efc1ee4dc793b92c610d46e6d6a6 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra Date: Tue, 28 Jun 2022 21:18:23 +0530 Subject: mtd: rawnand: arasan: Update NAND bus clock instead of system clock In current implementation the Arasan NAND driver is updating the system clock(i.e., anand->clk) in accordance to the timing modes (i.e., SDR or NVDDR). But as per the Arasan NAND controller spec the flash clock or the NAND bus clock(i.e., nfc->bus_clk), need to be updated instead. This patch keeps the system clock unchanged and updates the NAND bus clock as per the timing modes. Fixes: 197b88fecc50 ("mtd: rawnand: arasan: Add new Arasan NAND controller") CC: stable@vger.kernel.org # 5.8+ Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220628154824.12222-2-amit.kumar-mahapatra@xilinx.com --- drivers/mtd/nand/raw/arasan-nand-controller.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index 53bd10738418..c5264fa223c4 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -347,17 +347,17 @@ static int anfc_select_target(struct nand_chip *chip, int target) /* Update clock frequency */ if (nfc->cur_clk != anand->clk) { - clk_disable_unprepare(nfc->controller_clk); - ret = clk_set_rate(nfc->controller_clk, anand->clk); + clk_disable_unprepare(nfc->bus_clk); + ret = clk_set_rate(nfc->bus_clk, anand->clk); if (ret) { dev_err(nfc->dev, "Failed to change clock rate\n"); return ret; } - ret = clk_prepare_enable(nfc->controller_clk); + ret = clk_prepare_enable(nfc->bus_clk); if (ret) { dev_err(nfc->dev, - "Failed to re-enable the controller clock\n"); + "Failed to re-enable the bus clock\n"); return ret; } -- cgit v1.2.3-58-ga151 From e16eceea863b417fd328588b1be1a79de0bc937f Mon Sep 17 00:00:00 2001 From: Olga Kitaina Date: Tue, 28 Jun 2022 21:18:24 +0530 Subject: mtd: rawnand: arasan: Fix clock rate in NV-DDR According to the Arasan NAND controller spec, the flash clock rate for SDR must be <= 100 MHz, while for NV-DDR it must be the same as the rate of the CLK line for the mode. The driver previously always set 100 MHz for NV-DDR, which would result in incorrect behavior for NV-DDR modes 0-4. The appropriate clock rate can be calculated from the NV-DDR timing parameters as 1/tCK, or for rates measured in picoseconds, 10^12 / nand_nvddr_timings->tCK_min. Fixes: 197b88fecc50 ("mtd: rawnand: arasan: Add new Arasan NAND controller") CC: stable@vger.kernel.org # 5.8+ Signed-off-by: Olga Kitaina Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20220628154824.12222-3-amit.kumar-mahapatra@xilinx.com --- drivers/mtd/nand/raw/arasan-nand-controller.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index c5264fa223c4..296fb16c8dc3 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -1043,7 +1043,13 @@ static int anfc_setup_interface(struct nand_chip *chip, int target, DQS_BUFF_SEL_OUT(dqs_mode); } - anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK; + if (nand_interface_is_sdr(conf)) { + anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK; + } else { + /* ONFI timings are defined in picoseconds */ + anand->clk = div_u64((u64)NSEC_PER_SEC * 1000, + conf->timings.nvddr.tCK_min); + } /* * Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work -- cgit v1.2.3-58-ga151 From 92714596cdbe2ec1da739b674716633076916336 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Mon, 18 Jul 2022 20:42:43 +0530 Subject: MAINTAINERS: Use my kernel.org email Use the kernel.org email I have for reviewing patches. Signed-off-by: Pratyush Yadav Signed-off-by: Tudor Ambarus Link: https://lore.kernel.org/r/20220718151243.1149442-1-p.yadav@ti.com --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index fe5daf141501..282b34eae750 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18827,7 +18827,7 @@ F: drivers/pinctrl/spear/ SPI NOR SUBSYSTEM M: Tudor Ambarus -M: Pratyush Yadav +M: Pratyush Yadav R: Michael Walle L: linux-mtd@lists.infradead.org S: Maintained -- cgit v1.2.3-58-ga151 From 90c517f435a9e469cceb7b1985ba92011c2e26aa Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 6 May 2022 13:51:58 +0300 Subject: mtd: spi-nor: micron-st: Skip FSR reading if SPI controller does not support it The Intel SPI controller does not support low level operations, like reading the flag status register (FSR). It only exposes a set of high level operations for software to use. For this reason check the return value of micron_st_nor_read_fsr() and if the operation was not supported, use the status register value only. This allows the chip to work even when attached to Intel SPI controller (there are such systems out there). Signed-off-by: Mika Westerberg Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220506105158.43613-1-mika.westerberg@linux.intel.com --- drivers/mtd/spi-nor/micron-st.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index a96f74e0f568..3c9681a3f7a3 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -399,8 +399,16 @@ static int micron_st_nor_ready(struct spi_nor *nor) return sr_ready; ret = micron_st_nor_read_fsr(nor, nor->bouncebuf); - if (ret) - return ret; + if (ret) { + /* + * Some controllers, such as Intel SPI, do not support low + * level operations such as reading the flag status + * register. They only expose small amount of high level + * operations to the software. If this is the case we use + * only the status register value. + */ + return ret == -EOPNOTSUPP ? sr_ready : ret; + } if (nor->bouncebuf[0] & (FSR_E_ERR | FSR_P_ERR)) { if (nor->bouncebuf[0] & FSR_E_ERR) -- cgit v1.2.3-58-ga151 From 41e4f15f02af67fbcd6fec243ef52627f51760b4 Mon Sep 17 00:00:00 2001 From: Sungbo Eo Date: Fri, 23 Jul 2021 23:12:32 +0900 Subject: mtd: spi-nor: esmt: Use correct name of f25l32qa The flash ID of F25L32QA is 0x8c4016, whereas that of F25L32QA(2S) is 0x8c4116. F25L32QA(2S) is the newer version of F25L32QA and its BPn bits are non-volatile, unlike its older version. Signed-off-by: Sungbo Eo Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20210723141232.15659-1-mans0n@gorani.run Datasheet: https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F25L32QA.pdf Datasheet: https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F25L32QA_1(2S).pdf --- drivers/mtd/spi-nor/esmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/esmt.c b/drivers/mtd/spi-nor/esmt.c index 79e2408f4998..fcc3b0e7cda9 100644 --- a/drivers/mtd/spi-nor/esmt.c +++ b/drivers/mtd/spi-nor/esmt.c @@ -13,7 +13,7 @@ static const struct flash_info esmt_nor_parts[] = { { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) NO_SFDP_FLAGS(SECT_4K) }, - { "f25l32qa", INFO(0x8c4116, 0, 64 * 1024, 64) + { "f25l32qa-2s", INFO(0x8c4116, 0, 64 * 1024, 64) FLAGS(SPI_NOR_HAS_LOCK) NO_SFDP_FLAGS(SECT_4K) }, { "f25l64qa", INFO(0x8c4117, 0, 64 * 1024, 128) -- cgit v1.2.3-58-ga151 From c452d49849d48bd37ae97fc2bc92c6435707c35f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:24:59 +0300 Subject: mtd: spi-nor: s/addr_width/addr_nbytes Address width was an unfortunate name, as it means the number of IO lines used for the address, whereas in the code it is used as the number of address bytes. s/addr_width/addr_nbytes throughout the entire SPI NOR framework. Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Acked-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-2-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/controllers/hisi-sfc.c | 2 +- drivers/mtd/spi-nor/controllers/nxp-spifi.c | 8 ++--- drivers/mtd/spi-nor/core.c | 54 ++++++++++++++--------------- drivers/mtd/spi-nor/core.h | 12 +++---- drivers/mtd/spi-nor/debugfs.c | 2 +- drivers/mtd/spi-nor/issi.c | 8 ++--- drivers/mtd/spi-nor/otp.c | 12 +++---- drivers/mtd/spi-nor/sfdp.c | 32 ++++++++--------- drivers/mtd/spi-nor/xilinx.c | 2 +- include/linux/mtd/spi-nor.h | 4 +-- 10 files changed, 68 insertions(+), 68 deletions(-) diff --git a/drivers/mtd/spi-nor/controllers/hisi-sfc.c b/drivers/mtd/spi-nor/controllers/hisi-sfc.c index 94a969185ceb..5070d72835ec 100644 --- a/drivers/mtd/spi-nor/controllers/hisi-sfc.c +++ b/drivers/mtd/spi-nor/controllers/hisi-sfc.c @@ -237,7 +237,7 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, reg = readl(host->regbase + FMC_CFG); reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK); reg |= FMC_CFG_OP_MODE_NORMAL; - reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES + reg |= (nor->addr_nbytes == 4) ? SPI_NOR_ADDR_MODE_4BYTES : SPI_NOR_ADDR_MODE_3BYTES; writel(reg, host->regbase + FMC_CFG); diff --git a/drivers/mtd/spi-nor/controllers/nxp-spifi.c b/drivers/mtd/spi-nor/controllers/nxp-spifi.c index 9032b9ab2eaf..ab3990e6ac25 100644 --- a/drivers/mtd/spi-nor/controllers/nxp-spifi.c +++ b/drivers/mtd/spi-nor/controllers/nxp-spifi.c @@ -203,7 +203,7 @@ static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len, SPIFI_CMD_DATALEN(len) | SPIFI_CMD_FIELDFORM_ALL_SERIAL | SPIFI_CMD_OPCODE(nor->program_opcode) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); writel(cmd, spifi->io_base + SPIFI_CMD); for (i = 0; i < len; i++) @@ -230,7 +230,7 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs) cmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL | SPIFI_CMD_OPCODE(nor->erase_opcode) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); writel(cmd, spifi->io_base + SPIFI_CMD); return nxp_spifi_wait_for_cmd(spifi); @@ -252,12 +252,12 @@ static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi) } /* Memory mode supports address length between 1 and 4 */ - if (spifi->nor.addr_width < 1 || spifi->nor.addr_width > 4) + if (spifi->nor.addr_nbytes < 1 || spifi->nor.addr_nbytes > 4) return -EINVAL; spifi->mcmd |= SPIFI_CMD_OPCODE(spifi->nor.read_opcode) | SPIFI_CMD_INTLEN(spifi->nor.read_dummy / 8) | - SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1); + SPIFI_CMD_FRAMEFORM(spifi->nor.addr_nbytes + 1); return 0; } diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ce5d69317d46..31604188ee59 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -38,7 +38,7 @@ */ #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) -#define SPI_NOR_MAX_ADDR_WIDTH 4 +#define SPI_NOR_MAX_ADDR_NBYTES 4 #define SPI_NOR_SRST_SLEEP_MIN 200 #define SPI_NOR_SRST_SLEEP_MAX 400 @@ -198,7 +198,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, from, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, from, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DATA_IN(len, buf, 0)); bool usebouncebuf; @@ -262,7 +262,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, { struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, to, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, to, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 0)); ssize_t nbytes; @@ -1113,7 +1113,7 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) if (nor->spimem) { struct spi_mem_op op = SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, - nor->addr_width, addr); + nor->addr_nbytes, addr); spi_nor_spimem_setup_op(nor, &op, nor->write_proto); @@ -1126,13 +1126,13 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) * Default implementation, if driver doesn't have a specialized HW * control */ - for (i = nor->addr_width - 1; i >= 0; i--) { + for (i = nor->addr_nbytes - 1; i >= 0; i--) { nor->bouncebuf[i] = addr & 0xff; addr >>= 8; } return spi_nor_controller_ops_write_reg(nor, nor->erase_opcode, - nor->bouncebuf, nor->addr_width); + nor->bouncebuf, nor->addr_nbytes); } /** @@ -2249,43 +2249,43 @@ static int spi_nor_default_setup(struct spi_nor *nor, return 0; } -static int spi_nor_set_addr_width(struct spi_nor *nor) +static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { - if (nor->addr_width) { + if (nor->addr_nbytes) { /* already configured from SFDP */ } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { /* * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So - * in this protocol an odd address width cannot be used because + * in this protocol an odd addr_nbytes cannot be used because * then the address phase would only span a cycle and a half. * Half a cycle would be left over. We would then have to start * the dummy phase in the middle of a cycle and so too the data * phase, and we will end the transaction with half a cycle left * over. * - * Force all 8D-8D-8D flashes to use an address width of 4 to + * Force all 8D-8D-8D flashes to use an addr_nbytes of 4 to * avoid this situation. */ - nor->addr_width = 4; - } else if (nor->info->addr_width) { - nor->addr_width = nor->info->addr_width; + nor->addr_nbytes = 4; + } else if (nor->info->addr_nbytes) { + nor->addr_nbytes = nor->info->addr_nbytes; } else { - nor->addr_width = 3; + nor->addr_nbytes = 3; } - if (nor->addr_width == 3 && nor->params->size > 0x1000000) { + if (nor->addr_nbytes == 3 && nor->params->size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ - nor->addr_width = 4; + nor->addr_nbytes = 4; } - if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { - dev_dbg(nor->dev, "address width is too large: %u\n", - nor->addr_width); + if (nor->addr_nbytes > SPI_NOR_MAX_ADDR_NBYTES) { + dev_dbg(nor->dev, "The number of address bytes is too large: %u\n", + nor->addr_nbytes); return -EINVAL; } /* Set 4byte opcodes when possible. */ - if (nor->addr_width == 4 && nor->flags & SNOR_F_4B_OPCODES && + if (nor->addr_nbytes == 4 && nor->flags & SNOR_F_4B_OPCODES && !(nor->flags & SNOR_F_HAS_4BAIT)) spi_nor_set_4byte_opcodes(nor); @@ -2304,7 +2304,7 @@ static int spi_nor_setup(struct spi_nor *nor, if (ret) return ret; - return spi_nor_set_addr_width(nor); + return spi_nor_set_addr_nbytes(nor); } /** @@ -2492,7 +2492,7 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) if (spi_nor_parse_sfdp(nor)) { memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->addr_width = 0; + nor->addr_nbytes = 0; nor->flags &= ~SNOR_F_4B_OPCODES; } } @@ -2713,7 +2713,7 @@ static int spi_nor_init(struct spi_nor *nor) nor->flags & SNOR_F_SWP_IS_VOLATILE)) spi_nor_try_unlock_all(nor); - if (nor->addr_width == 4 && + if (nor->addr_nbytes == 4 && nor->read_proto != SNOR_PROTO_8_8_8_DTR && !(nor->flags & SNOR_F_4B_OPCODES)) { /* @@ -2840,7 +2840,7 @@ static void spi_nor_put_device(struct mtd_info *mtd) void spi_nor_restore(struct spi_nor *nor) { /* restore the addressing mode */ - if (nor->addr_width == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && + if (nor->addr_nbytes == 4 && !(nor->flags & SNOR_F_4B_OPCODES) && nor->flags & SNOR_F_BROKEN_RESET) nor->params->set_4byte_addr_mode(nor, false); @@ -2984,7 +2984,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, * - select op codes for (Fast) Read, Page Program and Sector Erase. * - set the number of dummy cycles (mode cycles + wait states). * - set the SPI protocols for register and memory accesses. - * - set the address width. + * - set the number of address bytes. */ ret = spi_nor_setup(nor, hwcaps); if (ret) @@ -3025,7 +3025,7 @@ static int spi_nor_create_read_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), SPI_MEM_OP_DUMMY(nor->read_dummy, 0), SPI_MEM_OP_DATA_IN(0, NULL, 0)), .offset = 0, @@ -3056,7 +3056,7 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor) { struct spi_mem_dirmap_info info = { .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0), - SPI_MEM_OP_ADDR(nor->addr_width, 0, 0), + SPI_MEM_OP_ADDR(nor->addr_nbytes, 0, 0), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(0, NULL, 0)), .offset = 0, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 61886868cd02..14921a507b0f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -84,9 +84,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_width, addr) \ +#define SPI_NOR_SECTOR_ERASE_OP(opcode, addr_nbytes, addr) \ SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ - SPI_MEM_OP_ADDR(addr_width, addr, 0), \ + SPI_MEM_OP_ADDR(addr_nbytes, addr, 0), \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) @@ -429,7 +429,7 @@ struct spi_nor_fixups { * isn't necessarily called a "sector" by the vendor. * @n_sectors: the number of sectors. * @page_size: the flash's page size. - * @addr_width: the flash's address width. + * @addr_nbytes: number of address bytes to send. * * @parse_sfdp: true when flash supports SFDP tables. The false value has no * meaning. If one wants to skip the SFDP tables, one should @@ -487,7 +487,7 @@ struct flash_info { unsigned sector_size; u16 n_sectors; u16 page_size; - u16 addr_width; + u16 addr_nbytes; bool parse_sfdp; u16 flags; @@ -548,11 +548,11 @@ struct flash_info { .n_sectors = (_n_sectors), \ .page_size = 256, \ -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_nbytes) \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ - .addr_width = (_addr_width), \ + .addr_nbytes = (_addr_nbytes), \ .flags = SPI_NOR_NO_ERASE | SPI_NOR_NO_FR, \ #define OTP_INFO(_len, _n_regions, _base, _offset) \ diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index eaf84f7a0676..df76cb5de3f9 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -86,7 +86,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) seq_printf(s, "size\t\t%s\n", buf); seq_printf(s, "write size\t%u\n", params->writesize); seq_printf(s, "page size\t%u\n", params->page_size); - seq_printf(s, "address width\t%u\n", nor->addr_width); + seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes); seq_puts(s, "flags\t\t"); spi_nor_print_flags(s, nor->flags, snor_f_names, sizeof(snor_f_names)); diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 3c7d51d2b050..71687e5babdc 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -14,13 +14,13 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_bfpt *bfpt) { /* - * IS25LP256 supports 4B opcodes, but the BFPT advertises a - * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY address width. - * Overwrite the address width advertised by the BFPT. + * IS25LP256 supports 4B opcodes, but the BFPT advertises + * BFPT_DWORD1_ADDRESS_BYTES_3_ONLY. + * Overwrite the number of address bytes advertised by the BFPT. */ if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) - nor->addr_width = 4; + nor->addr_nbytes = 4; return 0; } diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index fa63d8571218..00ab0d2d6d2f 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -35,13 +35,13 @@ */ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) { - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; struct spi_mem_dirmap_desc *rdesc; enum spi_nor_protocol read_proto; int ret; read_opcode = nor->read_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; read_proto = nor->read_proto; rdesc = nor->dirmap.rdesc; @@ -54,7 +54,7 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) ret = spi_nor_read_data(nor, addr, len, buf); nor->read_opcode = read_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; nor->read_proto = read_proto; nor->dirmap.rdesc = rdesc; @@ -85,11 +85,11 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, { enum spi_nor_protocol write_proto; struct spi_mem_dirmap_desc *wdesc; - u8 addr_width, program_opcode; + u8 addr_nbytes, program_opcode; int ret, written; program_opcode = nor->program_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; write_proto = nor->write_proto; wdesc = nor->dirmap.wdesc; @@ -113,7 +113,7 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, out: nor->program_opcode = program_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->write_proto = write_proto; nor->dirmap.wdesc = wdesc; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index a5211543d30d..61ae8c8c5237 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -134,7 +134,7 @@ struct sfdp_4bait { /** * spi_nor_read_raw() - raw read of serial flash memory. read_opcode, - * addr_width and read_dummy members of the struct spi_nor + * addr_nbytes and read_dummy members of the struct spi_nor * should be previously * set. * @nor: pointer to a 'struct spi_nor' @@ -178,21 +178,21 @@ static int spi_nor_read_raw(struct spi_nor *nor, u32 addr, size_t len, u8 *buf) static int spi_nor_read_sfdp(struct spi_nor *nor, u32 addr, size_t len, void *buf) { - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; int ret; read_opcode = nor->read_opcode; - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; nor->read_opcode = SPINOR_OP_RDSFDP; - nor->addr_width = 3; + nor->addr_nbytes = 3; nor->read_dummy = 8; ret = spi_nor_read_raw(nor, addr, len, buf); nor->read_opcode = read_opcode; - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; return ret; @@ -462,11 +462,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: - nor->addr_width = 3; + nor->addr_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: - nor->addr_width = 4; + nor->addr_nbytes = 4; break; default: @@ -637,12 +637,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, } /** - * spi_nor_smpt_addr_width() - return the address width used in the + * spi_nor_smpt_addr_nbytes() - return the number of address bytes used in the * configuration detection command. * @nor: pointer to a 'struct spi_nor' * @settings: configuration detection command descriptor, dword1 */ -static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) +static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings) { switch (settings & SMPT_CMD_ADDRESS_LEN_MASK) { case SMPT_CMD_ADDRESS_LEN_0: @@ -653,7 +653,7 @@ static u8 spi_nor_smpt_addr_width(const struct spi_nor *nor, const u32 settings) return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->addr_width; + return nor->addr_nbytes; } } @@ -690,7 +690,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, u32 addr; int err; u8 i; - u8 addr_width, read_opcode, read_dummy; + u8 addr_nbytes, read_opcode, read_dummy; u8 read_data_mask, map_id; /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ @@ -698,7 +698,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, if (!buf) return ERR_PTR(-ENOMEM); - addr_width = nor->addr_width; + addr_nbytes = nor->addr_nbytes; read_dummy = nor->read_dummy; read_opcode = nor->read_opcode; @@ -709,7 +709,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, break; read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); - nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); + nor->addr_nbytes = spi_nor_smpt_addr_nbytes(nor, smpt[i]); nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); addr = smpt[i + 1]; @@ -756,7 +756,7 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, /* fall through */ out: kfree(buf); - nor->addr_width = addr_width; + nor->addr_nbytes = addr_nbytes; nor->read_dummy = read_dummy; nor->read_opcode = read_opcode; return ret; @@ -1044,7 +1044,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, /* * We need at least one 4-byte op code per read, program and erase * operation; the .read(), .write() and .erase() hooks share the - * nor->addr_width value. + * nor->addr_nbytes value. */ if (!read_hwcaps || !pp_hwcaps || !erase_mask) goto out; @@ -1098,7 +1098,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * Spansion memory. However this quirk is no longer needed with new * SFDP compliant memories. */ - nor->addr_width = 4; + nor->addr_nbytes = 4; nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index 1d2f5db047bd..5723157739fc 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -31,7 +31,7 @@ .sector_size = (8 * (_page_size)), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ - .addr_width = 3, \ + .addr_nbytes = 3, \ .flags = SPI_NOR_NO_FR /* Xilinx S3AN share MFR with Atmel SPI NOR */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 1ede4c89805a..42218a1164f6 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -351,7 +351,7 @@ struct spi_nor_flash_parameter; * @bouncebuf_size: size of the bounce buffer * @info: SPI NOR part JEDEC MFR ID and other info * @manufacturer: SPI NOR manufacturer - * @addr_width: number of address bytes + * @addr_nbytes: number of address bytes * @erase_opcode: the opcode for erasing a sector * @read_opcode: the read opcode * @read_dummy: the dummy needed by the read operation @@ -381,7 +381,7 @@ struct spi_nor { size_t bouncebuf_size; const struct flash_info *info; const struct spi_nor_manufacturer *manufacturer; - u8 addr_width; + u8 addr_nbytes; u8 erase_opcode; u8 read_opcode; u8 read_dummy; -- cgit v1.2.3-58-ga151 From aa5d980a144cd0bf717eb16609c29ff276f8bd47 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:00 +0300 Subject: mtd: spi-nor: core: Shrink the storage size of the flash_info's addr_nbytes The maximum number of address bytes in SPI NOR is 4. Shrink the storage size of the flash_info's addr_nbytes. Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-3-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 14921a507b0f..19a692e27c92 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -487,7 +487,7 @@ struct flash_info { unsigned sector_size; u16 n_sectors; u16 page_size; - u16 addr_nbytes; + u8 addr_nbytes; bool parse_sfdp; u16 flags; -- cgit v1.2.3-58-ga151 From 47c6f8a67f2ce1b95202c16fa126411d6f5c7d5c Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:01 +0300 Subject: mtd: spi-nor: Do not change nor->addr_nbytes at SFDP parsing time At the SFDP parsing time we should not change members of struct spi_nor, but instead fill members of struct spi_nor_flash_parameters which could later on be used by callers. The caller will then decide if SFDP params should be used and more importantly when they should be used. Clean the code flow and don't initialize nor->addr_nbytes at SFDP parsing time. Signed-off-by: Tudor Ambarus Tested-by: Takahiro Kuwano Reviewed-by: Michael Walle Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220725092505.446315-4-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 5 ++--- drivers/mtd/spi-nor/core.h | 2 ++ drivers/mtd/spi-nor/issi.c | 2 +- drivers/mtd/spi-nor/sfdp.c | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 31604188ee59..9f07f1036151 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2251,8 +2251,8 @@ static int spi_nor_default_setup(struct spi_nor *nor, static int spi_nor_set_addr_nbytes(struct spi_nor *nor) { - if (nor->addr_nbytes) { - /* already configured from SFDP */ + if (nor->params->addr_nbytes) { + nor->addr_nbytes = nor->params->addr_nbytes; } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { /* * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So @@ -2492,7 +2492,6 @@ static void spi_nor_sfdp_init_params_deprecated(struct spi_nor *nor) if (spi_nor_parse_sfdp(nor)) { memcpy(nor->params, &sfdp_params, sizeof(*nor->params)); - nor->addr_nbytes = 0; nor->flags &= ~SNOR_F_4B_OPCODES; } } diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 19a692e27c92..7dc4cda41db3 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -340,6 +340,7 @@ struct spi_nor_otp { * @writesize Minimal writable flash unit size. Defaults to 1. Set to * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. + * @addr_nbytes: number of address bytes to send. * @rdsr_dummy: dummy cycles needed for Read Status Register command * in octal DTR mode. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register @@ -372,6 +373,7 @@ struct spi_nor_flash_parameter { u64 size; u32 writesize; u32 page_size; + u8 addr_nbytes; u8 rdsr_dummy; u8 rdsr_addr_nbytes; diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 71687e5babdc..89a66a19d754 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -20,7 +20,7 @@ is25lp256_post_bfpt_fixups(struct spi_nor *nor, */ if ((bfpt->dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) == BFPT_DWORD1_ADDRESS_BYTES_3_ONLY) - nor->addr_nbytes = 4; + nor->params->addr_nbytes = 4; return 0; } diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 61ae8c8c5237..3a48173a2d78 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -462,11 +462,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, switch (bfpt.dwords[BFPT_DWORD(1)] & BFPT_DWORD1_ADDRESS_BYTES_MASK) { case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: - nor->addr_nbytes = 3; + params->addr_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: - nor->addr_nbytes = 4; + params->addr_nbytes = 4; break; default: @@ -653,7 +653,7 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->addr_nbytes; + return nor->params->addr_nbytes; } } @@ -1098,7 +1098,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, * Spansion memory. However this quirk is no longer needed with new * SFDP compliant memories. */ - nor->addr_nbytes = 4; + params->addr_nbytes = 4; nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT; /* fall through */ -- cgit v1.2.3-58-ga151 From 08412e72afba3a2daef3e7f3378c3753255a0017 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:02 +0300 Subject: mtd: spi-nor: core: Return error code from set_4byte_addr_mode() The prams->set_4byte_addr_mode returns error code but is not handled in spi_nor_init(). Handle the return code from set_4byte_addr_mode(). Suggested-by: Michael Walle Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Pratyush Yadav Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-5-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 9f07f1036151..ec4c368b4ba6 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2724,7 +2724,7 @@ static int spi_nor_init(struct spi_nor *nor) */ WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET, "enabling reset hack; may not recover from unexpected reboots\n"); - nor->params->set_4byte_addr_mode(nor, true); + return nor->params->set_4byte_addr_mode(nor, true); } return 0; -- cgit v1.2.3-58-ga151 From d7931a21506321327351f68fdebf74a56a58e675 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Mon, 25 Jul 2022 12:25:03 +0300 Subject: mtd: spi-nor: core: Track flash's internal address mode We need to track the flash's internal address mode as there are flashes that can operate with 4B opcodes but unfortunately do not have a 4B opcode correspondent for all the 3B opcodes. Such an example is the Infineon Semper chips which provide 4B opcodes for read/program/erase but do not provide 4B opcodes for Read/Write Any Register. These registers are indexed by address and require the internal address mode of the flash before Read/Write Any Register opcodes are issued. 4B opcodes are preferred over changing the flash's address mode to 4byte, as set_4byte_addr_mode could be done in a non-volatile way and could break the boot sequence. Thus we need to track the flash's internal address mode so that we can use 4B opcodes together with opcodes that don't have a 4B opcode correspondent. Track flash's internal address mode. addr_mode_nbytes is discovered when parsing BFPT. For the BFPT_DWORD1_ADDRESS_BYTES_3_OR_4 case, one could introduce a method that queries the flash's internal address mode at run-time (works for Winbond). If a run-time querying can not be accomplished or if SFDP is not defined at all, but the address mode is volatile and resets to a default known value at boot, one can change the default addr_mode_nbytes value of 3 by introducing a flash_info flag. If the address mode can not be queried, discovered and it is configured via a non-volatile register, we may introduce a dt property, but it will harm the generic approach of the jedec,spi-nor compatible. All this complexity is not needed now, so let it for future development. Signed-off-by: Tudor Ambarus Reviewed-by: Takahiro Kuwano Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-6-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/core.h | 5 +++++ drivers/mtd/spi-nor/sfdp.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 7dc4cda41db3..85b0cf254e97 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -341,6 +341,10 @@ struct spi_nor_otp { * ECC unit size for ECC-ed flashes. * @page_size: the page size of the SPI NOR flash memory. * @addr_nbytes: number of address bytes to send. + * @addr_mode_nbytes: number of address bytes of current address mode. Useful + * when the flash operates with 4B opcodes but needs the + * internal address mode for opcodes that don't have a 4B + * opcode correspondent. * @rdsr_dummy: dummy cycles needed for Read Status Register command * in octal DTR mode. * @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register @@ -374,6 +378,7 @@ struct spi_nor_flash_parameter { u32 writesize; u32 page_size; u8 addr_nbytes; + u8 addr_mode_nbytes; u8 rdsr_dummy; u8 rdsr_addr_nbytes; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 3a48173a2d78..2257f1b4c2e2 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -463,10 +463,12 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, case BFPT_DWORD1_ADDRESS_BYTES_3_ONLY: case BFPT_DWORD1_ADDRESS_BYTES_3_OR_4: params->addr_nbytes = 3; + params->addr_mode_nbytes = 3; break; case BFPT_DWORD1_ADDRESS_BYTES_4_ONLY: params->addr_nbytes = 4; + params->addr_mode_nbytes = 4; break; default: @@ -653,7 +655,7 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings return 4; case SMPT_CMD_ADDRESS_LEN_USE_CURRENT: default: - return nor->params->addr_nbytes; + return nor->params->addr_mode_nbytes; } } -- cgit v1.2.3-58-ga151 From a6b50aa1279614df7033f8b57d6854fce0334e27 Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:04 +0300 Subject: mtd: spi-nor: spansion: Add local function to discover page size The page size check in s28hs512t fixup can be used for s25hs/hl-t as well. Move that to a newly created local function. Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Reviewed-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-7-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/spansion.c | 53 ++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 43cd6cd92537..60e41e1a9a92 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -113,6 +113,36 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } +/** + * cypress_nor_set_page_size() - Set page size which corresponds to the flash + * configuration. + * @nor: pointer to a 'struct spi_nor' + * + * The BFPT table advertises a 512B or 256B page size depending on part but the + * page size is actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_set_page_size(struct spi_nor *nor) +{ + struct spi_mem_op op = + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, + nor->bouncebuf); + int ret; + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) + nor->params->page_size = 512; + else + nor->params->page_size = 256; + + return 0; +} + /** * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' @@ -167,28 +197,7 @@ static int s28hs512t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - /* - * The BFPT table advertises a 512B page size but the page size is - * actually configurable (with the default being 256B). Read from - * CFR3V[4] and set the correct size. - */ - struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_CFR3V, - nor->bouncebuf); - int ret; - - spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); - - ret = spi_mem_exec_op(nor->spimem, &op); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3V_PGSZ) - nor->params->page_size = 512; - else - nor->params->page_size = 256; - - return 0; + return cypress_nor_set_page_size(nor); } static const struct spi_nor_fixups s28hs512t_fixups = { -- cgit v1.2.3-58-ga151 From b6b23833fc42a10ceed00006cb0a6184f9b9bbde Mon Sep 17 00:00:00 2001 From: Takahiro Kuwano Date: Mon, 25 Jul 2022 12:25:05 +0300 Subject: mtd: spi-nor: spansion: Add s25hl-t/s25hs-t IDs and fixups The S25HL-T/S25HS-T family is the Infineon SEMPER Flash with Quad SPI. These Infineon chips support volatile version of configuration registers and it is recommended to update volatile registers in the field application due to a risk of the non-volatile registers corruption by power interrupt. Add support for volatile QE bit. For the single-die package parts (512Mb and 1Gb), only bottom 4KB and uniform sector sizes are supported. This is due to missing or incorrect entries in SMPT. Fixup for other sector sizes configurations will be followed up as needed. Tested on Xilinx Zynq-7000 FPGA board. Signed-off-by: Takahiro Kuwano Signed-off-by: Tudor Ambarus Acked-by: Michael Walle Link: https://lore.kernel.org/r/20220725092505.446315-8-tudor.ambarus@microchip.com --- drivers/mtd/spi-nor/spansion.c | 132 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 60e41e1a9a92..0150049007be 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -14,6 +14,8 @@ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ +#define SPINOR_REG_CYPRESS_CFR1V 0x00800002 +#define SPINOR_REG_CYPRESS_CFR1V_QUAD_EN BIT(1) /* Quad Enable */ #define SPINOR_REG_CYPRESS_CFR2V 0x00800003 #define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR3V 0x00800004 @@ -113,6 +115,68 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } +/** + * cypress_nor_quad_enable_volatile() - enable Quad I/O mode in volatile + * register. + * @nor: pointer to a 'struct spi_nor' + * + * It is recommended to update volatile registers in the field application due + * to a risk of the non-volatile registers corruption by power interrupt. This + * function sets Quad Enable bit in CFR1 volatile. If users set the Quad Enable + * bit in the CFR1 non-volatile in advance (typically by a Flash programmer + * before mounting Flash on PCB), the Quad Enable bit in the CFR1 volatile is + * also set during Flash power-up. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) +{ + struct spi_mem_op op; + u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; + u8 cfr1v_written; + int ret; + + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, + nor->bouncebuf); + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR1V_QUAD_EN) + return 0; + + /* Update the Quad Enable bit. */ + nor->bouncebuf[0] |= SPINOR_REG_CYPRESS_CFR1V_QUAD_EN; + op = (struct spi_mem_op) + CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, 1, + nor->bouncebuf); + ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + cfr1v_written = nor->bouncebuf[0]; + + /* Read back and check it. */ + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, + SPINOR_REG_CYPRESS_CFR1V, + nor->bouncebuf); + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + + if (nor->bouncebuf[0] != cfr1v_written) { + dev_err(nor->dev, "CFR1: Read back test failed\n"); + return -EIO; + } + + return 0; +} + /** * cypress_nor_set_page_size() - Set page size which corresponds to the flash * configuration. @@ -143,6 +207,58 @@ static int cypress_nor_set_page_size(struct spi_nor *nor) return 0; } +static int +s25hx_t_post_bfpt_fixup(struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header, + const struct sfdp_bfpt *bfpt) +{ + /* Replace Quad Enable with volatile version */ + nor->params->quad_enable = cypress_nor_quad_enable_volatile; + + return cypress_nor_set_page_size(nor); +} + +static void s25hx_t_post_sfdp_fixup(struct spi_nor *nor) +{ + struct spi_nor_erase_type *erase_type = + nor->params->erase_map.erase_type; + unsigned int i; + + /* + * In some parts, 3byte erase opcodes are advertised by 4BAIT. + * Convert them to 4byte erase opcodes. + */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) { + switch (erase_type[i].opcode) { + case SPINOR_OP_SE: + erase_type[i].opcode = SPINOR_OP_SE_4B; + break; + case SPINOR_OP_BE_4K: + erase_type[i].opcode = SPINOR_OP_BE_4K_4B; + break; + default: + break; + } + } +} + +static void s25hx_t_late_init(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + + /* Fast Read 4B requires mode cycles */ + params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; + + /* The writesize should be ECC data unit size */ + params->writesize = 16; +} + +static struct spi_nor_fixups s25hx_t_fixups = { + .post_bfpt = s25hx_t_post_bfpt_fixup, + .post_sfdp = s25hx_t_post_sfdp_fixup, + .late_init = s25hx_t_late_init, +}; + /** * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' @@ -319,6 +435,22 @@ static const struct flash_info spansion_nor_parts[] = { { "s25fl256l", INFO(0x016019, 0, 64 * 1024, 512) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, + { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512) + PARSE_SFDP + MFR_FLAGS(USE_CLSR) + .fixups = &s25hx_t_fixups }, { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) FLAGS(SPI_NOR_NO_ERASE) }, { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) -- cgit v1.2.3-58-ga151 From f8cd9f632f4415b1e8838bdca8ab42cfb37a6584 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 29 Jun 2022 15:30:13 +0200 Subject: mtd: spi-nor: fix spi_nor_spimem_setup_op() call in spi_nor_erase_{sector,chip}() For erase operations, reg_proto must be used as indicated in struct spi_nor description in spi-nor.h. This issue was found when DT property spi-tx-bus-width is set to 4. In this case the spi_mem_op->addr.buswidth is set to 4 for erase command which is not correct. Tested on stm32mp157c-ev1 board with mx66l51235f spi-nor. Fixes: 0e30f47232ab ("mtd: spi-nor: add support for DTR protocol") Signed-off-by: Patrice Chotard [ta: use nor->reg_proto in spi_nor_controller_ops_erase()] Signed-off-by: Tudor Ambarus Tested-by: Alexander Sverdlin Reviewed-by: Pratyush Yadav Link: https://lore.kernel.org/r/20220629133013.3382393-1-patrice.chotard@foss.st.com --- drivers/mtd/spi-nor/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index ec4c368b4ba6..f2c64006f8d7 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -177,7 +177,7 @@ int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode, static int spi_nor_controller_ops_erase(struct spi_nor *nor, loff_t offs) { - if (spi_nor_protocol_is_dtr(nor->write_proto)) + if (spi_nor_protocol_is_dtr(nor->reg_proto)) return -EOPNOTSUPP; return nor->controller_ops->erase(nor, offs); @@ -972,7 +972,7 @@ static int spi_nor_erase_chip(struct spi_nor *nor) if (nor->spimem) { struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; - spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); ret = spi_mem_exec_op(nor->spimem, &op); } else { @@ -1115,7 +1115,7 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) SPI_NOR_SECTOR_ERASE_OP(nor->erase_opcode, nor->addr_nbytes, addr); - spi_nor_spimem_setup_op(nor, &op, nor->write_proto); + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); return spi_mem_exec_op(nor->spimem, &op); } else if (nor->controller_ops->erase) { -- cgit v1.2.3-58-ga151 From 7ec4cdb321738d44ae5d405e7b6ac73dfbf99caa Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 25 Jul 2022 22:49:25 +0900 Subject: mtd: core: check partition before dereference syzbot is reporting NULL pointer dereference at mtd_check_of_node() [1], for mtdram test device (CONFIG_MTD_MTDRAM) is not partition. Link: https://syzkaller.appspot.com/bug?extid=fe013f55a2814a9e8cfd [1] Reported-by: syzbot Reported-by: kernel test robot Fixes: ad9b10d1eaada169 ("mtd: core: introduce of support for dynamic partitions") Signed-off-by: Tetsuo Handa CC: stable@vger.kernel.org Signed-off-by: Richard Weinberger --- drivers/mtd/mtdcore.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6fafea80fd98..a9b8be9f40dc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -559,6 +559,8 @@ static void mtd_check_of_node(struct mtd_info *mtd) return; /* Check if a partitions node exist */ + if (!mtd_is_partition(mtd)) + return; parent = mtd->parent; parent_dn = dev_of_node(&parent->dev); if (!parent_dn) -- cgit v1.2.3-58-ga151