From d3a337a592bc26be374a70d1aca4aa20080527d4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 7 Aug 2022 12:06:56 +0200 Subject: [PATCH] uboot-mediatek: additions from MTK SDK * updated SNAND/SNFI driver brings support for MT7981 * add support for MediaTek NAND Memory bad Block Management (NMBM) (not used for any boards atm, but could be useful in future) * wire up NMBM support for MT7622, MT7629, MT7981 and MT7986 * replace some local patches with updated version from SDK * bring some legacy precompiler symbols which haven't been converted into Kconfig symbols in U-Boot 2022.07, remove when bumbping to U-Boot 2022.10: 100-28-include-configs-mt7986-h-from-SDK.patch Source: https://github.com/mtk-openwrt/u-boot Signed-off-by: Daniel Golle --- ...-clk-Add-debugging-for-return-values.patch | 69 - ...22-enable-environment-for-mt7622_rfb.patch | 2 +- ...7622-remove-default-pinctrl-of-uart0.patch | 2 +- ...t7622-force-high-speed-mode-for-uart.patch | 2 +- ...-0018-arm-dts-mt7622-add-i2c-support.patch | 4 +- ...split-the-code-of-generating-NAND-he.patch | 2 +- ...Add-support-for-flashes-tested-by-xi.patch | 182 + ...-Add-support-for-Macronix-Octal-flas.patch | 614 +++ ...add-winbond-w25q512nw-family-support.patch | 39 + ...clk-remove-log_ret-from-clk_get_rate.patch | 43 + ...tek-add-more-network-configurations.patch} | 32 +- ...upport-for-MediaTek-SPI-NAND-flash-.patch} | 424 +- ...3-mtd-mtk-snand-add-support-for-SPL.patch} | 11 +- ...-add-support-for-generic-MTD-device.patch} | 4 +- ...d-add-a-new-mtd-device-type-for-NMBM.patch | 44 + ...6-mtd-add-core-facility-code-of-NMBM.patch | 3422 +++++++++++++++++ .../100-07-mtd-nmbm-add-support-for-mtd.patch | 958 +++++ ...dd-support-to-initialize-NMBM-after-.patch | 46 + .../patches/100-09-cmd-add-nmbm-command.patch | 370 ++ ...-markbad-subcommand-for-NMBM-testing.patch | 80 + ...add-support-for-NMBM-upper-MTD-layer.patch | 280 ++ ...d-mtk-snand-add-NMBM-support-for-SPL.patch | 173 + ...new-command-for-NAND-flash-debugging.patch | 1118 ++++++ ...-add-support-to-read-flash-unique-ID.patch | 142 + ...-add-support-to-read-flash-unique-ID.patch | 48 + ...-ability-to-select-item-by-shortkey.patch} | 130 +- ...and-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch | 28 + ...d-support-for-booting-from-SPI-NAND.patch} | 141 +- ...oard-mt7622-use-new-spi-nand-driver.patch} | 16 +- ...-reference-board-using-new-spi-nand-.patch | 223 ++ ...00-21-mtd-spi-nor-add-more-flash-ids.patch | 76 + ...i-nand-backport-from-upstream-kernel.patch | 1550 ++++++++ ...support-to-display-verbose-error-log.patch | 78 + ...olume-find-create-remove-APIs-public.patch | 58 + ...creating-volume-with-all-free-spaces.patch | 27 + ...ort-to-create-environment-volume-if-.patch | 72 + ...rt-for-UBI-end-of-filesystem-marker.patch} | 15 + ...28-include-configs-mt7986-h-from-SDK.patch | 26 + ...-board-mediatek-wire-up-NMBM-support.patch | 240 ++ .../patches/110-no-kwbimage.patch | 2 +- .../211-cmd-bootmenu-custom-title.patch | 6 +- .../patches/220-cmd-env-readmem.patch | 8 +- ...7622-generic-reset-button-ignore-env.patch | 12 +- ...50-add-support-for-Winbond-W25Q512JV.patch | 11 - .../patches/412-add-ubnt-unifi-6-lr.patch | 8 +- 45 files changed, 10562 insertions(+), 276 deletions(-) delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch create mode 100644 package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch create mode 100644 package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch create mode 100644 package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch create mode 100644 package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch rename package/boot/uboot-mediatek/patches/{000-mtk-09-board-mediatek-add-more-network-configurations.patch => 100-01-board-mediatek-add-more-network-configurations.patch} (56%) rename package/boot/uboot-mediatek/patches/{000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch => 100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch} (90%) rename package/boot/uboot-mediatek/patches/{000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch => 100-03-mtd-mtk-snand-add-support-for-SPL.patch} (93%) rename package/boot/uboot-mediatek/patches/{000-mtk-16-env-add-support-for-generic-MTD-device.patch => 100-04-env-add-support-for-generic-MTD-device.patch} (98%) create mode 100644 package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch create mode 100644 package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch create mode 100644 package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch create mode 100644 package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch create mode 100644 package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch create mode 100644 package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch create mode 100644 package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch create mode 100644 package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch create mode 100644 package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch create mode 100644 package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch create mode 100644 package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch rename package/boot/uboot-mediatek/patches/{210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch => 100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch} (62%) create mode 100644 package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch rename package/boot/uboot-mediatek/patches/{000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch => 100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch} (61%) rename package/boot/uboot-mediatek/patches/{000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch => 100-19-board-mt7622-use-new-spi-nand-driver.patch} (85%) create mode 100644 package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch create mode 100644 package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch create mode 100644 package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch create mode 100644 package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch create mode 100644 package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch create mode 100644 package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch create mode 100644 package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch rename package/boot/uboot-mediatek/patches/{600-ubi-detect-eof-marker.patch => 100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch} (65%) create mode 100644 package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch create mode 100644 package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch delete mode 100644 package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch diff --git a/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch b/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch deleted file mode 100644 index 3b68c52713a..00000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 34ed9f6d3018d32c7c015e57c9985d3c4c07b706 Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Thu, 11 Mar 2021 10:28:53 +0000 -Subject: [PATCH 01/12] Revert "clk: Add debugging for return values" - -This reverts commit 5c5992cb90cf9ca4d51e38d9a95a13c293904df5. ---- - drivers/clk/clk-uclass.c | 16 +++++----------- - 1 file changed, 5 insertions(+), 11 deletions(-) - ---- a/drivers/clk/clk-uclass.c -+++ b/drivers/clk/clk-uclass.c -@@ -88,7 +88,7 @@ static int clk_get_by_index_tail(int ret - if (ret) { - debug("%s: uclass_get_device_by_of_offset failed: err=%d\n", - __func__, ret); -- return log_msg_ret("get", ret); -+ return ret; - } - - clk->dev = dev_clk; -@@ -101,15 +101,14 @@ static int clk_get_by_index_tail(int ret - ret = clk_of_xlate_default(clk, args); - if (ret) { - debug("of_xlate() failed: %d\n", ret); -- return log_msg_ret("xlate", ret); -+ return ret; - } - - return clk_request(dev_clk, clk); - err: - debug("%s: Node '%s', property '%s', failed to request CLK index %d: %d\n", - __func__, ofnode_get_name(node), list_name, index, ret); -- -- return log_msg_ret("prop", ret); -+ return ret; - } - - static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name, -@@ -128,7 +127,7 @@ static int clk_get_by_indexed_prop(struc - if (ret) { - debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n", - __func__, ret); -- return log_ret(ret); -+ return ret; - } - - -@@ -469,7 +468,6 @@ void clk_free(struct clk *clk) - ulong clk_get_rate(struct clk *clk) - { - const struct clk_ops *ops; -- int ret; - - debug("%s(clk=%p)\n", __func__, clk); - if (!clk_valid(clk)) -@@ -479,11 +477,7 @@ ulong clk_get_rate(struct clk *clk) - if (!ops->get_rate) - return -ENOSYS; - -- ret = ops->get_rate(clk); -- if (ret) -- return log_ret(ret); -- -- return 0; -+ return ops->get_rate(clk); - } - - struct clk *clk_get_parent(struct clk *clk) diff --git a/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch b/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch index cc5fb573247..cf2e48d5c48 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch +++ b/package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch @@ -21,7 +21,7 @@ Signed-off-by: Weijie Gao CONFIG_DEBUG_UART_BASE=0x11002000 CONFIG_DEBUG_UART_CLOCK=25000000 CONFIG_SYS_LOAD_ADDR=0x4007ff28 -@@ -22,6 +24,9 @@ CONFIG_CMD_SF_TEST=y +@@ -21,6 +23,9 @@ CONFIG_CMD_SF_TEST=y CONFIG_CMD_PING=y CONFIG_CMD_SMC=y CONFIG_ENV_OVERWRITE=y diff --git a/package/boot/uboot-mediatek/patches/001-mtk-0100-arm-dts-mt7622-remove-default-pinctrl-of-uart0.patch b/package/boot/uboot-mediatek/patches/001-mtk-0100-arm-dts-mt7622-remove-default-pinctrl-of-uart0.patch index 63e189f56ce..603896f2605 100644 --- a/package/boot/uboot-mediatek/patches/001-mtk-0100-arm-dts-mt7622-remove-default-pinctrl-of-uart0.patch +++ b/package/boot/uboot-mediatek/patches/001-mtk-0100-arm-dts-mt7622-remove-default-pinctrl-of-uart0.patch @@ -34,7 +34,7 @@ Signed-off-by: Weijie Gao --- a/arch/arm/dts/mt7622-rfb.dts +++ b/arch/arm/dts/mt7622-rfb.dts -@@ -196,8 +196,6 @@ +@@ -189,8 +189,6 @@ }; &uart0 { diff --git a/package/boot/uboot-mediatek/patches/002-0011-arm-dts-mt7622-force-high-speed-mode-for-uart.patch b/package/boot/uboot-mediatek/patches/002-0011-arm-dts-mt7622-force-high-speed-mode-for-uart.patch index 085d66776fd..7ed6083c895 100644 --- a/package/boot/uboot-mediatek/patches/002-0011-arm-dts-mt7622-force-high-speed-mode-for-uart.patch +++ b/package/boot/uboot-mediatek/patches/002-0011-arm-dts-mt7622-force-high-speed-mode-for-uart.patch @@ -16,7 +16,7 @@ Signed-off-by: Weijie Gao --- a/arch/arm/dts/mt7622.dtsi +++ b/arch/arm/dts/mt7622.dtsi -@@ -191,6 +191,7 @@ +@@ -175,6 +175,7 @@ status = "disabled"; assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>; assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>; diff --git a/package/boot/uboot-mediatek/patches/002-0018-arm-dts-mt7622-add-i2c-support.patch b/package/boot/uboot-mediatek/patches/002-0018-arm-dts-mt7622-add-i2c-support.patch index 415b37ba559..378078882e5 100644 --- a/package/boot/uboot-mediatek/patches/002-0018-arm-dts-mt7622-add-i2c-support.patch +++ b/package/boot/uboot-mediatek/patches/002-0018-arm-dts-mt7622-add-i2c-support.patch @@ -29,7 +29,7 @@ Signed-off-by: Weijie Gao }; &snfi { -@@ -249,3 +257,13 @@ +@@ -242,3 +250,13 @@ &u3phy { status = "okay"; }; @@ -45,7 +45,7 @@ Signed-off-by: Weijie Gao +}; --- a/arch/arm/dts/mt7622.dtsi +++ b/arch/arm/dts/mt7622.dtsi -@@ -440,4 +440,28 @@ +@@ -424,4 +424,28 @@ status = "disabled"; }; diff --git a/package/boot/uboot-mediatek/patches/002-0029-tools-mtk_image-split-the-code-of-generating-NAND-he.patch b/package/boot/uboot-mediatek/patches/002-0029-tools-mtk_image-split-the-code-of-generating-NAND-he.patch index 8ecfda2d464..9a5332f6950 100644 --- a/package/boot/uboot-mediatek/patches/002-0029-tools-mtk_image-split-the-code-of-generating-NAND-he.patch +++ b/package/boot/uboot-mediatek/patches/002-0029-tools-mtk_image-split-the-code-of-generating-NAND-he.patch @@ -24,7 +24,7 @@ Signed-off-by: Weijie Gao --- a/tools/Makefile +++ b/tools/Makefile -@@ -148,6 +148,7 @@ dumpimage-mkimage-objs := aisimage.o \ +@@ -147,6 +147,7 @@ dumpimage-mkimage-objs := aisimage.o \ gpimage.o \ gpimage-common.o \ mtk_image.o \ diff --git a/package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch b/package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch new file mode 100644 index 00000000000..e54b46f5e44 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch @@ -0,0 +1,182 @@ +From baef13ec9d592a27b5d3bf03967bfd2bebd65157 Mon Sep 17 00:00:00 2001 +From: Ashok Reddy Soma +Date: Wed, 25 May 2022 10:47:12 +0530 +Subject: [PATCH] mtd: spi-nor-ids: Add support for flashes tested by xilinx + +Add support for various flashes from below manufacturers which are tested +by xilinx for years. + +EON: + en25q128b +GIGA: + gd25lx256e +ISSI: + is25lp008 + is25lp016 + is25lp01g + is25wp008 + is25wp016 + is25wp01g + is25wx256 +MACRONIX: + mx25u51245f + mx66u1g45g + mx66l2g45g +MICRON: + mt35xl512aba + mt35xu01g +SPANSION: + s70fs01gs_256k +SST: + sst26wf016b +WINBOND: + w25q16dw + w25q16jv + w25q512jv + w25q32bv + w25h02jv + +Signed-off-by: Ashok Reddy Soma +Link: https://lore.kernel.org/r/1653455832-14763-1-git-send-email-ashok.reddy.soma@xilinx.com +Signed-off-by: Michal Simek +--- + drivers/mtd/spi/spi-nor-ids.c | 37 +++++++++++++++++++++++++++++++++++ + 1 file changed, 37 insertions(+) + +--- a/drivers/mtd/spi/spi-nor-ids.c ++++ b/drivers/mtd/spi/spi-nor-ids.c +@@ -82,6 +82,7 @@ const struct flash_info spi_nor_ids[] = + /* EON -- en25xxx */ + { INFO("en25q32b", 0x1c3016, 0, 64 * 1024, 64, 0) }, + { INFO("en25q64", 0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, ++ { INFO("en25q128b", 0x1c3018, 0, 64 * 1024, 256, 0) }, + { INFO("en25qh128", 0x1c7018, 0, 64 * 1024, 256, 0) }, + { INFO("en25s64", 0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, + #endif +@@ -127,11 +128,17 @@ const struct flash_info spi_nor_ids[] = + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, ++ { ++ INFO("gd25lx256e", 0xc86819, 0, 64 * 1024, 512, ++ SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) ++ }, + #endif + #ifdef CONFIG_SPI_FLASH_ISSI /* ISSI */ + /* ISSI */ + { INFO("is25lq040b", 0x9d4013, 0, 64 * 1024, 8, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("is25lp008", 0x9d6014, 0, 64 * 1024, 16, SPI_NOR_QUAD_READ) }, ++ { INFO("is25lp016", 0x9d6015, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ) }, + { INFO("is25lp032", 0x9d6016, 0, 64 * 1024, 64, 0) }, + { INFO("is25lp064", 0x9d6017, 0, 64 * 1024, 128, 0) }, + { INFO("is25lp128", 0x9d6018, 0, 64 * 1024, 256, +@@ -140,6 +147,10 @@ const struct flash_info spi_nor_ids[] = + SECT_4K | SPI_NOR_DUAL_READ) }, + { INFO("is25lp512", 0x9d601a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("is25lp01g", 0x9d601b, 0, 64 * 1024, 2048, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("is25wp008", 0x9d7014, 0, 64 * 1024, 16, SPI_NOR_QUAD_READ) }, ++ { INFO("is25wp016", 0x9d7015, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ) }, + { INFO("is25wp032", 0x9d7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("is25wp064", 0x9d7017, 0, 64 * 1024, 128, +@@ -151,6 +162,10 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_4B_OPCODES) }, + { INFO("is25wp512", 0x9d701a, 0, 64 * 1024, 1024, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("is25wp01g", 0x9d701b, 0, 64 * 1024, 2048, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("is25wx256", 0x9d5b19, 0, 128 * 1024, 256, ++ SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, + #endif + #ifdef CONFIG_SPI_FLASH_MACRONIX /* MACRONIX */ + /* Macronix */ +@@ -176,8 +191,11 @@ const struct flash_info spi_nor_ids[] = + { INFO("mx25l25655e", 0xc22619, 0, 64 * 1024, 512, 0) }, + { INFO("mx66l51235l", 0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mx66u51235f", 0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25u51245f", 0xc2953a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66u1g45g", 0xc2253b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mx66u2g45g", 0xc2253c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mx66l1g45g", 0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("mx66l2g45g", 0xc2201c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mx25l1633e", 0xc22415, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) }, + { INFO("mx25r6435f", 0xc22817, 0, 64 * 1024, 128, SECT_4K) }, + { INFO("mx66uw2g345g", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, +@@ -208,8 +226,10 @@ const struct flash_info spi_nor_ids[] = + { INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) }, + { INFO("mt25ql02g", 0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) }, + #ifdef CONFIG_SPI_FLASH_MT35XU ++ { INFO("mt35xl512aba", 0x2c5a1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) }, + { INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) }, + #endif /* CONFIG_SPI_FLASH_MT35XU */ ++ { INFO6("mt35xu01g", 0x2c5b1b, 0x104100, 128 * 1024, 1024, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, + #endif + #ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */ +@@ -225,6 +245,7 @@ const struct flash_info spi_nor_ids[] = + { INFO("s25fl512s_256k", 0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, + { INFO("s25fl512s_64k", 0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, + { INFO("s25fl512s_512k", 0x010220, 0x4f00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, ++ { INFO("s70fs01gs_256k", 0x010221, 0x4d00, 256 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, 0) }, + { INFO("s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, 0) }, + { INFO6("s25fl128s", 0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) }, +@@ -275,6 +296,7 @@ const struct flash_info spi_nor_ids[] = + { INFO("sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) }, + { INFO("sst25wf080", 0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) }, + { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("sst26wf016b", 0xbf2641, 0, 64 * 1024, 32, SECT_4K) }, + { INFO("sst26wf016", 0xbf2651, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, + { INFO("sst26wf032", 0xbf2622, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, + { INFO("sst26wf064", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK) }, +@@ -312,11 +334,19 @@ const struct flash_info spi_nor_ids[] = + { INFO("w25q20ew", 0xef6012, 0, 64 * 1024, 4, SECT_4K) }, + { INFO("w25q32", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { ++ INFO("w25q16dw", 0xef6015, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) ++ }, ++ { + INFO("w25q32dw", 0xef6016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { ++ INFO("w25q16jv", 0xef7015, 0, 64 * 1024, 32, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) ++ }, ++ { + INFO("w25q32jv", 0xef7016, 0, 64 * 1024, 64, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +@@ -363,6 +393,11 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { ++ INFO("w25q512jv", 0xef7119, 0, 64 * 1024, 512, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { + INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +@@ -370,6 +405,7 @@ const struct flash_info spi_nor_ids[] = + { INFO("w25q80", 0xef5014, 0, 64 * 1024, 16, SECT_4K) }, + { INFO("w25q80bl", 0xef4014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25q16cl", 0xef4015, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("w25q32bv", 0xef4016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25q64cv", 0xef4017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25q128", 0xef4018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | +@@ -378,6 +414,7 @@ const struct flash_info spi_nor_ids[] = + { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + #endif + #ifdef CONFIG_SPI_FLASH_XMC + /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */ diff --git a/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch new file mode 100644 index 00000000000..b0299ac6d8c --- /dev/null +++ b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch @@ -0,0 +1,614 @@ +From 4290ed7835e0a76792b8e554ae79e3f6d52ac800 Mon Sep 17 00:00:00 2001 +From: JaimeLiao +Date: Mon, 18 Jul 2022 14:49:22 +0800 +Subject: [PATCH] mtd: spi-nor-core: Add support for Macronix Octal flash + +Adding Macronix Octal flash for Octal DTR support. + +The octaflash series can be divided into the following types: + +MX25 series : Serial NOR Flash. +MX66 series : Serial NOR Flash with stacked die.(Size larger than 1Gb) +LM/UM series : Up to 250MHz clock frequency with both DTR/STR operation. +LW/UW series : Support simultaneous Read-while-Write operation in multiple + bank architecture. Read-while-write feature which means read + data one bank while another bank is programing or erasing. + +MX25LM : 3.0V Octal I/O + -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7841/MX25LM51245G,%203V,%20512Mb,%20v1.1.pdf + +MX25UM : 1.8V Octal I/O + -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7525/MX25UM51245G%20Extreme%20Speed,%201.8V,%20512Mb,%20v1.0.pdf + +MX66LM : 3.0V Octal I/O with stacked die + -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7929/MX66LM1G45G,%203V,%201Gb,%20v1.1.pdf + +MX66UM : 1.8V Octal I/O with stacked die + -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7721/MX66UM1G45G,%201.8V,%201Gb,%20v1.1.pdf + +MX25LW : 3.0V Octal I/O with Read-while-Write +MX25UW : 1.8V Octal I/O with Read-while-Write +MX66LW : 3.0V Octal I/O with Read-while-Write and stack die +MX66UW : 1.8V Octal I/O with Read-while-Write and stack die + +About LW/UW series, please contact us freely if you have any +questions. For adding Octal NOR Flash IDs, we have validated +each Flash on plateform zynq-picozed. + +As below are the SFDP table dump. + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2943c +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx66uw2g345gx0 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw2g345gx0 +zynq> hexdump mx66uw2g345gx0 +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 7fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7987 0001 1284 e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 001f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2853b +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx66lm1g45g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66lm1g45g +zynq> hexdump mx66lm1g45g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 6987 0001 1282 e200 02cc 3867 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 6666 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000 +0000130 3514 001c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2853a +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25lm51245g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lm51245g +zynq> hexdump mx25lm51245g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7989 0001 128d e200 02cc 4467 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 6666 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000 +0000130 3514 001c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2863a +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25lw51245g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lw51245g +zynq> hexdump mx25lw51245g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 0000 0000 0000 0000 +0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 6666 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000 +0000130 3514 001c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28539 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25lm25645g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lm25645g +zynq> hexdump mx25lm25645g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 6987 0001 1282 d200 02cc 3867 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 6666 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000 +0000130 3514 001c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2843c +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx66uw2g345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw2g345g +zynq> hexdump mx66uw2g345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 7fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7987 0001 1284 e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 001f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2803b +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx66um1g45g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66um1g45g +zynq> hexdump mx66um1g45g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7989 0001 128d e200 02cc 4467 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 3514 809c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2813b +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx66uw1g45g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw1g45g +zynq> hexdump mx66uw1g45g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2813a +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw51245g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw51245g +zynq> hexdump mx25uw51245g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 7777 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c2843a +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw51345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw51345g +zynq> hexdump mx25uw51345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f e200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28039 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25um25645g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25um25645g +zynq> random: fast init done +zynq> hexdump mx25um25645g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7987 0001 1284 d200 02cc 3867 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 3514 809c 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28139 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw25645g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw25645g +zynq> hexdump mx25uw25645g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7989 0001 128d d200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28339 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25um25345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25um25345g +zynq> hexdump mx25um25345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 6987 0001 1282 d200 02cc 3867 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0904 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28439 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw25345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw25345g +zynq> hexdump mx25uw25345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7987 0001 1284 d200 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28138 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw12845g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw12845g +zynq> hexdump mx25uw12845g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 0000 0000 0000 0000 +0000040 20e5 ff8a ffff 07ff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f c900 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28438 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw12345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw12345g +zynq> hexdump mx25uw12345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 07ff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f c900 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28137 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw6445g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw6445g +zynq> hexdump mx25uw6445g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 03ff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 7989 0001 128d c400 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 a37c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id +c28437 +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer +macronix +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname +mx25uw6345g +zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw6345g +zynq> hexdump mx25uw6345g +0000000 4653 5044 0108 fd04 0700 1401 0040 ff00 +0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00 +0000020 0005 0501 0120 ff00 0084 0201 0134 ff00 +0000030 0000 0000 0000 0000 ffff ffff ffff ffff +0000040 20e5 ff8a ffff 03ff ff00 ff00 ff00 ff00 +0000050 ffee ffff ffff ff00 ffff ff00 200c d810 +0000060 ff00 ff00 798b 0001 128f c400 04cc 4667 +0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000 +0000080 0000 0000 0000 237c 0048 0000 0000 8888 +0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff +00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600 +00000b0 7172 b803 7172 b803 0000 0000 a390 8218 +00000c0 c000 9669 0000 0000 0000 0000 7172 9800 +00000d0 7172 b800 7172 9900 0000 0000 7172 9800 +00000e0 7172 f800 7172 9900 7172 f900 0000 0000 +00000f0 0000 0000 1501 d001 7172 d806 0000 5086 +0000100 0000 0106 0000 0000 0002 0301 0200 0000 +0000110 0000 0106 0000 0000 0000 0672 0200 0000 +0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000 +0000130 4514 8098 0643 000f dc21 ffff ffff ffff +0000140 ffff ffff ffff ffff ffff ffff ffff ffff + +Signed-off-by: JaimeLiao +Reviewed-by: Jagan Teki +--- + drivers/mtd/spi/spi-nor-ids.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/spi/spi-nor-ids.c ++++ b/drivers/mtd/spi/spi-nor-ids.c +@@ -198,7 +198,24 @@ const struct flash_info spi_nor_ids[] = + { INFO("mx66l2g45g", 0xc2201c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, + { INFO("mx25l1633e", 0xc22415, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) }, + { INFO("mx25r6435f", 0xc22817, 0, 64 * 1024, 128, SECT_4K) }, +- { INFO("mx66uw2g345g", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66uw2g345gx0", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66lm1g45g", 0xc2853b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25lm51245g", 0xc2853a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25lw51245g", 0xc2863a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25lm25645g", 0xc28539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66uw2g345g", 0xc2843c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66um1g45g", 0xc2803b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx66uw1g45g", 0xc2813b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw51245g", 0xc2813a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw51345g", 0xc2843a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25um25645g", 0xc28039, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw25645g", 0xc28139, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25um25345g", 0xc28339, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw25345g", 0xc28439, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw12845g", 0xc28138, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw12345g", 0xc28438, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw6445g", 0xc28137, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, ++ { INFO("mx25uw6345g", 0xc28437, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) }, + #endif + + #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ diff --git a/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch new file mode 100644 index 00000000000..b88120052cf --- /dev/null +++ b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch @@ -0,0 +1,39 @@ +From 47ed8b22fd561b65e8541919becc76ab3d86f7a3 Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo +Date: Fri, 8 Jul 2022 12:03:19 -0700 +Subject: [PATCH] mtd: spi-nor-ids: add winbond w25q512nw family support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Winbond w25q512nwq/n and w25q512nwm support. + +datasheet: +https://www.winbond.com/resource-files/W25Q512NW%20RevB%2007192021.pdf + +Signed-off-by: Jae Hyun Yoo +Reviewed-by: Cédric Le Goater +Reviewed-by: Jagan Teki +--- + drivers/mtd/spi/spi-nor-ids.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +--- a/drivers/mtd/spi/spi-nor-ids.c ++++ b/drivers/mtd/spi/spi-nor-ids.c +@@ -415,6 +415,16 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { ++ INFO("w25q512nwq", 0xef6020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { ++ INFO("w25q512nwm", 0xef8020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { + INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) diff --git a/package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch b/package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch new file mode 100644 index 00000000000..30dcffd59e8 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch @@ -0,0 +1,43 @@ +From 19f2aa053d5531a9ca0ece04dca172a522d58b90 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Fri, 29 Jul 2022 11:32:28 +0800 +Subject: [PATCH 32/71] clk: remove log_ret from clk_get_rate + +The return value of clk_get_rate is ulong, an unsigned type. The size of +ulong depends on the cpu architecture, i.e. 4 bytes on 32-bit CPUs and +8 bytes on 64-bit CPUs. + +However log_ret only accepts and returns value in int type, a fixed 4-byte +type. This may truncate the real clock value and cause unexpected error on +64-bit platforms. + +This patch removes log_ret to solve this issue. + +Signed-off-by: Weijie Gao +--- + drivers/clk/clk-uclass.c | 7 +------ + 1 file changed, 1 insertion(+), 6 deletions(-) + +--- a/drivers/clk/clk-uclass.c ++++ b/drivers/clk/clk-uclass.c +@@ -469,7 +469,6 @@ void clk_free(struct clk *clk) + ulong clk_get_rate(struct clk *clk) + { + const struct clk_ops *ops; +- int ret; + + debug("%s(clk=%p)\n", __func__, clk); + if (!clk_valid(clk)) +@@ -479,11 +478,7 @@ ulong clk_get_rate(struct clk *clk) + if (!ops->get_rate) + return -ENOSYS; + +- ret = ops->get_rate(clk); +- if (ret) +- return log_ret(ret); +- +- return 0; ++ return ops->get_rate(clk); + } + + struct clk *clk_get_parent(struct clk *clk) diff --git a/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch similarity index 56% rename from package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch rename to package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch index 50c2ac2e423..95914633736 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch +++ b/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch @@ -1,7 +1,7 @@ -From 938ba7ed996a86c9cc7af08b69df57b8b4c09510 Mon Sep 17 00:00:00 2001 +From 97df847f8f895cc2692bb0e4e933269c275da378 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Tue, 2 Mar 2021 15:47:45 +0800 -Subject: [PATCH 02/12] board: mediatek: add more network configurations +Subject: [PATCH 35/71] board: mediatek: add more network configurations Make the network configurations uniform for mediatek boards @@ -10,7 +10,9 @@ Signed-off-by: Weijie Gao include/configs/mt7622.h | 3 ++- include/configs/mt7623.h | 1 + include/configs/mt7629.h | 1 + - 3 files changed, 4 insertions(+), 1 deletion(-) + include/configs/mt7981.h | 5 +++++ + include/configs/mt7986.h | 5 +++++ + 5 files changed, 14 insertions(+), 1 deletion(-) --- a/include/configs/mt7622.h +++ b/include/configs/mt7622.h @@ -42,3 +44,27 @@ Signed-off-by: Weijie Gao +#define CONFIG_NETMASK 255.255.255.0 #endif +--- a/include/configs/mt7981.h ++++ b/include/configs/mt7981.h +@@ -23,4 +23,9 @@ + /* DRAM */ + #define CONFIG_SYS_SDRAM_BASE 0x40000000 + ++/* Ethernet */ ++#define CONFIG_IPADDR 192.168.1.1 ++#define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 ++ + #endif +--- a/include/configs/mt7986.h ++++ b/include/configs/mt7986.h +@@ -23,4 +23,9 @@ + /* DRAM */ + #define CONFIG_SYS_SDRAM_BASE 0x40000000 + ++/* Ethernet */ ++#define CONFIG_IPADDR 192.168.1.1 ++#define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 ++ + #endif diff --git a/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch b/package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch similarity index 90% rename from package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch rename to package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch index 1caa7ffd243..e2316c62c42 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch +++ b/package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch @@ -1,7 +1,7 @@ -From d6c5309185aae3d9ecf80eae8b248522d11a6136 Mon Sep 17 00:00:00 2001 +From f7704275957852cd4c4632d6da126979ef24b83a Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Tue, 2 Mar 2021 16:58:01 +0800 -Subject: [PATCH 04/12] drivers: mtd: add support for MediaTek SPI-NAND flash +Subject: [PATCH 36/71] drivers: mtd: add support for MediaTek SPI-NAND flash controller Add mtd driver for MediaTek SPI-NAND flash controller @@ -16,15 +16,15 @@ Signed-off-by: Weijie Gao drivers/mtd/Makefile | 2 + drivers/mtd/mtk-snand/Kconfig | 21 + drivers/mtd/mtk-snand/Makefile | 11 + - drivers/mtd/mtk-snand/mtk-snand-def.h | 266 ++++ - drivers/mtd/mtk-snand/mtk-snand-ecc.c | 264 ++++ + drivers/mtd/mtk-snand/mtk-snand-def.h | 271 ++++ + drivers/mtd/mtk-snand/mtk-snand-ecc.c | 395 +++++ drivers/mtd/mtk-snand/mtk-snand-ids.c | 511 +++++++ - drivers/mtd/mtk-snand/mtk-snand-mtd.c | 526 ++++++++ + drivers/mtd/mtk-snand/mtk-snand-mtd.c | 535 +++++++ drivers/mtd/mtk-snand/mtk-snand-os.c | 39 + drivers/mtd/mtk-snand/mtk-snand-os.h | 120 ++ - drivers/mtd/mtk-snand/mtk-snand.c | 1776 +++++++++++++++++++++++++ - drivers/mtd/mtk-snand/mtk-snand.h | 77 ++ - 12 files changed, 3615 insertions(+) + drivers/mtd/mtk-snand/mtk-snand.c | 1933 +++++++++++++++++++++++++ + drivers/mtd/mtk-snand/mtk-snand.h | 77 + + 12 files changed, 3917 insertions(+) create mode 100644 drivers/mtd/mtk-snand/Kconfig create mode 100644 drivers/mtd/mtk-snand/Makefile create mode 100644 drivers/mtd/mtk-snand/mtk-snand-def.h @@ -95,7 +95,7 @@ Signed-off-by: Weijie Gao +ccflags-y += -DPRIVATE_MTK_SNAND_HEADER --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand-def.h -@@ -0,0 +1,266 @@ +@@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. @@ -213,6 +213,9 @@ Signed-off-by: Weijie Gao + + const uint8_t *spare_sizes; + uint32_t num_spare_size; ++ ++ uint16_t latch_lat; ++ uint16_t sample_delay; +}; + +enum mtk_ecc_regs { @@ -272,6 +275,7 @@ Signed-off-by: Weijie Gao + + uint8_t *page_cache; /* Used by read/write page */ + uint8_t *buf_cache; /* Used by block bad/markbad & auto_oob */ ++ int *sect_bf; /* Used by ECC correction */ +}; + +enum mtk_snand_log_category { @@ -290,7 +294,8 @@ Signed-off-by: Weijie Gao +int mtk_snand_ecc_decoder_start(struct mtk_snand *snf); +void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf); +int mtk_ecc_wait_decoder_done(struct mtk_snand *snf); -+int mtk_ecc_check_decode_error(struct mtk_snand *snf, uint32_t page); ++int mtk_ecc_check_decode_error(struct mtk_snand *snf); ++int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect); + +int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, + uint8_t *in, uint32_t inlen); @@ -364,7 +369,7 @@ Signed-off-by: Weijie Gao +#endif /* _MTK_SNAND_DEF_H_ */ --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand-ecc.c -@@ -0,0 +1,264 @@ +@@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. @@ -599,7 +604,7 @@ Signed-off-by: Weijie Gao + return ret; +} + -+int mtk_ecc_check_decode_error(struct mtk_snand *snf, uint32_t page) ++int mtk_ecc_check_decode_error(struct mtk_snand *snf) +{ + uint32_t i, regi, fi, errnum; + uint32_t errnum_shift = snf->ecc_soc->errnum_shift; @@ -612,23 +617,154 @@ Signed-off-by: Weijie Gao + + errnum = ecc_read32(snf, ECC_DECENUM(regi)); + errnum = (errnum >> (fi * errnum_shift)) & errnum_mask; -+ if (!errnum) -+ continue; + + if (errnum <= snf->ecc_strength) { -+ if (ret >= 0) -+ ret += errnum; -+ continue; ++ snf->sect_bf[i] = errnum; ++ } else { ++ snf->sect_bf[i] = -1; ++ ret = -EBADMSG; + } -+ -+ snand_log_ecc(snf->pdev, -+ "Uncorrectable bitflips in page %u sect %u\n", -+ page, i); -+ ret = -EBADMSG; + } + + return ret; +} ++ ++static int mtk_ecc_check_buf_bitflips(struct mtk_snand *snf, const void *buf, ++ size_t len, uint32_t bitflips) ++{ ++ const uint8_t *buf8 = buf; ++ const uint32_t *buf32; ++ uint32_t d, weight; ++ ++ while (len && ((uintptr_t)buf8) % sizeof(uint32_t)) { ++ weight = hweight8(*buf8); ++ bitflips += BITS_PER_BYTE - weight; ++ buf8++; ++ len--; ++ ++ if (bitflips > snf->ecc_strength) ++ return -EBADMSG; ++ } ++ ++ buf32 = (const uint32_t *)buf8; ++ while (len >= sizeof(uint32_t)) { ++ d = *buf32; ++ ++ if (d != ~0) { ++ weight = hweight32(d); ++ bitflips += sizeof(uint32_t) * BITS_PER_BYTE - weight; ++ } ++ ++ buf32++; ++ len -= sizeof(uint32_t); ++ ++ if (bitflips > snf->ecc_strength) ++ return -EBADMSG; ++ } ++ ++ buf8 = (const uint8_t *)buf32; ++ while (len) { ++ weight = hweight8(*buf8); ++ bitflips += BITS_PER_BYTE - weight; ++ buf8++; ++ len--; ++ ++ if (bitflips > snf->ecc_strength) ++ return -EBADMSG; ++ } ++ ++ return bitflips; ++} ++ ++static int mtk_ecc_check_parity_bitflips(struct mtk_snand *snf, const void *buf, ++ uint32_t bits, uint32_t bitflips) ++{ ++ uint32_t len, i; ++ uint8_t b; ++ int rc; ++ ++ len = bits >> 3; ++ bits &= 7; ++ ++ rc = mtk_ecc_check_buf_bitflips(snf, buf, len, bitflips); ++ if (!bits || rc < 0) ++ return rc; ++ ++ bitflips = rc; ++ ++ /* We want a precise count of bits */ ++ b = ((const uint8_t *)buf)[len]; ++ for (i = 0; i < bits; i++) { ++ if (!(b & BIT(i))) ++ bitflips++; ++ } ++ ++ if (bitflips > snf->ecc_strength) ++ return -EBADMSG; ++ ++ return bitflips; ++} ++ ++static void mtk_ecc_reset_parity(void *buf, uint32_t bits) ++{ ++ uint32_t len; ++ ++ len = bits >> 3; ++ bits &= 7; ++ ++ memset(buf, 0xff, len); ++ ++ /* Only reset bits protected by ECC to 1 */ ++ if (bits) ++ ((uint8_t *)buf)[len] |= GENMASK(bits - 1, 0); ++} ++ ++int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect) ++{ ++ uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ uint8_t *oob = snf->page_cache + snf->writesize; ++ uint8_t *data_ptr, *fdm_ptr, *ecc_ptr; ++ int bitflips = 0, ecc_bits, parity_bits; ++ ++ parity_bits = fls(snf->nfi_soc->sector_size * 8); ++ ecc_bits = snf->ecc_strength * parity_bits; ++ ++ data_ptr = snf->page_cache + sect * snf->nfi_soc->sector_size; ++ fdm_ptr = oob + sect * snf->nfi_soc->fdm_size; ++ ecc_ptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size + ++ sect * ecc_bytes; ++ ++ /* ++ * Check whether DATA + FDM + ECC of a sector contains correctable ++ * bitflips ++ */ ++ bitflips = mtk_ecc_check_buf_bitflips(snf, data_ptr, ++ snf->nfi_soc->sector_size, ++ bitflips); ++ if (bitflips < 0) ++ return -EBADMSG; ++ ++ bitflips = mtk_ecc_check_buf_bitflips(snf, fdm_ptr, ++ snf->nfi_soc->fdm_ecc_size, ++ bitflips); ++ if (bitflips < 0) ++ return -EBADMSG; ++ ++ bitflips = mtk_ecc_check_parity_bitflips(snf, ecc_ptr, ecc_bits, ++ bitflips); ++ if (bitflips < 0) ++ return -EBADMSG; ++ ++ if (!bitflips) ++ return 0; ++ ++ /* Reset the data of this sector to 0xff */ ++ memset(data_ptr, 0xff, snf->nfi_soc->sector_size); ++ memset(fdm_ptr, 0xff, snf->nfi_soc->fdm_ecc_size); ++ mtk_ecc_reset_parity(ecc_ptr, ecc_bits); ++ ++ return bitflips; ++} --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand-ids.c @@ -0,0 +1,511 @@ @@ -1145,7 +1281,7 @@ Signed-off-by: Weijie Gao +} --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c -@@ -0,0 +1,524 @@ +@@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. @@ -1220,13 +1356,12 @@ Signed-off-by: Weijie Gao + start_addr += mtd->erasesize; + } + -+ if (ret) -+ instr->state = MTD_ERASE_FAILED; -+ else ++ if (!ret) { + instr->state = MTD_ERASE_DONE; -+ -+ if (ret) ++ } else { ++ instr->state = MTD_ERASE_FAILED; + ret = -EIO; ++ } + + return ret; +} @@ -1238,8 +1373,8 @@ Signed-off-by: Weijie Gao + size_t len, ooblen, maxooblen, chklen; + uint32_t col, ooboffs; + uint8_t *datcache, *oobcache; -+ bool raw = ops->mode == MTD_OPS_RAW ? true : false; -+ int ret; ++ bool ecc_failed = false, raw = ops->mode == MTD_OPS_RAW ? true : false; ++ int ret, max_bitflips = 0; + + col = addr & mtd->writesize_mask; + addr &= ~mtd->writesize_mask; @@ -1264,9 +1399,20 @@ Signed-off-by: Weijie Gao + ret = mtk_snand_read_page(msm->snf, addr, datcache, + oobcache, raw); + -+ if (ret < 0) ++ if (ret < 0 && ret != -EBADMSG) + return ret; + ++ if (ret == -EBADMSG) { ++ mtd->ecc_stats.failed++; ++ ecc_failed = true; ++ } else { ++ mtd->ecc_stats.corrected += ret; ++ max_bitflips = max_t(int, ret, max_bitflips); ++ } ++ ++ mtd->ecc_stats.corrected += ret; ++ max_bitflips = max_t(int, ret, max_bitflips); ++ + if (len) { + /* Move data */ + chklen = mtd->writesize - col; @@ -1296,7 +1442,7 @@ Signed-off-by: Weijie Gao + addr += mtd->writesize; + } + -+ return 0; ++ return ecc_failed ? -EBADMSG : max_bitflips; +} + +static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from, @@ -1620,7 +1766,7 @@ Signed-off-by: Weijie Gao + + mtd->ooblayout = &mtk_snand_ooblayout; + -+ mtd->ecc_strength = msm->cinfo.ecc_strength * msm->cinfo.num_sectors; ++ mtd->ecc_strength = msm->cinfo.ecc_strength; + mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4; + mtd->ecc_step_size = msm->cinfo.sector_size; + @@ -1659,6 +1805,7 @@ Signed-off-by: Weijie Gao +static const struct udevice_id mtk_snand_ids[] = { + { .compatible = "mediatek,mt7622-snand", .data = SNAND_SOC_MT7622 }, + { .compatible = "mediatek,mt7629-snand", .data = SNAND_SOC_MT7629 }, ++ { .compatible = "mediatek,mt7981-snand", .data = SNAND_SOC_MT7981 }, + { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 }, + { /* sentinel */ }, +}; @@ -1837,7 +1984,7 @@ Signed-off-by: Weijie Gao +#endif /* _MTK_SNAND_OS_H_ */ --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand.c -@@ -0,0 +1,1776 @@ +@@ -0,0 +1,1933 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. @@ -1897,8 +2044,16 @@ Signed-off-by: Weijie Gao +#define FIFO_WR_REMAIN_S 8 +#define FIFO_RD_REMAIN_S 0 + ++#define NFI_ADDRCNTR 0x070 ++#define SEC_CNTR GENMASK(16, 12) ++#define SEC_CNTR_S 12 ++#define NFI_SEC_CNTR(val) (((val) & SEC_CNTR) >> SEC_CNTR_S) ++ +#define NFI_STRADDR 0x080 + ++#define NFI_BYTELEN 0x084 ++#define BUS_SEC_CNTR(val) (((val) & SEC_CNTR) >> SEC_CNTR_S) ++ +#define NFI_FDM0L 0x0a0 +#define NFI_FDM0M 0x0a4 +#define NFI_FDML(n) (NFI_FDM0L + (n) * 8) @@ -1915,6 +2070,7 @@ Signed-off-by: Weijie Gao +#define NFI_MASTERSTA_MASK_7622 (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY) +#define AHB_BUS_BUSY BIT(1) +#define BUS_BUSY BIT(0) ++#define NFI_MASTERSTA_MASK_7981 (AHB_BUS_BUSY | BUS_BUSY) +#define NFI_MASTERSTA_MASK_7986 (AHB_BUS_BUSY | BUS_BUSY) + +/* SNFI registers */ @@ -1950,6 +2106,8 @@ Signed-off-by: Weijie Gao +#define DATA_READ_MODE_X4 2 +#define DATA_READ_MODE_DUAL 5 +#define DATA_READ_MODE_QUAD 6 ++#define LATCH_LAT_S 8 ++#define LATCH_LAT GENMASK(9, 8) +#define PG_LOAD_CUSTOM_EN BIT(7) +#define DATARD_CUSTOM_EN BIT(6) +#define CS_DESELECT_CYC_S 0 @@ -1977,6 +2135,11 @@ Signed-off-by: Weijie Gao + +static const uint8_t mt7622_spare_sizes[] = { 16, 26, 27, 28 }; + ++static const uint8_t mt7981_spare_sizes[] = { ++ 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, ++ 67, 74 ++}; ++ +static const uint8_t mt7986_spare_sizes[] = { + 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, + 67, 74 @@ -1993,7 +2156,9 @@ Signed-off-by: Weijie Gao + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) ++ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 + }, + [SNAND_SOC_MT7629] = { + .sector_size = 512, @@ -2005,7 +2170,23 @@ Signed-off-by: Weijie Gao + .empty_page_check = false, + .mastersta_mask = NFI_MASTERSTA_MASK_7622, + .spare_sizes = mt7622_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) ++ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 ++ }, ++ [SNAND_SOC_MT7981] = { ++ .sector_size = 1024, ++ .max_sectors = 16, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 64, ++ .bbm_swap = true, ++ .empty_page_check = true, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7981, ++ .spare_sizes = mt7981_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7981_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 + }, + [SNAND_SOC_MT7986] = { + .sector_size = 1024, @@ -2017,7 +2198,9 @@ Signed-off-by: Weijie Gao + .empty_page_check = true, + .mastersta_mask = NFI_MASTERSTA_MASK_7986, + .spare_sizes = mt7986_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes) ++ .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 + }, +}; + @@ -2189,7 +2372,7 @@ Signed-off-by: Weijie Gao + snand_log_snfi(snf->pdev, "Failed to reset SNFI MAC\n"); + + nfi_write32(snf, SNF_MISC_CTL, (2 << FIFO_RD_LTC_S) | -+ (10 << CS_DESELECT_CYC_S)); ++ (10 << CS_DESELECT_CYC_S) | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); + + return ret; +} @@ -2485,9 +2668,77 @@ Signed-off-by: Weijie Gao + } +} + ++static int mtk_snand_read_ecc_parity(struct mtk_snand *snf, uint32_t page, ++ uint32_t sect, uint8_t *oob) ++{ ++ uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ uint32_t coladdr, raw_offs, offs; ++ uint8_t op[4]; ++ ++ if (sizeof(op) + ecc_bytes > SNF_GPRAM_SIZE) { ++ snand_log_snfi(snf->pdev, ++ "ECC parity size does not fit the GPRAM\n"); ++ return -ENOTSUPP; ++ } ++ ++ raw_offs = sect * snf->raw_sector_size + snf->nfi_soc->sector_size + ++ snf->nfi_soc->fdm_size; ++ offs = snf->ecc_steps * snf->nfi_soc->fdm_size + sect * ecc_bytes; ++ ++ /* Column address with plane bit */ ++ coladdr = raw_offs | mtk_snand_get_plane_address(snf, page); ++ ++ op[0] = SNAND_CMD_READ_FROM_CACHE; ++ op[1] = (coladdr >> 8) & 0xff; ++ op[2] = coladdr & 0xff; ++ op[3] = 0; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), oob + offs, ecc_bytes); ++} ++ ++static int mtk_snand_check_ecc_result(struct mtk_snand *snf, uint32_t page) ++{ ++ uint8_t *oob = snf->page_cache + snf->writesize; ++ int i, rc, ret = 0, max_bitflips = 0; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ if (snf->sect_bf[i] >= 0) { ++ if (snf->sect_bf[i] > max_bitflips) ++ max_bitflips = snf->sect_bf[i]; ++ continue; ++ } ++ ++ rc = mtk_snand_read_ecc_parity(snf, page, i, oob); ++ if (rc) ++ return rc; ++ ++ rc = mtk_ecc_fixup_empty_sector(snf, i); ++ if (rc < 0) { ++ ret = -EBADMSG; ++ ++ snand_log_ecc(snf->pdev, ++ "Uncorrectable bitflips in page %u sect %u\n", ++ page, i); ++ } else if (rc) { ++ snf->sect_bf[i] = rc; ++ ++ if (snf->sect_bf[i] > max_bitflips) ++ max_bitflips = snf->sect_bf[i]; ++ ++ snand_log_ecc(snf->pdev, ++ "%u bitflip%s corrected in page %u sect %u\n", ++ rc, rc > 1 ? "s" : "", page, i); ++ } else { ++ snf->sect_bf[i] = 0; ++ } ++ } ++ ++ return ret ? ret : max_bitflips; ++} ++ +static int mtk_snand_read_cache(struct mtk_snand *snf, uint32_t page, bool raw) +{ -+ uint32_t coladdr, rwbytes, mode, len; ++ uint32_t coladdr, rwbytes, mode, len, val; + uintptr_t dma_addr; + int ret; + @@ -2507,7 +2758,8 @@ Signed-off-by: Weijie Gao + + /* Set read mode */ + mode = (uint32_t)snf->mode_rfc << DATA_READ_MODE_S; -+ nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE, mode | DATARD_CUSTOM_EN); ++ nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE, ++ mode | DATARD_CUSTOM_EN | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); + + /* Set bytes to read */ + rwbytes = snf->ecc_steps * snf->raw_sector_size; @@ -2555,6 +2807,26 @@ Signed-off-by: Weijie Gao + goto cleanup; + } + ++ /* Wait for BUS_SEC_CNTR returning expected value */ ++ ret = read32_poll_timeout(snf->nfi_base + NFI_BYTELEN, val, ++ BUS_SEC_CNTR(val) >= snf->ecc_steps, ++ 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "Timed out waiting for BUS_SEC_CNTR\n"); ++ goto cleanup; ++ } ++ ++ /* Wait for bus becoming idle */ ++ ret = read32_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val, ++ !(val & snf->nfi_soc->mastersta_mask), ++ 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "Timed out waiting for bus becoming idle\n"); ++ goto cleanup; ++ } ++ + if (!raw) { + ret = mtk_ecc_wait_decoder_done(snf); + if (ret) @@ -2562,17 +2834,10 @@ Signed-off-by: Weijie Gao + + mtk_snand_read_fdm(snf, snf->page_cache + snf->writesize); + -+ /* -+ * For new IPs, ecc error may occur on empty pages. -+ * Use an specific indication bit to check empty page. -+ */ -+ if (snf->nfi_soc->empty_page_check && -+ (nfi_read32(snf, NFI_STA) & READ_EMPTY)) -+ ret = 0; -+ else -+ ret = mtk_ecc_check_decode_error(snf, page); -+ ++ mtk_ecc_check_decode_error(snf); + mtk_snand_ecc_decoder_stop(snf); ++ ++ ret = mtk_snand_check_ecc_result(snf, page); + } + +cleanup: @@ -2581,6 +2846,7 @@ Signed-off-by: Weijie Gao + + /* Stop read */ + nfi_write32(snf, NFI_CON, 0); ++ nfi_write16(snf, NFI_CNFG, 0); + + /* Clear SNF done flag */ + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE); @@ -2590,7 +2856,7 @@ Signed-off-by: Weijie Gao + nfi_read32(snf, NFI_INTR_STA); + nfi_write32(snf, NFI_INTR_EN, 0); + -+ nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN, 0); ++ nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN | LATCH_LAT, 0); + + return ret; +} @@ -2626,12 +2892,14 @@ Signed-off-by: Weijie Gao + void *buf, void *oob, bool raw, bool format) +{ + uint64_t die_addr; -+ uint32_t page; -+ int ret; ++ uint32_t page, dly_ctrl3; ++ int ret, retry_cnt = 0; + + die_addr = mtk_snand_select_die_address(snf, addr); + page = die_addr >> snf->writesize_shift; + ++ dly_ctrl3 = nfi_read32(snf, SNF_DLY_CTL3); ++ + ret = mtk_snand_page_op(snf, page, SNAND_CMD_READ_TO_CACHE); + if (ret) + return ret; @@ -2642,10 +2910,30 @@ Signed-off-by: Weijie Gao + return ret; + } + ++retry: + ret = mtk_snand_read_cache(snf, page, raw); + if (ret < 0 && ret != -EBADMSG) + return ret; + ++ if (ret == -EBADMSG && retry_cnt < 16) { ++ nfi_write32(snf, SNF_DLY_CTL3, retry_cnt * 2); ++ retry_cnt++; ++ goto retry; ++ } ++ ++ if (retry_cnt) { ++ if(ret == -EBADMSG) { ++ nfi_write32(snf, SNF_DLY_CTL3, dly_ctrl3); ++ snand_log_chip(snf->pdev, ++ "NFI calibration failed. Original sample delay: 0x%x\n", ++ dly_ctrl3); ++ } else { ++ snand_log_chip(snf->pdev, ++ "NFI calibration passed. New sample delay: 0x%x\n", ++ nfi_read32(snf, SNF_DLY_CTL3)); ++ } ++ } ++ + if (raw) { + if (format) { + mtk_snand_bm_swap_raw(snf); @@ -2719,7 +3007,7 @@ Signed-off-by: Weijie Gao +static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page, + bool raw) +{ -+ uint32_t coladdr, rwbytes, mode, len; ++ uint32_t coladdr, rwbytes, mode, len, val; + uintptr_t dma_addr; + int ret; + @@ -2789,6 +3077,16 @@ Signed-off-by: Weijie Gao + goto cleanup; + } + ++ /* Wait for NFI_SEC_CNTR returning expected value */ ++ ret = read32_poll_timeout(snf->nfi_base + NFI_ADDRCNTR, val, ++ NFI_SEC_CNTR(val) >= snf->ecc_steps, ++ 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "Timed out waiting for BUS_SEC_CNTR\n"); ++ goto cleanup; ++ } ++ + if (!raw) + mtk_snand_ecc_encoder_stop(snf); + @@ -2797,7 +3095,8 @@ Signed-off-by: Weijie Gao + dma_mem_unmap(snf->pdev, dma_addr, len, true); + + /* Stop write */ -+ nfi_write16(snf, NFI_CON, 0); ++ nfi_write32(snf, NFI_CON, 0); ++ nfi_write16(snf, NFI_CNFG, 0); + + /* Clear SNF done flag */ + nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE); @@ -3454,7 +3753,7 @@ Signed-off-by: Weijie Gao + + /* Tuning options */ + nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN); -+ nfi_write32(snf, SNF_DLY_CTL3, (40 << SFCK_SAM_DLY_S)); ++ nfi_write32(snf, SNF_DLY_CTL3, (snf->nfi_soc->sample_delay << SFCK_SAM_DLY_S)); + + /* Interrupts */ + nfi_read32(snf, NFI_INTR_STA); @@ -3523,8 +3822,8 @@ Signed-off-by: Weijie Gao + struct mtk_snand **psnf) +{ + const struct snand_flash_info *snand_info; ++ uint32_t rawpage_size, sect_bf_size; + struct mtk_snand tmpsnf, *snf; -+ uint32_t rawpage_size; + int ret; + + if (!pdata || !psnf) @@ -3565,14 +3864,19 @@ Signed-off-by: Weijie Gao + rawpage_size = snand_info->memorg.pagesize + + snand_info->memorg.sparesize; + ++ sect_bf_size = mtk_snand_socs[pdata->soc].max_sectors * ++ sizeof(*snf->sect_bf); ++ + /* Allocate memory for instance and cache */ -+ snf = generic_mem_alloc(dev, sizeof(*snf) + rawpage_size); ++ snf = generic_mem_alloc(dev, ++ sizeof(*snf) + rawpage_size + sect_bf_size); + if (!snf) { + snand_log_chip(dev, "Failed to allocate memory for instance\n"); + return -ENOMEM; + } + -+ snf->buf_cache = (uint8_t *)((uintptr_t)snf + sizeof(*snf)); ++ snf->sect_bf = (int *)((uintptr_t)snf + sizeof(*snf)); ++ snf->buf_cache = (uint8_t *)((uintptr_t)snf->sect_bf + sect_bf_size); + + /* Allocate memory for DMA buffer */ + snf->page_cache = dma_mem_alloc(dev, rawpage_size); @@ -3636,8 +3940,8 @@ Signed-off-by: Weijie Gao +enum mtk_snand_soc { + SNAND_SOC_MT7622, + SNAND_SOC_MT7629, ++ SNAND_SOC_MT7981, + SNAND_SOC_MT7986, -+ + __SNAND_SOC_MAX +}; + diff --git a/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch similarity index 93% rename from package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch rename to package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch index 4a06acc2dc1..ac56638ff74 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch +++ b/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch @@ -1,7 +1,7 @@ -From b7fb0e0674db12bcf53df4b107a17c80758ee5d3 Mon Sep 17 00:00:00 2001 +From a347e374cb338213632c6dde88dd226d64bd8b27 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Wed, 3 Mar 2021 08:57:29 +0800 -Subject: [PATCH 05/12] mtd: mtk-snand: add support for SPL +Subject: [PATCH 37/71] mtd: mtk-snand: add support for SPL Add support to initialize SPI-NAND in SPL. Add implementation for SPL NAND loader. @@ -10,8 +10,8 @@ Signed-off-by: Weijie Gao --- drivers/mtd/mtk-snand/Kconfig | 6 ++ drivers/mtd/mtk-snand/Makefile | 4 + - drivers/mtd/mtk-snand/mtk-snand-spl.c | 132 ++++++++++++++++++++++++++ - 3 files changed, 142 insertions(+) + drivers/mtd/mtk-snand/mtk-snand-spl.c | 133 ++++++++++++++++++++++++++ + 3 files changed, 143 insertions(+) create mode 100644 drivers/mtd/mtk-snand/mtk-snand-spl.c --- a/drivers/mtd/mtk-snand/Kconfig @@ -39,7 +39,7 @@ Signed-off-by: Weijie Gao ccflags-y += -DPRIVATE_MTK_SNAND_HEADER --- /dev/null +++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c -@@ -0,0 +1,132 @@ +@@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. @@ -162,6 +162,7 @@ Signed-off-by: Weijie Gao +static const struct udevice_id mtk_snand_ids[] = { + { .compatible = "mediatek,mt7622-snand", .data = SNAND_SOC_MT7622 }, + { .compatible = "mediatek,mt7629-snand", .data = SNAND_SOC_MT7629 }, ++ { .compatible = "mediatek,mt7981-snand", .data = SNAND_SOC_MT7981 }, + { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 }, + { /* sentinel */ }, +}; diff --git a/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch b/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch similarity index 98% rename from package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch rename to package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch index f2e91671fbf..9d6beb3ebfb 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch +++ b/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch @@ -1,7 +1,7 @@ -From a26620ec83fa3077f0c261046e82091f7455736f Mon Sep 17 00:00:00 2001 +From efc3e6f5d29f87a433b42f15a0b87e04b7cd498d Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Wed, 3 Mar 2021 10:11:32 +0800 -Subject: [PATCH 06/12] env: add support for generic MTD device +Subject: [PATCH 38/71] env: add support for generic MTD device Add an env driver for generic MTD device. diff --git a/package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch b/package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch new file mode 100644 index 00000000000..1c62130cf62 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch @@ -0,0 +1,44 @@ +From d26a789c451068caf4bbb4d1ac7bc1f592b5493e Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:58:06 +0800 +Subject: [PATCH 39/71] mtd: add a new mtd device type for NMBM + +This patch adds a new mtd device type for NMBM so that mtdparts can be +correctly probed. And this also gives us an opportunity to add NMBM support +for filesystems in the future. + +Signed-off-by: Weijie Gao +--- + cmd/mtdparts.c | 3 +++ + include/jffs2/load_kernel.h | 4 +++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +--- a/cmd/mtdparts.c ++++ b/cmd/mtdparts.c +@@ -1060,6 +1060,9 @@ int mtd_id_parse(const char *id, const c + } else if (strncmp(p, "spi-nand", 8) == 0) { + *dev_type = MTD_DEV_TYPE_SPINAND; + p += 8; ++ } else if (strncmp(p, "nmbm", 4) == 0) { ++ *dev_type = MTD_DEV_TYPE_NMBM; ++ p += 4; + } else { + printf("incorrect device type in %s\n", id); + return 1; +--- a/include/jffs2/load_kernel.h ++++ b/include/jffs2/load_kernel.h +@@ -16,11 +16,13 @@ + #define MTD_DEV_TYPE_NAND 0x0002 + #define MTD_DEV_TYPE_ONENAND 0x0004 + #define MTD_DEV_TYPE_SPINAND 0x0008 ++#define MTD_DEV_TYPE_NMBM 0x0010 + + #define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" : \ + (type == MTD_DEV_TYPE_NOR ? "nor" : \ + (type == MTD_DEV_TYPE_ONENAND ? "onenand" : \ +- "spi-nand"))) \ ++ (type == MTD_DEV_TYPE_SPINAND ? "spi-nand" : \ ++ "nmbm")))) \ + + struct mtd_device { + struct list_head link; diff --git a/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch b/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch new file mode 100644 index 00000000000..9f80deb137c --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch @@ -0,0 +1,3422 @@ +From 690479081fb6a0c0f77f10fb457ad69e71390f15 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:26:35 +0800 +Subject: [PATCH 40/71] mtd: add core facility code of NMBM + +This patch adds a NAND bad block management named NMBM (NAND mapping block +management) which supports using a mapping table to deal with bad blocks +before factory shipping and during use. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 1 + + drivers/mtd/nmbm/Kconfig | 29 + + drivers/mtd/nmbm/Makefile | 5 + + drivers/mtd/nmbm/nmbm-core.c | 2936 +++++++++++++++++++++++++++++++ + drivers/mtd/nmbm/nmbm-debug.h | 37 + + drivers/mtd/nmbm/nmbm-debug.inl | 39 + + drivers/mtd/nmbm/nmbm-private.h | 137 ++ + include/nmbm/nmbm-os.h | 66 + + include/nmbm/nmbm.h | 102 ++ + 10 files changed, 3354 insertions(+) + create mode 100644 drivers/mtd/nmbm/Kconfig + create mode 100644 drivers/mtd/nmbm/Makefile + create mode 100644 drivers/mtd/nmbm/nmbm-core.c + create mode 100644 drivers/mtd/nmbm/nmbm-debug.h + create mode 100644 drivers/mtd/nmbm/nmbm-debug.inl + create mode 100644 drivers/mtd/nmbm/nmbm-private.h + create mode 100644 include/nmbm/nmbm-os.h + create mode 100644 include/nmbm/nmbm.h + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -174,4 +174,6 @@ source "drivers/mtd/spi/Kconfig" + + source "drivers/mtd/ubi/Kconfig" + ++source "drivers/mtd/nmbm/Kconfig" ++ + endmenu +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -41,3 +41,4 @@ obj-$(CONFIG_SPL_UBI) += ubispl/ + endif + + obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/ ++obj-$(CONFIG_NMBM) += nmbm/ +--- /dev/null ++++ b/drivers/mtd/nmbm/Kconfig +@@ -0,0 +1,29 @@ ++ ++config NMBM ++ bool "Enable NAND mapping block management" ++ default n ++ ++choice ++ prompt "Default log level" ++ depends on NMBM ++ default NMBM_LOG_LEVEL_INFO ++ ++config NMBM_LOG_LEVEL_DEBUG ++ bool "0 - Debug" ++ ++config NMBM_LOG_LEVEL_INFO ++ bool "1 - Info" ++ ++config NMBM_LOG_LEVEL_WARN ++ bool "2 - Warn" ++ ++config NMBM_LOG_LEVEL_ERR ++ bool "3 - Error" ++ ++config NMBM_LOG_LEVEL_EMERG ++ bool "4 - Emergency" ++ ++config NMBM_LOG_LEVEL_NONE ++ bool "5 - None" ++ ++endchoice +--- /dev/null ++++ b/drivers/mtd/nmbm/Makefile +@@ -0,0 +1,5 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# (C) Copyright 2020 MediaTek Inc. All rights reserved. ++ ++obj-$(CONFIG_NMBM) += nmbm-core.o +--- /dev/null ++++ b/drivers/mtd/nmbm/nmbm-core.c +@@ -0,0 +1,2936 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "nmbm-private.h" ++ ++#include "nmbm-debug.h" ++ ++#define NMBM_VER_MAJOR 1 ++#define NMBM_VER_MINOR 0 ++#define NMBM_VER NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \ ++ NMBM_VER_MINOR) ++ ++#define NMBM_ALIGN(v, a) (((v) + (a) - 1) & ~((a) - 1)) ++ ++/*****************************************************************************/ ++/* Logging related functions */ ++/*****************************************************************************/ ++ ++/* ++ * nmbm_log_lower - Print log using OS specific routine ++ * @nld: NMBM lower device structure ++ * @level: log level ++ * @fmt: format string ++ */ ++static void nmbm_log_lower(struct nmbm_lower_device *nld, ++ enum nmbm_log_category level, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (!nld->logprint) ++ return; ++ ++ va_start(ap, fmt); ++ nld->logprint(nld->arg, level, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * nmbm_log - Print log using OS specific routine ++ * @ni: NMBM instance structure ++ * @level: log level ++ * @fmt: format string ++ */ ++static void nmbm_log(struct nmbm_instance *ni, enum nmbm_log_category level, ++ const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (!ni) ++ return; ++ ++ if (!ni->lower.logprint || level < ni->log_display_level) ++ return; ++ ++ va_start(ap, fmt); ++ ni->lower.logprint(ni->lower.arg, level, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * nmbm_set_log_level - Set log display level ++ * @ni: NMBM instance structure ++ * @level: log display level ++ */ ++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni, ++ enum nmbm_log_category level) ++{ ++ enum nmbm_log_category old; ++ ++ if (!ni) ++ return __NMBM_LOG_MAX; ++ ++ old = ni->log_display_level; ++ ni->log_display_level = level; ++ return old; ++} ++ ++/* ++ * nlog_table_creation - Print log of table creation event ++ * @ni: NMBM instance structure ++ * @main_table: whether the table is main info table ++ * @start_ba: start block address of the table ++ * @end_ba: block address after the end of the table ++ */ ++static void nlog_table_creation(struct nmbm_instance *ni, bool main_table, ++ uint32_t start_ba, uint32_t end_ba) ++{ ++ if (start_ba == end_ba - 1) ++ nlog_info(ni, "%s info table has been written to block %u\n", ++ main_table ? "Main" : "Backup", start_ba); ++ else ++ nlog_info(ni, "%s info table has been written to block %u-%u\n", ++ main_table ? "Main" : "Backup", start_ba, end_ba - 1); ++ ++ nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); ++} ++ ++/* ++ * nlog_table_update - Print log of table update event ++ * @ni: NMBM instance structure ++ * @main_table: whether the table is main info table ++ * @start_ba: start block address of the table ++ * @end_ba: block address after the end of the table ++ */ ++static void nlog_table_update(struct nmbm_instance *ni, bool main_table, ++ uint32_t start_ba, uint32_t end_ba) ++{ ++ if (start_ba == end_ba - 1) ++ nlog_debug(ni, "%s info table has been updated in block %u\n", ++ main_table ? "Main" : "Backup", start_ba); ++ else ++ nlog_debug(ni, "%s info table has been updated in block %u-%u\n", ++ main_table ? "Main" : "Backup", start_ba, end_ba - 1); ++ ++ nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); ++} ++ ++/* ++ * nlog_table_found - Print log of table found event ++ * @ni: NMBM instance structure ++ * @first_table: whether the table is first found info table ++ * @write_count: write count of the info table ++ * @start_ba: start block address of the table ++ * @end_ba: block address after the end of the table ++ */ ++static void nlog_table_found(struct nmbm_instance *ni, bool first_table, ++ uint32_t write_count, uint32_t start_ba, ++ uint32_t end_ba) ++{ ++ if (start_ba == end_ba - 1) ++ nlog_info(ni, "%s info table with writecount %u found in block %u\n", ++ first_table ? "First" : "Second", write_count, ++ start_ba); ++ else ++ nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n", ++ first_table ? "First" : "Second", write_count, ++ start_ba, end_ba - 1); ++ ++ nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1); ++} ++ ++/*****************************************************************************/ ++/* Address conversion functions */ ++/*****************************************************************************/ ++ ++/* ++ * addr2ba - Convert a linear address to block address ++ * @ni: NMBM instance structure ++ * @addr: Linear address ++ */ ++static uint32_t addr2ba(struct nmbm_instance *ni, uint64_t addr) ++{ ++ return addr >> ni->erasesize_shift; ++} ++ ++/* ++ * ba2addr - Convert a block address to linear address ++ * @ni: NMBM instance structure ++ * @ba: Block address ++ */ ++static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba) ++{ ++ return (uint64_t)ba << ni->erasesize_shift; ++} ++/* ++ * size2blk - Get minimum required blocks for storing specific size of data ++ * @ni: NMBM instance structure ++ * @size: size for storing ++ */ ++static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size) ++{ ++ return (size + ni->lower.erasesize - 1) >> ni->erasesize_shift; ++} ++ ++/*****************************************************************************/ ++/* High level NAND chip APIs */ ++/*****************************************************************************/ ++ ++/* ++ * nmbm_reset_chip - Reset NAND device ++ * @nld: Lower NAND chip structure ++ */ ++static void nmbm_reset_chip(struct nmbm_instance *ni) ++{ ++ if (ni->lower.reset_chip) ++ ni->lower.reset_chip(ni->lower.arg); ++} ++ ++/* ++ * nmbm_read_phys_page - Read page with retry ++ * @ni: NMBM instance structure ++ * @addr: linear address where the data will be read from ++ * @data: the main data to be read ++ * @oob: the oob data to be read ++ * @mode: mode for processing oob data ++ * ++ * Read a page for at most NMBM_TRY_COUNT times. ++ * ++ * Return 0 for success, positive value for corrected bitflip count, ++ * -EBADMSG for ecc error, other negative values for other errors ++ */ ++static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr, ++ void *data, void *oob, enum nmbm_oob_mode mode) ++{ ++ int tries, ret; ++ ++ for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { ++ ret = ni->lower.read_page(ni->lower.arg, addr, data, oob, mode); ++ if (ret >= 0) ++ return ret; ++ ++ nmbm_reset_chip(ni); ++ } ++ ++ if (ret != -EBADMSG) ++ nlog_err(ni, "Page read failed at address 0x%08llx\n", addr); ++ ++ return ret; ++} ++ ++/* ++ * nmbm_write_phys_page - Write page with retry ++ * @ni: NMBM instance structure ++ * @addr: linear address where the data will be written to ++ * @data: the main data to be written ++ * @oob: the oob data to be written ++ * @mode: mode for processing oob data ++ * ++ * Write a page for at most NMBM_TRY_COUNT times. ++ */ ++static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr, ++ const void *data, const void *oob, ++ enum nmbm_oob_mode mode) ++{ ++ int tries, ret; ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) { ++ nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); ++ return false; ++ } ++ ++ for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { ++ ret = ni->lower.write_page(ni->lower.arg, addr, data, oob, mode); ++ if (!ret) ++ return true; ++ ++ nmbm_reset_chip(ni); ++ } ++ ++ nlog_err(ni, "Page write failed at address 0x%08llx\n", addr); ++ ++ return false; ++} ++ ++/* ++ * nmbm_erase_phys_block - Erase a block with retry ++ * @ni: NMBM instance structure ++ * @addr: Linear address ++ * ++ * Erase a block for at most NMBM_TRY_COUNT times. ++ */ ++static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr) ++{ ++ int tries, ret; ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) { ++ nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); ++ return false; ++ } ++ ++ for (tries = 0; tries < NMBM_TRY_COUNT; tries++) { ++ ret = ni->lower.erase_block(ni->lower.arg, addr); ++ if (!ret) ++ return true; ++ ++ nmbm_reset_chip(ni); ++ } ++ ++ nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr); ++ ++ return false; ++} ++ ++/* ++ * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB ++ * @ni: NMBM instance structure ++ * @ba: block address ++ */ ++static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba) ++{ ++ uint64_t addr = ba2addr(ni, ba); ++ int ret; ++ ++ if (ni->lower.is_bad_block) ++ return ni->lower.is_bad_block(ni->lower.arg, addr); ++ ++ /* Treat ECC error as read success */ ++ ret = nmbm_read_phys_page(ni, addr, NULL, ++ ni->page_cache + ni->lower.writesize, ++ NMBM_MODE_RAW); ++ if (ret < 0 && ret != -EBADMSG) ++ return true; ++ ++ return ni->page_cache[ni->lower.writesize] != 0xff; ++} ++ ++/* ++ * nmbm_mark_phys_bad_block - Mark a block bad ++ * @ni: NMBM instance structure ++ * @addr: Linear address ++ */ ++static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba) ++{ ++ uint64_t addr = ba2addr(ni, ba); ++ enum nmbm_log_category level; ++ uint32_t off; ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) { ++ nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr); ++ return false; ++ } ++ ++ nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr); ++ ++ if (ni->lower.mark_bad_block) ++ return ni->lower.mark_bad_block(ni->lower.arg, addr); ++ ++ /* Whole page set to 0x00 */ ++ memset(ni->page_cache, 0, ni->rawpage_size); ++ ++ /* Write to all pages within this block, disable all errors */ ++ level = nmbm_set_log_level(ni, __NMBM_LOG_MAX); ++ ++ for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { ++ nmbm_write_phys_page(ni, addr + off, ni->page_cache, ++ ni->page_cache + ni->lower.writesize, ++ NMBM_MODE_RAW); ++ } ++ ++ nmbm_set_log_level(ni, level); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++/* NMBM related functions */ ++/*****************************************************************************/ ++ ++/* ++ * nmbm_check_header - Check whether a NMBM structure is valid ++ * @data: pointer to a NMBM structure with a NMBM header at beginning ++ * @size: Size of the buffer pointed by @header ++ * ++ * The size of the NMBM structure may be larger than NMBM header, ++ * e.g. block mapping table and block state table. ++ */ ++static bool nmbm_check_header(const void *data, uint32_t size) ++{ ++ const struct nmbm_header *header = data; ++ struct nmbm_header nhdr; ++ uint32_t new_checksum; ++ ++ /* ++ * Make sure expected structure size is equal or smaller than ++ * buffer size. ++ */ ++ if (header->size > size) ++ return false; ++ ++ memcpy(&nhdr, data, sizeof(nhdr)); ++ ++ nhdr.checksum = 0; ++ new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr)); ++ if (header->size > sizeof(nhdr)) ++ new_checksum = nmbm_crc32(new_checksum, ++ (const uint8_t *)data + sizeof(nhdr), ++ header->size - sizeof(nhdr)); ++ ++ if (header->checksum != new_checksum) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * nmbm_update_checksum - Update checksum of a NMBM structure ++ * @header: pointer to a NMBM structure with a NMBM header at beginning ++ * ++ * The size of the NMBM structure must be specified by @header->size ++ */ ++static void nmbm_update_checksum(struct nmbm_header *header) ++{ ++ header->checksum = 0; ++ header->checksum = nmbm_crc32(0, header, header->size); ++} ++ ++/* ++ * nmbm_get_spare_block_count - Calculate number of blocks should be reserved ++ * @block_count: number of blocks of data ++ * ++ * Calculate number of blocks should be reserved for data ++ */ ++static uint32_t nmbm_get_spare_block_count(uint32_t block_count) ++{ ++ uint32_t val; ++ ++ val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV; ++ val *= NMBM_SPARE_BLOCK_MULTI; ++ ++ if (val < NMBM_SPARE_BLOCK_MIN) ++ val = NMBM_SPARE_BLOCK_MIN; ++ ++ return val; ++} ++ ++/* ++ * nmbm_get_block_state_raw - Get state of a block from raw block state table ++ * @block_state: pointer to raw block state table (bitmap) ++ * @ba: block address ++ */ ++static uint32_t nmbm_get_block_state_raw(nmbm_bitmap_t *block_state, ++ uint32_t ba) ++{ ++ uint32_t unit, shift; ++ ++ unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; ++ shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; ++ ++ return (block_state[unit] >> shift) & BLOCK_ST_MASK; ++} ++ ++/* ++ * nmbm_get_block_state - Get state of a block from block state table ++ * @ni: NMBM instance structure ++ * @ba: block address ++ */ ++static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba) ++{ ++ return nmbm_get_block_state_raw(ni->block_state, ba); ++} ++ ++/* ++ * nmbm_set_block_state - Set state of a block to block state table ++ * @ni: NMBM instance structure ++ * @ba: block address ++ * @state: block state ++ * ++ * Set state of a block. If the block state changed, ni->block_state_changed ++ * will be increased. ++ */ ++static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t state) ++{ ++ uint32_t unit, shift, orig; ++ nmbm_bitmap_t uv; ++ ++ unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT; ++ shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK; ++ ++ orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK; ++ state &= BLOCK_ST_MASK; ++ ++ uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift)); ++ uv |= state << shift; ++ ni->block_state[unit] = uv; ++ ++ if (state == BLOCK_ST_BAD) ++ nmbm_mark_block_color_bad(ni, ba); ++ ++ if (orig != state) { ++ ni->block_state_changed++; ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr. ++ * @ni: NMBM instance structure ++ * @ba: start physical block address ++ * @nba: return physical block address after walk ++ * @count: number of good blocks to be skipped ++ * @limit: highest block address allowed for walking ++ * ++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and ++ * return the next good block address. ++ * ++ * If no enough good blocks counted while @limit reached, false will be returned. ++ * ++ * If @count == 0, nearest good block address will be returned. ++ * @limit is not counted in walking. ++ */ ++static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t *nba, uint32_t count, ++ uint32_t limit) ++{ ++ int32_t nblock = count; ++ ++ if (limit >= ni->block_count) ++ limit = ni->block_count - 1; ++ ++ while (ba < limit) { ++ if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) ++ nblock--; ++ ++ if (nblock < 0) { ++ *nba = ba; ++ return true; ++ } ++ ++ ba++; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr ++ * @ni: NMBM instance structure ++ * @ba: start physical block address ++ * @nba: return physical block address after walk ++ * @count: number of good blocks to be skipped ++ * @limit: lowest block address allowed for walking ++ * ++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and ++ * return the next good block address. ++ * ++ * If no enough good blocks counted while @limit reached, false will be returned. ++ * ++ * If @count == 0, nearest good block address will be returned. ++ * @limit is not counted in walking. ++ */ ++static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t *nba, uint32_t count, uint32_t limit) ++{ ++ int32_t nblock = count; ++ ++ if (limit >= ni->block_count) ++ limit = ni->block_count - 1; ++ ++ while (ba > limit) { ++ if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD) ++ nblock--; ++ ++ if (nblock < 0) { ++ *nba = ba; ++ return true; ++ } ++ ++ ba--; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_block_walk - Skip specified number of good blocks from curr. block addr ++ * @ni: NMBM instance structure ++ * @ascending: whether to walk ascending ++ * @ba: start physical block address ++ * @nba: return physical block address after walk ++ * @count: number of good blocks to be skipped ++ * @limit: highest/lowest block address allowed for walking ++ * ++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and ++ * return the next good block address. ++ * ++ * If no enough good blocks counted while @limit reached, false will be returned. ++ * ++ * If @count == 0, nearest good block address will be returned. ++ * @limit can be set to negative if no limit required. ++ * @limit is not counted in walking. ++ */ ++static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending, ++ uint32_t ba, uint32_t *nba, int32_t count, ++ int32_t limit) ++{ ++ if (ascending) ++ return nmbm_block_walk_asc(ni, ba, nba, count, limit); ++ ++ return nmbm_block_walk_desc(ni, ba, nba, count, limit); ++} ++ ++/* ++ * nmbm_scan_badblocks - Scan and record all bad blocks ++ * @ni: NMBM instance structure ++ * ++ * Scan the entire lower NAND chip and record all bad blocks in to block state ++ * table. ++ */ ++static void nmbm_scan_badblocks(struct nmbm_instance *ni) ++{ ++ uint32_t ba; ++ ++ for (ba = 0; ba < ni->block_count; ba++) { ++ if (nmbm_check_bad_phys_block(ni, ba)) { ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ nlog_info(ni, "Bad block %u [0x%08llx]\n", ba, ++ ba2addr(ni, ba)); ++ } ++ } ++} ++ ++/* ++ * nmbm_build_mapping_table - Build initial block mapping table ++ * @ni: NMBM instance structure ++ * ++ * The initial mapping table will be compatible with the stratage of ++ * factory production. ++ */ ++static void nmbm_build_mapping_table(struct nmbm_instance *ni) ++{ ++ uint32_t pb, lb; ++ ++ for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) { ++ if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) ++ continue; ++ ++ /* Always map to the next good block */ ++ ni->block_mapping[lb++] = pb; ++ } ++ ++ ni->data_block_count = lb; ++ ++ /* Unusable/Management blocks */ ++ for (pb = lb; pb < ni->block_count; pb++) ++ ni->block_mapping[pb] = -1; ++} ++ ++/* ++ * nmbm_erase_block_and_check - Erase a block and check its usability ++ * @ni: NMBM instance structure ++ * @ba: block address to be erased ++ * ++ * Erase a block anc check its usability ++ * ++ * Return true if the block is usable, false if erasure failure or the block ++ * has too many bitflips. ++ */ ++static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba) ++{ ++ uint64_t addr, off; ++ bool success; ++ int ret; ++ ++ success = nmbm_erase_phys_block(ni, ba2addr(ni, ba)); ++ if (!success) ++ return false; ++ ++ if (!(ni->lower.flags & NMBM_F_EMPTY_PAGE_ECC_OK)) ++ return true; ++ ++ /* Check every page to make sure there aren't too many bitflips */ ++ ++ addr = ba2addr(ni, ba); ++ ++ for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { ++ WATCHDOG_RESET(); ++ ++ ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL, ++ NMBM_MODE_PLACE_OOB); ++ if (ret == -EBADMSG) { ++ /* ++ * NMBM_F_EMPTY_PAGE_ECC_OK means the empty page is ++ * still protected by ECC. So reading pages with ECC ++ * enabled and -EBADMSG means there are too many ++ * bitflips that can't be recovered, and the block ++ * containing the page should be marked bad. ++ */ ++ nlog_err(ni, ++ "Too many bitflips in empty page at 0x%llx\n", ++ addr + off); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_erase_range - Erase a range of blocks ++ * @ni: NMBM instance structure ++ * @ba: block address where the erasure will start ++ * @limit: top block address allowed for erasure ++ * ++ * Erase blocks within the specific range. Newly-found bad blocks will be ++ * marked. ++ * ++ * @limit is not counted into the allowed erasure address. ++ */ ++static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t limit) ++{ ++ bool success; ++ ++ while (ba < limit) { ++ WATCHDOG_RESET(); ++ ++ if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) ++ goto next_block; ++ ++ /* Insurance to detect unexpected bad block marked by user */ ++ if (nmbm_check_bad_phys_block(ni, ba)) { ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ goto next_block; ++ } ++ ++ success = nmbm_erase_block_and_check(ni, ba); ++ if (success) ++ goto next_block; ++ ++ nmbm_mark_phys_bad_block(ni, ba); ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ ++ next_block: ++ ba++; ++ } ++} ++ ++/* ++ * nmbm_write_repeated_data - Write critical data to a block with retry ++ * @ni: NMBM instance structure ++ * @ba: block address where the data will be written to ++ * @data: the data to be written ++ * @size: size of the data ++ * ++ * Write data to every page of the block. Success only if all pages within ++ * this block have been successfully written. ++ * ++ * Make sure data size is not bigger than one page. ++ * ++ * This function will write and verify every page for at most ++ * NMBM_TRY_COUNT times. ++ */ ++static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba, ++ const void *data, uint32_t size) ++{ ++ uint64_t addr, off; ++ bool success; ++ int ret; ++ ++ if (size > ni->lower.writesize) ++ return false; ++ ++ addr = ba2addr(ni, ba); ++ ++ for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) { ++ WATCHDOG_RESET(); ++ ++ /* Prepare page data. fill 0xff to unused region */ ++ memcpy(ni->page_cache, data, size); ++ memset(ni->page_cache + size, 0xff, ni->rawpage_size - size); ++ ++ success = nmbm_write_phys_page(ni, addr + off, ni->page_cache, ++ NULL, NMBM_MODE_PLACE_OOB); ++ if (!success) ++ return false; ++ ++ /* Verify the data just written. ECC error indicates failure */ ++ ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL, ++ NMBM_MODE_PLACE_OOB); ++ if (ret < 0) ++ return false; ++ ++ if (memcmp(ni->page_cache, data, size)) ++ return false; ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_write_signature - Write signature to NAND chip ++ * @ni: NMBM instance structure ++ * @limit: top block address allowed for writing ++ * @signature: the signature to be written ++ * @signature_ba: the actual block address where signature is written to ++ * ++ * Write signature within a specific range, from chip bottom to limit. ++ * At most one block will be written. ++ * ++ * @limit is not counted into the allowed write address. ++ */ ++static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit, ++ const struct nmbm_signature *signature, ++ uint32_t *signature_ba) ++{ ++ uint32_t ba = ni->block_count - 1; ++ bool success; ++ ++ while (ba > limit) { ++ WATCHDOG_RESET(); ++ ++ if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) ++ goto next_block; ++ ++ /* Insurance to detect unexpected bad block marked by user */ ++ if (nmbm_check_bad_phys_block(ni, ba)) { ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ goto next_block; ++ } ++ ++ success = nmbm_erase_block_and_check(ni, ba); ++ if (!success) ++ goto skip_bad_block; ++ ++ success = nmbm_write_repeated_data(ni, ba, signature, ++ sizeof(*signature)); ++ if (success) { ++ *signature_ba = ba; ++ return true; ++ } ++ ++ skip_bad_block: ++ nmbm_mark_phys_bad_block(ni, ba); ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ ++ next_block: ++ ba--; ++ }; ++ ++ return false; ++} ++ ++/* ++ * nmbn_read_data - Read data ++ * @ni: NMBM instance structure ++ * @addr: linear address where the data will be read from ++ * @data: the data to be read ++ * @size: the size of data ++ * ++ * Read data range. ++ * Every page will be tried for at most NMBM_TRY_COUNT times. ++ * ++ * Return 0 for success, positive value for corrected bitflip count, ++ * -EBADMSG for ecc error, other negative values for other errors ++ */ ++static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data, ++ uint32_t size) ++{ ++ uint64_t off = addr; ++ uint8_t *ptr = data; ++ uint32_t sizeremain = size, chunksize, leading; ++ int ret; ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & ni->writesize_mask; ++ chunksize = ni->lower.writesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ if (chunksize == ni->lower.writesize) { ++ ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL, ++ NMBM_MODE_PLACE_OOB); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = nmbm_read_phys_page(ni, off - leading, ++ ni->page_cache, NULL, ++ NMBM_MODE_PLACE_OOB); ++ if (ret < 0) ++ return ret; ++ ++ memcpy(ptr, ni->page_cache + leading, chunksize); ++ } ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ return 0; ++} ++ ++/* ++ * nmbn_write_verify_data - Write data with validation ++ * @ni: NMBM instance structure ++ * @addr: linear address where the data will be written to ++ * @data: the data to be written ++ * @size: the size of data ++ * ++ * Write data and verify. ++ * Every page will be tried for at most NMBM_TRY_COUNT times. ++ */ ++static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr, ++ const void *data, uint32_t size) ++{ ++ uint64_t off = addr; ++ const uint8_t *ptr = data; ++ uint32_t sizeremain = size, chunksize, leading; ++ bool success; ++ int ret; ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & ni->writesize_mask; ++ chunksize = ni->lower.writesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ /* Prepare page data. fill 0xff to unused region */ ++ memset(ni->page_cache, 0xff, ni->rawpage_size); ++ memcpy(ni->page_cache + leading, ptr, chunksize); ++ ++ success = nmbm_write_phys_page(ni, off - leading, ++ ni->page_cache, NULL, ++ NMBM_MODE_PLACE_OOB); ++ if (!success) ++ return false; ++ ++ /* Verify the data just written. ECC error indicates failure */ ++ ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache, ++ NULL, NMBM_MODE_PLACE_OOB); ++ if (ret < 0) ++ return false; ++ ++ if (memcmp(ni->page_cache + leading, ptr, chunksize)) ++ return false; ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_write_mgmt_range - Write management data into NAND within a range ++ * @ni: NMBM instance structure ++ * @addr: preferred start block address for writing ++ * @limit: highest block address allowed for writing ++ * @data: the data to be written ++ * @size: the size of data ++ * @actual_start_ba: actual start block address of data ++ * @actual_end_ba: block address after the end of data ++ * ++ * @limit is not counted into the allowed write address. ++ */ ++static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t limit, const void *data, ++ uint32_t size, uint32_t *actual_start_ba, ++ uint32_t *actual_end_ba) ++{ ++ const uint8_t *ptr = data; ++ uint32_t sizeremain = size, chunksize; ++ bool success; ++ ++ while (sizeremain && ba < limit) { ++ WATCHDOG_RESET(); ++ ++ chunksize = sizeremain; ++ if (chunksize > ni->lower.erasesize) ++ chunksize = ni->lower.erasesize; ++ ++ if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) ++ goto next_block; ++ ++ /* Insurance to detect unexpected bad block marked by user */ ++ if (nmbm_check_bad_phys_block(ni, ba)) { ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ goto next_block; ++ } ++ ++ success = nmbm_erase_block_and_check(ni, ba); ++ if (!success) ++ goto skip_bad_block; ++ ++ success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr, ++ chunksize); ++ if (!success) ++ goto skip_bad_block; ++ ++ if (sizeremain == size) ++ *actual_start_ba = ba; ++ ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ ++ goto next_block; ++ ++ skip_bad_block: ++ nmbm_mark_phys_bad_block(ni, ba); ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ ++ next_block: ++ ba++; ++ } ++ ++ if (sizeremain) ++ return false; ++ ++ *actual_end_ba = ba; ++ ++ return true; ++} ++ ++/* ++ * nmbm_generate_info_table_cache - Generate info table cache data ++ * @ni: NMBM instance structure ++ * ++ * Generate info table cache data to be written into flash. ++ */ ++static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni) ++{ ++ bool changed = false; ++ ++ memset(ni->info_table_cache, 0xff, ni->info_table_size); ++ ++ memcpy(ni->info_table_cache + ni->info_table.state_table_off, ++ ni->block_state, ni->state_table_size); ++ ++ memcpy(ni->info_table_cache + ni->info_table.mapping_table_off, ++ ni->block_mapping, ni->mapping_table_size); ++ ++ ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE; ++ ni->info_table.header.version = NMBM_VER; ++ ni->info_table.header.size = ni->info_table_size; ++ ++ if (ni->block_state_changed || ni->block_mapping_changed) { ++ ni->info_table.write_count++; ++ changed = true; ++ } ++ ++ memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table)); ++ ++ nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache); ++ ++ return changed; ++} ++ ++/* ++ * nmbm_write_info_table - Write info table into NAND within a range ++ * @ni: NMBM instance structure ++ * @ba: preferred start block address for writing ++ * @limit: highest block address allowed for writing ++ * @actual_start_ba: actual start block address of info table ++ * @actual_end_ba: block address after the end of info table ++ * ++ * @limit is counted into the allowed write address. ++ */ ++static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t limit, uint32_t *actual_start_ba, ++ uint32_t *actual_end_ba) ++{ ++ return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache, ++ ni->info_table_size, actual_start_ba, ++ actual_end_ba); ++} ++ ++/* ++ * nmbm_mark_tables_clean - Mark info table `clean' ++ * @ni: NMBM instance structure ++ */ ++static void nmbm_mark_tables_clean(struct nmbm_instance *ni) ++{ ++ ni->block_state_changed = 0; ++ ni->block_mapping_changed = 0; ++} ++ ++/* ++ * nmbm_try_reserve_blocks - Reserve blocks with compromisation ++ * @ni: NMBM instance structure ++ * @ba: start physical block address ++ * @nba: return physical block address after reservation ++ * @count: number of good blocks to be skipped ++ * @min_count: minimum number of good blocks to be skipped ++ * @limit: highest/lowest block address allowed for walking ++ * ++ * Reserve specific blocks. If failed, try to reserve as many as possible. ++ */ ++static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t *nba, uint32_t count, ++ int32_t min_count, int32_t limit) ++{ ++ int32_t nblocks = count; ++ bool success; ++ ++ while (nblocks >= min_count) { ++ success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit); ++ if (success) ++ return true; ++ ++ nblocks--; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_rebuild_info_table - Build main & backup info table from scratch ++ * @ni: NMBM instance structure ++ * @allow_no_gap: allow no spare blocks between two tables ++ */ ++static bool nmbm_rebuild_info_table(struct nmbm_instance *ni) ++{ ++ uint32_t table_start_ba, table_end_ba, next_start_ba; ++ uint32_t main_table_end_ba; ++ bool success; ++ ++ /* Set initial value */ ++ ni->main_table_ba = 0; ++ ni->backup_table_ba = 0; ++ ni->mapping_blocks_ba = ni->mapping_blocks_top_ba; ++ ++ /* Write main table */ ++ success = nmbm_write_info_table(ni, ni->mgmt_start_ba, ++ ni->mapping_blocks_top_ba, ++ &table_start_ba, &table_end_ba); ++ if (!success) { ++ /* Failed to write main table, data will be lost */ ++ nlog_emerg(ni, "Unable to write at least one info table!\n"); ++ nlog_emerg(ni, "Please save your data before power off!\n"); ++ ni->protected = 1; ++ return false; ++ } ++ ++ /* Main info table is successfully written, record its offset */ ++ ni->main_table_ba = table_start_ba; ++ main_table_end_ba = table_end_ba; ++ ++ /* Adjust mapping_blocks_ba */ ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_table_creation(ni, true, table_start_ba, table_end_ba); ++ ++ /* Reserve spare blocks for main info table. */ ++ success = nmbm_try_reserve_blocks(ni, table_end_ba, ++ &next_start_ba, ++ ni->info_table_spare_blocks, 0, ++ ni->mapping_blocks_top_ba - ++ size2blk(ni, ni->info_table_size)); ++ if (!success) { ++ /* There is no spare block. */ ++ nlog_debug(ni, "No room for backup info table\n"); ++ return true; ++ } ++ ++ /* Write backup info table. */ ++ success = nmbm_write_info_table(ni, next_start_ba, ++ ni->mapping_blocks_top_ba, ++ &table_start_ba, &table_end_ba); ++ if (!success) { ++ /* There is no enough blocks for backup table. */ ++ nlog_debug(ni, "No room for backup info table\n"); ++ return true; ++ } ++ ++ /* Backup table is successfully written, record its offset */ ++ ni->backup_table_ba = table_start_ba; ++ ++ /* Adjust mapping_blocks_off */ ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ /* Erase spare blocks of main table to clean possible interference data */ ++ nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); ++ ++ nlog_table_creation(ni, false, table_start_ba, table_end_ba); ++ ++ return true; ++} ++ ++/* ++ * nmbm_rescue_single_info_table - Rescue when there is only one info table ++ * @ni: NMBM instance structure ++ * ++ * This function is called when there is only one info table exists. ++ * This function may fail if we can't write new info table ++ */ ++static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni) ++{ ++ uint32_t table_start_ba, table_end_ba, write_ba; ++ bool success; ++ ++ /* Try to write new info table in front of existing table */ ++ success = nmbm_write_info_table(ni, ni->mgmt_start_ba, ++ ni->main_table_ba, ++ &table_start_ba, ++ &table_end_ba); ++ if (success) { ++ /* ++ * New table becomes the main table, existing table becomes ++ * the backup table. ++ */ ++ ni->backup_table_ba = ni->main_table_ba; ++ ni->main_table_ba = table_start_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ /* Erase spare blocks of main table to clean possible interference data */ ++ nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba); ++ ++ nlog_table_creation(ni, true, table_start_ba, table_end_ba); ++ ++ return true; ++ } ++ ++ /* Try to reserve spare blocks for existing table */ ++ success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, ++ ni->info_table_spare_blocks, 0, ++ ni->mapping_blocks_top_ba - ++ size2blk(ni, ni->info_table_size)); ++ if (!success) { ++ nlog_warn(ni, "Failed to rescue single info table\n"); ++ return false; ++ } ++ ++ /* Try to write new info table next to the existing table */ ++ while (write_ba >= ni->mapping_blocks_ba) { ++ WATCHDOG_RESET(); ++ ++ success = nmbm_write_info_table(ni, write_ba, ++ ni->mapping_blocks_top_ba, ++ &table_start_ba, ++ &table_end_ba); ++ if (success) ++ break; ++ ++ write_ba--; ++ } ++ ++ if (success) { ++ /* Erase spare blocks of main table to clean possible interference data */ ++ nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba); ++ ++ /* New table becomes the backup table */ ++ ni->backup_table_ba = table_start_ba; ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_table_creation(ni, false, table_start_ba, table_end_ba); ++ ++ return true; ++ } ++ ++ nlog_warn(ni, "Failed to rescue single info table\n"); ++ return false; ++} ++ ++/* ++ * nmbm_update_single_info_table - Update specific one info table ++ * @ni: NMBM instance structure ++ */ ++static bool nmbm_update_single_info_table(struct nmbm_instance *ni, ++ bool update_main_table) ++{ ++ uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba; ++ bool success; ++ ++ /* Determine the write range */ ++ if (update_main_table) { ++ write_start_ba = ni->main_table_ba; ++ write_limit = ni->backup_table_ba; ++ } else { ++ write_start_ba = ni->backup_table_ba; ++ write_limit = ni->mapping_blocks_top_ba; ++ } ++ ++ nmbm_mark_block_color_mgmt(ni, write_start_ba, write_limit - 1); ++ ++ success = nmbm_write_info_table(ni, write_start_ba, write_limit, ++ &table_start_ba, &table_end_ba); ++ if (success) { ++ if (update_main_table) { ++ ni->main_table_ba = table_start_ba; ++ } else { ++ ni->backup_table_ba = table_start_ba; ++ ni->mapping_blocks_ba = table_end_ba; ++ } ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_table_update(ni, update_main_table, table_start_ba, ++ table_end_ba); ++ ++ return true; ++ } ++ ++ if (update_main_table) { ++ /* ++ * If failed to update main table, make backup table the new ++ * main table, and call nmbm_rescue_single_info_table() ++ */ ++ nlog_warn(ni, "Unable to update %s info table\n", ++ update_main_table ? "Main" : "Backup"); ++ ++ ni->main_table_ba = ni->backup_table_ba; ++ ni->backup_table_ba = 0; ++ return nmbm_rescue_single_info_table(ni); ++ } ++ ++ /* Only one table left */ ++ ni->mapping_blocks_ba = ni->backup_table_ba; ++ ni->backup_table_ba = 0; ++ ++ return false; ++} ++ ++/* ++ * nmbm_rescue_main_info_table - Rescue when failed to write main info table ++ * @ni: NMBM instance structure ++ * ++ * This function is called when main info table failed to be written, and ++ * backup info table exists. ++ */ ++static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni) ++{ ++ uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba; ++ uint32_t main_table_end_ba, write_ba; ++ uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size); ++ bool success; ++ ++ /* Try to reserve spare blocks for existing backup info table */ ++ success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba, ++ ni->info_table_spare_blocks, 0, ++ ni->mapping_blocks_top_ba - ++ info_table_erasesize); ++ if (!success) { ++ /* There is no spare block. Backup info table becomes the main table. */ ++ nlog_err(ni, "No room for temporary info table\n"); ++ ni->main_table_ba = ni->backup_table_ba; ++ ni->backup_table_ba = 0; ++ return true; ++ } ++ ++ /* Try to write temporary info table into spare unmapped blocks */ ++ while (write_ba >= ni->mapping_blocks_ba) { ++ WATCHDOG_RESET(); ++ ++ success = nmbm_write_info_table(ni, write_ba, ++ ni->mapping_blocks_top_ba, ++ &tmp_table_start_ba, ++ &tmp_table_end_ba); ++ if (success) ++ break; ++ ++ write_ba--; ++ } ++ ++ if (!success) { ++ /* Backup info table becomes the main table */ ++ nlog_err(ni, "Failed to update main info table\n"); ++ ni->main_table_ba = ni->backup_table_ba; ++ ni->backup_table_ba = 0; ++ return true; ++ } ++ ++ /* Adjust mapping_blocks_off */ ++ ni->mapping_blocks_ba = tmp_table_end_ba; ++ ++ nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba, ++ tmp_table_end_ba - 1); ++ ++ /* ++ * Now write main info table at the beginning of management area. ++ * This operation will generally destroy the original backup info ++ * table. ++ */ ++ success = nmbm_write_info_table(ni, ni->mgmt_start_ba, ++ tmp_table_start_ba, ++ &main_table_start_ba, ++ &main_table_end_ba); ++ if (!success) { ++ /* Temporary info table becomes the main table */ ++ ni->main_table_ba = tmp_table_start_ba; ++ ni->backup_table_ba = 0; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_err(ni, "Failed to update main info table\n"); ++ nmbm_mark_block_color_info_table(ni, tmp_table_start_ba, ++ tmp_table_end_ba - 1); ++ ++ return true; ++ } ++ ++ /* Main info table has been successfully written, record its offset */ ++ ni->main_table_ba = main_table_start_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba); ++ ++ /* ++ * Temporary info table becomes the new backup info table if it's ++ * not overwritten. ++ */ ++ if (main_table_end_ba <= tmp_table_start_ba) { ++ ni->backup_table_ba = tmp_table_start_ba; ++ ++ nlog_table_creation(ni, false, tmp_table_start_ba, ++ tmp_table_end_ba); ++ ++ return true; ++ } ++ ++ /* Adjust mapping_blocks_off */ ++ ni->mapping_blocks_ba = main_table_end_ba; ++ ++ /* Try to reserve spare blocks for new main info table */ ++ success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba, ++ ni->info_table_spare_blocks, 0, ++ ni->mapping_blocks_top_ba - ++ info_table_erasesize); ++ if (!success) { ++ /* There is no spare block. Only main table exists. */ ++ nlog_err(ni, "No room for backup info table\n"); ++ ni->backup_table_ba = 0; ++ return true; ++ } ++ ++ /* Write new backup info table. */ ++ while (write_ba >= main_table_end_ba) { ++ WATCHDOG_RESET(); ++ ++ success = nmbm_write_info_table(ni, write_ba, ++ ni->mapping_blocks_top_ba, ++ &tmp_table_start_ba, ++ &tmp_table_end_ba); ++ if (success) ++ break; ++ ++ write_ba--; ++ } ++ ++ if (!success) { ++ nlog_err(ni, "No room for backup info table\n"); ++ ni->backup_table_ba = 0; ++ return true; ++ } ++ ++ /* Backup info table has been successfully written, record its offset */ ++ ni->backup_table_ba = tmp_table_start_ba; ++ ++ /* Adjust mapping_blocks_off */ ++ ni->mapping_blocks_ba = tmp_table_end_ba; ++ ++ /* Erase spare blocks of main table to clean possible interference data */ ++ nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba); ++ ++ nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba); ++ ++ return true; ++} ++ ++/* ++ * nmbm_update_info_table_once - Update info table once ++ * @ni: NMBM instance structure ++ * @force: force update ++ * ++ * Update both main and backup info table. Return true if at least one info ++ * table has been successfully written. ++ * This function only try to update info table once regard less of the result. ++ */ ++static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force) ++{ ++ uint32_t table_start_ba, table_end_ba; ++ uint32_t main_table_limit; ++ bool success; ++ ++ /* Do nothing if there is no change */ ++ if (!nmbm_generate_info_table_cache(ni) && !force) ++ return true; ++ ++ /* Check whether both two tables exist */ ++ if (!ni->backup_table_ba) { ++ main_table_limit = ni->mapping_blocks_top_ba; ++ goto write_main_table; ++ } ++ ++ nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba, ++ ni->mapping_blocks_ba - 1); ++ ++ /* ++ * Write backup info table in its current range. ++ * Note that limit is set to mapping_blocks_top_off to provide as many ++ * spare blocks as possible for the backup table. If at last ++ * unmapped blocks are used by backup table, mapping_blocks_off will ++ * be adjusted. ++ */ ++ success = nmbm_write_info_table(ni, ni->backup_table_ba, ++ ni->mapping_blocks_top_ba, ++ &table_start_ba, &table_end_ba); ++ if (!success) { ++ /* ++ * There is nothing to do if failed to write backup table. ++ * Write the main table now. ++ */ ++ nlog_err(ni, "No room for backup table\n"); ++ ni->mapping_blocks_ba = ni->backup_table_ba; ++ ni->backup_table_ba = 0; ++ main_table_limit = ni->mapping_blocks_top_ba; ++ goto write_main_table; ++ } ++ ++ /* Backup table is successfully written, record its offset */ ++ ni->backup_table_ba = table_start_ba; ++ ++ /* Adjust mapping_blocks_off */ ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ /* The normal limit of main table */ ++ main_table_limit = ni->backup_table_ba; ++ ++ nlog_table_update(ni, false, table_start_ba, table_end_ba); ++ ++write_main_table: ++ if (!ni->main_table_ba) ++ goto rebuild_tables; ++ ++ if (!ni->backup_table_ba) ++ nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ++ ni->mapping_blocks_ba - 1); ++ else ++ nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ++ ni->backup_table_ba - 1); ++ ++ /* Write main info table in its current range */ ++ success = nmbm_write_info_table(ni, ni->main_table_ba, ++ main_table_limit, &table_start_ba, ++ &table_end_ba); ++ if (!success) { ++ /* If failed to write main table, go rescue procedure */ ++ if (!ni->backup_table_ba) ++ goto rebuild_tables; ++ ++ return nmbm_rescue_main_info_table(ni); ++ } ++ ++ /* Main info table is successfully written, record its offset */ ++ ni->main_table_ba = table_start_ba; ++ ++ /* Adjust mapping_blocks_off */ ++ if (!ni->backup_table_ba) ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ nmbm_mark_tables_clean(ni); ++ ++ nlog_table_update(ni, true, table_start_ba, table_end_ba); ++ ++ return true; ++ ++rebuild_tables: ++ return nmbm_rebuild_info_table(ni); ++} ++ ++/* ++ * nmbm_update_info_table - Update info table ++ * @ni: NMBM instance structure ++ * ++ * Update both main and backup info table. Return true if at least one table ++ * has been successfully written. ++ * This function will try to update info table repeatedly until no new bad ++ * block found during updating. ++ */ ++static bool nmbm_update_info_table(struct nmbm_instance *ni) ++{ ++ bool success; ++ ++ if (ni->protected) ++ return true; ++ ++ while (ni->block_state_changed || ni->block_mapping_changed) { ++ success = nmbm_update_info_table_once(ni, false); ++ if (!success) { ++ nlog_err(ni, "Failed to update info table\n"); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_map_block - Map a bad block to a unused spare block ++ * @ni: NMBM instance structure ++ * @lb: logic block addr to map ++ */ ++static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb) ++{ ++ uint32_t pb; ++ bool success; ++ ++ if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { ++ nlog_warn(ni, "No spare unmapped blocks.\n"); ++ return false; ++ } ++ ++ success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0, ++ ni->mapping_blocks_ba); ++ if (!success) { ++ nlog_warn(ni, "No spare unmapped blocks.\n"); ++ nmbm_update_info_table(ni); ++ ni->mapping_blocks_top_ba = ni->mapping_blocks_ba; ++ return false; ++ } ++ ++ ni->block_mapping[lb] = pb; ++ ni->mapping_blocks_top_ba--; ++ ni->block_mapping_changed++; ++ ++ nlog_info(ni, "Logic block %u mapped to physical blcok %u\n", lb, pb); ++ nmbm_mark_block_color_mapped(ni, pb); ++ ++ return true; ++} ++ ++/* ++ * nmbm_create_info_table - Create info table(s) ++ * @ni: NMBM instance structure ++ * ++ * This function assumes that the chip has no existing info table(s) ++ */ ++static bool nmbm_create_info_table(struct nmbm_instance *ni) ++{ ++ uint32_t lb; ++ bool success; ++ ++ /* Set initial mapping_blocks_top_off */ ++ success = nmbm_block_walk(ni, false, ni->signature_ba, ++ &ni->mapping_blocks_top_ba, 1, ++ ni->mgmt_start_ba); ++ if (!success) { ++ nlog_err(ni, "No room for spare blocks\n"); ++ return false; ++ } ++ ++ /* Generate info table cache */ ++ nmbm_generate_info_table_cache(ni); ++ ++ /* Write info table */ ++ success = nmbm_rebuild_info_table(ni); ++ if (!success) { ++ nlog_err(ni, "Failed to build info tables\n"); ++ return false; ++ } ++ ++ /* Remap bad block(s) at end of data area */ ++ for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) { ++ success = nmbm_map_block(ni, lb); ++ if (!success) ++ break; ++ ++ ni->data_block_count++; ++ } ++ ++ /* If state table and/or mapping table changed, update info table. */ ++ success = nmbm_update_info_table(ni); ++ if (!success) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * nmbm_create_new - Create NMBM on a new chip ++ * @ni: NMBM instance structure ++ */ ++static bool nmbm_create_new(struct nmbm_instance *ni) ++{ ++ bool success; ++ ++ /* Determine the boundary of management blocks */ ++ ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV; ++ ++ if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks) ++ ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks; ++ ++ nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n", ++ ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); ++ nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1); ++ ++ /* Fill block state table & mapping table */ ++ nmbm_scan_badblocks(ni); ++ nmbm_build_mapping_table(ni); ++ ++ /* Write signature */ ++ ni->signature.header.magic = NMBM_MAGIC_SIGNATURE; ++ ni->signature.header.version = NMBM_VER; ++ ni->signature.header.size = sizeof(ni->signature); ++ ni->signature.nand_size = ni->lower.size; ++ ni->signature.block_size = ni->lower.erasesize; ++ ni->signature.page_size = ni->lower.writesize; ++ ni->signature.spare_size = ni->lower.oobsize; ++ ni->signature.mgmt_start_pb = ni->mgmt_start_ba; ++ ni->signature.max_try_count = NMBM_TRY_COUNT; ++ nmbm_update_checksum(&ni->signature.header); ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) { ++ nlog_info(ni, "NMBM has been initialized in read-only mode\n"); ++ return true; ++ } ++ ++ success = nmbm_write_signature(ni, ni->mgmt_start_ba, ++ &ni->signature, &ni->signature_ba); ++ if (!success) { ++ nlog_err(ni, "Failed to write signature to a proper offset\n"); ++ return false; ++ } ++ ++ nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n", ++ ni->signature_ba, ba2addr(ni, ni->signature_ba)); ++ nmbm_mark_block_color_signature(ni, ni->signature_ba); ++ ++ /* Write info table(s) */ ++ success = nmbm_create_info_table(ni); ++ if (success) { ++ nlog_info(ni, "NMBM has been successfully created\n"); ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_check_info_table_header - Check if a info table header is valid ++ * @ni: NMBM instance structure ++ * @data: pointer to the info table header ++ */ ++static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data) ++{ ++ struct nmbm_info_table_header *ifthdr = data; ++ ++ if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE) ++ return false; ++ ++ if (ifthdr->header.size != ni->info_table_size) ++ return false; ++ ++ if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size) ++ return false; ++ ++ if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size) ++ return false; ++ ++ return true; ++} ++ ++/* ++ * nmbm_check_info_table - Check if a whole info table is valid ++ * @ni: NMBM instance structure ++ * @start_ba: start block address of this table ++ * @end_ba: end block address of this table ++ * @data: pointer to the info table header ++ * @mapping_blocks_top_ba: return the block address of top remapped block ++ */ ++static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba, ++ uint32_t end_ba, void *data, ++ uint32_t *mapping_blocks_top_ba) ++{ ++ struct nmbm_info_table_header *ifthdr = data; ++ int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off); ++ nmbm_bitmap_t *block_state = (nmbm_bitmap_t *)((uintptr_t)data + ifthdr->state_table_off); ++ uint32_t minimum_mapping_pb = ni->signature_ba; ++ uint32_t ba; ++ ++ for (ba = 0; ba < ni->data_block_count; ba++) { ++ if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) || ++ block_mapping[ba] == ni->signature_ba) ++ return false; ++ ++ if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb) ++ minimum_mapping_pb = block_mapping[ba]; ++ } ++ ++ for (ba = start_ba; ba < end_ba; ba++) { ++ if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) ++ continue; ++ ++ if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD) ++ return false; ++ } ++ ++ *mapping_blocks_top_ba = minimum_mapping_pb - 1; ++ ++ return true; ++} ++ ++/* ++ * nmbm_try_load_info_table - Try to load info table from a address ++ * @ni: NMBM instance structure ++ * @ba: start block address of the info table ++ * @eba: return the block address after end of the table ++ * @write_count: return the write count of this table ++ * @mapping_blocks_top_ba: return the block address of top remapped block ++ * @table_loaded: used to record whether ni->info_table has valid data ++ */ ++static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t *eba, uint32_t *write_count, ++ uint32_t *mapping_blocks_top_ba, ++ bool table_loaded) ++{ ++ struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache; ++ uint8_t *off = ni->info_table_cache; ++ uint32_t limit = ba + size2blk(ni, ni->info_table_size); ++ uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size; ++ bool success, checkhdr = true; ++ int ret; ++ ++ while (sizeremain && ba < limit) { ++ WATCHDOG_RESET(); ++ ++ if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD) ++ goto next_block; ++ ++ if (nmbm_check_bad_phys_block(ni, ba)) { ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ goto next_block; ++ } ++ ++ chunksize = sizeremain; ++ if (chunksize > ni->lower.erasesize) ++ chunksize = ni->lower.erasesize; ++ ++ /* Assume block with ECC error has no info table data */ ++ ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize); ++ if (ret < 0) ++ goto skip_bad_block; ++ else if (ret > 0) ++ return false; ++ ++ if (checkhdr) { ++ success = nmbm_check_info_table_header(ni, off); ++ if (!success) ++ return false; ++ ++ start_ba = ba; ++ checkhdr = false; ++ } ++ ++ off += chunksize; ++ sizeremain -= chunksize; ++ ++ goto next_block; ++ ++ skip_bad_block: ++ /* Only mark bad in memory */ ++ nmbm_set_block_state(ni, ba, BLOCK_ST_BAD); ++ ++ next_block: ++ ba++; ++ } ++ ++ if (sizeremain) ++ return false; ++ ++ success = nmbm_check_header(ni->info_table_cache, ni->info_table_size); ++ if (!success) ++ return false; ++ ++ *eba = ba; ++ *write_count = ifthdr->write_count; ++ ++ success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache, ++ mapping_blocks_top_ba); ++ if (!success) ++ return false; ++ ++ if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) { ++ memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table)); ++ memcpy(ni->block_state, ++ (uint8_t *)ifthdr + ifthdr->state_table_off, ++ ni->state_table_size); ++ memcpy(ni->block_mapping, ++ (uint8_t *)ifthdr + ifthdr->mapping_table_off, ++ ni->mapping_table_size); ++ ni->info_table.write_count = ifthdr->write_count; ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_search_info_table - Search info table from specific address ++ * @ni: NMBM instance structure ++ * @ba: start block address to search ++ * @limit: highest block address allowed for searching ++ * @table_start_ba: return the start block address of this table ++ * @table_end_ba: return the block address after end of this table ++ * @write_count: return the write count of this table ++ * @mapping_blocks_top_ba: return the block address of top remapped block ++ * @table_loaded: used to record whether ni->info_table has valid data ++ */ ++static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t limit, uint32_t *table_start_ba, ++ uint32_t *table_end_ba, ++ uint32_t *write_count, ++ uint32_t *mapping_blocks_top_ba, ++ bool table_loaded) ++{ ++ bool success; ++ ++ while (ba < limit - size2blk(ni, ni->info_table_size)) { ++ WATCHDOG_RESET(); ++ ++ success = nmbm_try_load_info_table(ni, ba, table_end_ba, ++ write_count, ++ mapping_blocks_top_ba, ++ table_loaded); ++ if (success) { ++ *table_start_ba = ba; ++ return true; ++ } ++ ++ ba++; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_load_info_table - Load info table(s) from a chip ++ * @ni: NMBM instance structure ++ * @ba: start block address to search info table ++ * @limit: highest block address allowed for searching ++ */ ++static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba, ++ uint32_t limit) ++{ ++ uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba; ++ uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba; ++ uint32_t main_table_write_count, backup_table_write_count; ++ uint32_t i; ++ bool success; ++ ++ /* Set initial value */ ++ ni->main_table_ba = 0; ++ ni->backup_table_ba = 0; ++ ni->info_table.write_count = 0; ++ ni->mapping_blocks_top_ba = ni->signature_ba - 1; ++ ni->data_block_count = ni->signature.mgmt_start_pb; ++ ++ /* Find first info table */ ++ success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba, ++ &main_table_end_ba, &main_table_write_count, ++ &main_mapping_blocks_top_ba, false); ++ if (!success) { ++ nlog_warn(ni, "No valid info table found\n"); ++ return false; ++ } ++ ++ table_end_ba = main_table_end_ba; ++ ++ nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba, ++ main_table_end_ba); ++ ++ /* Find second info table */ ++ success = nmbm_search_info_table(ni, main_table_end_ba, limit, ++ &ni->backup_table_ba, &backup_table_end_ba, ++ &backup_table_write_count, &backup_mapping_blocks_top_ba, true); ++ if (!success) { ++ nlog_warn(ni, "Second info table not found\n"); ++ } else { ++ table_end_ba = backup_table_end_ba; ++ ++ nlog_table_found(ni, false, backup_table_write_count, ++ ni->backup_table_ba, backup_table_end_ba); ++ } ++ ++ /* Pick mapping_blocks_top_ba */ ++ if (!ni->backup_table_ba) { ++ ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba; ++ } else { ++ if (main_table_write_count >= backup_table_write_count) ++ ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba; ++ else ++ ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba; ++ } ++ ++ /* Set final mapping_blocks_ba */ ++ ni->mapping_blocks_ba = table_end_ba; ++ ++ /* Set final data_block_count */ ++ for (i = ni->signature.mgmt_start_pb; i > 0; i--) { ++ if (ni->block_mapping[i - 1] >= 0) { ++ ni->data_block_count = i; ++ break; ++ } ++ } ++ ++ /* Debug purpose: mark mapped blocks and bad blocks */ ++ for (i = 0; i < ni->data_block_count; i++) { ++ if (ni->block_mapping[i] > ni->mapping_blocks_top_ba) ++ nmbm_mark_block_color_mapped(ni, ni->block_mapping[i]); ++ } ++ ++ for (i = 0; i < ni->block_count; i++) { ++ if (nmbm_get_block_state(ni, i) == BLOCK_ST_BAD) ++ nmbm_mark_block_color_bad(ni, i); ++ } ++ ++ /* Regenerate the info table cache from the final selected info table */ ++ nmbm_generate_info_table_cache(ni); ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) ++ return true; ++ ++ /* ++ * If only one table exists, try to write another table. ++ * If two tables have different write count, try to update info table ++ */ ++ if (!ni->backup_table_ba) { ++ success = nmbm_rescue_single_info_table(ni); ++ } else if (main_table_write_count != backup_table_write_count) { ++ /* Mark state & mapping tables changed */ ++ ni->block_state_changed = 1; ++ ni->block_mapping_changed = 1; ++ ++ success = nmbm_update_single_info_table(ni, ++ main_table_write_count < backup_table_write_count); ++ } else { ++ success = true; ++ } ++ ++ /* ++ * If there is no spare unmapped blocks, or still only one table ++ * exists, set the chip to read-only ++ */ ++ if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) { ++ nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n"); ++ ni->protected = 1; ++ } else if (!success) { ++ nlog_warn(ni, "Only one info table found. Device is now read-only\n"); ++ ni->protected = 1; ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_load_existing - Load NMBM from a new chip ++ * @ni: NMBM instance structure ++ */ ++static bool nmbm_load_existing(struct nmbm_instance *ni) ++{ ++ bool success; ++ ++ /* Calculate the boundary of management blocks */ ++ ni->mgmt_start_ba = ni->signature.mgmt_start_pb; ++ ++ nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n", ++ ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba)); ++ nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ++ ni->signature_ba - 1); ++ ++ /* Look for info table(s) */ ++ success = nmbm_load_info_table(ni, ni->mgmt_start_ba, ++ ni->signature_ba); ++ if (success) { ++ nlog_info(ni, "NMBM has been successfully attached %s\n", ++ (ni->lower.flags & NMBM_F_READ_ONLY) ? "in read-only mode" : ""); ++ return true; ++ } ++ ++ if (!(ni->lower.flags & NMBM_F_CREATE)) ++ return false; ++ ++ /* Fill block state table & mapping table */ ++ nmbm_scan_badblocks(ni); ++ nmbm_build_mapping_table(ni); ++ ++ if (ni->lower.flags & NMBM_F_READ_ONLY) { ++ nlog_info(ni, "NMBM has been initialized in read-only mode\n"); ++ return true; ++ } ++ ++ /* Write info table(s) */ ++ success = nmbm_create_info_table(ni); ++ if (success) { ++ nlog_info(ni, "NMBM has been successfully created\n"); ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * nmbm_find_signature - Find signature in the lower NAND chip ++ * @ni: NMBM instance structure ++ * @signature_ba: used for storing block address of the signature ++ * @signature_ba: return the actual block address of signature block ++ * ++ * Find a valid signature from a specific range in the lower NAND chip, ++ * from bottom (highest address) to top (lowest address) ++ * ++ * Return true if found. ++ */ ++static bool nmbm_find_signature(struct nmbm_instance *ni, ++ struct nmbm_signature *signature, ++ uint32_t *signature_ba) ++{ ++ struct nmbm_signature sig; ++ uint64_t off, addr; ++ uint32_t block_count, ba, limit; ++ bool success; ++ int ret; ++ ++ /* Calculate top and bottom block address */ ++ block_count = ni->lower.size >> ni->erasesize_shift; ++ ba = block_count; ++ limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->lower.max_ratio); ++ if (ni->lower.max_reserved_blocks && block_count - limit > ni->lower.max_reserved_blocks) ++ limit = block_count - ni->lower.max_reserved_blocks; ++ ++ while (ba >= limit) { ++ WATCHDOG_RESET(); ++ ++ ba--; ++ addr = ba2addr(ni, ba); ++ ++ if (nmbm_check_bad_phys_block(ni, ba)) ++ continue; ++ ++ /* Check every page. ++ * As long as at leaset one page contains valid signature, ++ * the block is treated as a valid signature block. ++ */ ++ for (off = 0; off < ni->lower.erasesize; ++ off += ni->lower.writesize) { ++ WATCHDOG_RESET(); ++ ++ ret = nmbn_read_data(ni, addr + off, &sig, ++ sizeof(sig)); ++ if (ret) ++ continue; ++ ++ /* Check for header size and checksum */ ++ success = nmbm_check_header(&sig, sizeof(sig)); ++ if (!success) ++ continue; ++ ++ /* Check for header magic */ ++ if (sig.header.magic == NMBM_MAGIC_SIGNATURE) { ++ /* Found it */ ++ memcpy(signature, &sig, sizeof(sig)); ++ *signature_ba = ba; ++ return true; ++ } ++ } ++ }; ++ ++ return false; ++} ++ ++/* ++ * is_power_of_2_u64 - Check whether a 64-bit integer is power of 2 ++ * @n: number to check ++ */ ++static bool is_power_of_2_u64(uint64_t n) ++{ ++ return (n != 0 && ((n & (n - 1)) == 0)); ++} ++ ++/* ++ * nmbm_check_lower_members - Validate the members of lower NAND device ++ * @nld: Lower NAND chip structure ++ */ ++static bool nmbm_check_lower_members(struct nmbm_lower_device *nld) ++{ ++ ++ if (!nld->size || !is_power_of_2_u64(nld->size)) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "Chip size %llu is not valid\n", nld->size); ++ return false; ++ } ++ ++ if (!nld->erasesize || !is_power_of_2(nld->erasesize)) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "Block size %u is not valid\n", nld->erasesize); ++ return false; ++ } ++ ++ if (!nld->writesize || !is_power_of_2(nld->writesize)) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "Page size %u is not valid\n", nld->writesize); ++ return false; ++ } ++ ++ if (!nld->oobsize || !is_power_of_2(nld->oobsize)) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "Page spare size %u is not valid\n", nld->oobsize); ++ return false; ++ } ++ ++ if (!nld->read_page) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, "read_page() is required\n"); ++ return false; ++ } ++ ++ if (!(nld->flags & NMBM_F_READ_ONLY) && (!nld->write_page || !nld->erase_block)) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "write_page() and erase_block() are required\n"); ++ return false; ++ } ++ ++ /* Data sanity check */ ++ if (!nld->max_ratio) ++ nld->max_ratio = 1; ++ ++ if (nld->max_ratio >= NMBM_MGMT_DIV - 1) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "max ratio %u is invalid\n", nld->max_ratio); ++ return false; ++ } ++ ++ if (nld->max_reserved_blocks && nld->max_reserved_blocks < NMBM_MGMT_BLOCKS_MIN) { ++ nmbm_log_lower(nld, NMBM_LOG_ERR, ++ "max reserved blocks %u is too small\n", nld->max_reserved_blocks); ++ return false; ++ } ++ ++ return true; ++} ++ ++/* ++ * nmbm_calc_structure_size - Calculate the instance structure size ++ * @nld: NMBM lower device structure ++ */ ++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld) ++{ ++ uint32_t state_table_size, mapping_table_size, info_table_size; ++ uint32_t block_count; ++ ++ block_count = nmbm_lldiv(nld->size, nld->erasesize); ++ ++ /* Calculate info table size */ ++ state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / ++ NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; ++ mapping_table_size = block_count * sizeof(int32_t); ++ ++ info_table_size = NMBM_ALIGN(sizeof(struct nmbm_info_table_header), ++ nld->writesize); ++ info_table_size += NMBM_ALIGN(state_table_size, nld->writesize); ++ info_table_size += NMBM_ALIGN(mapping_table_size, nld->writesize); ++ ++ return info_table_size + state_table_size + mapping_table_size + ++ nld->writesize + nld->oobsize + sizeof(struct nmbm_instance); ++} ++ ++/* ++ * nmbm_init_structure - Initialize members of instance structure ++ * @ni: NMBM instance structure ++ */ ++static void nmbm_init_structure(struct nmbm_instance *ni) ++{ ++ uint32_t pages_per_block, blocks_per_chip; ++ uintptr_t ptr; ++ ++ pages_per_block = ni->lower.erasesize / ni->lower.writesize; ++ blocks_per_chip = nmbm_lldiv(ni->lower.size, ni->lower.erasesize); ++ ++ ni->rawpage_size = ni->lower.writesize + ni->lower.oobsize; ++ ni->rawblock_size = pages_per_block * ni->rawpage_size; ++ ni->rawchip_size = blocks_per_chip * ni->rawblock_size; ++ ++ ni->writesize_mask = ni->lower.writesize - 1; ++ ni->erasesize_mask = ni->lower.erasesize - 1; ++ ++ ni->writesize_shift = ffs(ni->lower.writesize) - 1; ++ ni->erasesize_shift = ffs(ni->lower.erasesize) - 1; ++ ++ /* Calculate number of block this chip */ ++ ni->block_count = ni->lower.size >> ni->erasesize_shift; ++ ++ /* Calculate info table size */ ++ ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) / ++ NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE; ++ ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping); ++ ++ ni->info_table_size = NMBM_ALIGN(sizeof(ni->info_table), ++ ni->lower.writesize); ++ ni->info_table.state_table_off = ni->info_table_size; ++ ++ ni->info_table_size += NMBM_ALIGN(ni->state_table_size, ++ ni->lower.writesize); ++ ni->info_table.mapping_table_off = ni->info_table_size; ++ ++ ni->info_table_size += NMBM_ALIGN(ni->mapping_table_size, ++ ni->lower.writesize); ++ ++ ni->info_table_spare_blocks = nmbm_get_spare_block_count( ++ size2blk(ni, ni->info_table_size)); ++ ++ /* Assign memory to members */ ++ ptr = (uintptr_t)ni + sizeof(*ni); ++ ++ ni->info_table_cache = (void *)ptr; ++ ptr += ni->info_table_size; ++ ++ ni->block_state = (void *)ptr; ++ ptr += ni->state_table_size; ++ ++ ni->block_mapping = (void *)ptr; ++ ptr += ni->mapping_table_size; ++ ++ ni->page_cache = (uint8_t *)ptr; ++ ++ /* Initialize block state table */ ++ ni->block_state_changed = 0; ++ memset(ni->block_state, 0xff, ni->state_table_size); ++ ++ /* Initialize block mapping table */ ++ ni->block_mapping_changed = 0; ++} ++ ++/* ++ * nmbm_attach - Attach to a lower device ++ * @nld: NMBM lower device structure ++ * @ni: NMBM instance structure ++ */ ++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni) ++{ ++ bool success; ++ ++ if (!nld || !ni) ++ return -EINVAL; ++ ++ /* Set default log level */ ++ ni->log_display_level = NMBM_DEFAULT_LOG_LEVEL; ++ ++ /* Check lower members */ ++ success = nmbm_check_lower_members(nld); ++ if (!success) ++ return -EINVAL; ++ ++ /* Initialize NMBM instance */ ++ memcpy(&ni->lower, nld, sizeof(struct nmbm_lower_device)); ++ nmbm_init_structure(ni); ++ ++ success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba); ++ if (!success) { ++ if (!(nld->flags & NMBM_F_CREATE)) { ++ nlog_err(ni, "Signature not found\n"); ++ return -ENODEV; ++ } ++ ++ success = nmbm_create_new(ni); ++ if (!success) ++ return -ENODEV; ++ ++ return 0; ++ } ++ ++ nlog_info(ni, "Signature found at block %u [0x%08llx]\n", ++ ni->signature_ba, ba2addr(ni, ni->signature_ba)); ++ nmbm_mark_block_color_signature(ni, ni->signature_ba); ++ ++ if (ni->signature.header.version != NMBM_VER) { ++ nlog_err(ni, "NMBM version %u.%u is not supported\n", ++ NMBM_VERSION_MAJOR_GET(ni->signature.header.version), ++ NMBM_VERSION_MINOR_GET(ni->signature.header.version)); ++ return -EINVAL; ++ } ++ ++ if (ni->signature.nand_size != nld->size || ++ ni->signature.block_size != nld->erasesize || ++ ni->signature.page_size != nld->writesize || ++ ni->signature.spare_size != nld->oobsize) { ++ nlog_err(ni, "NMBM configuration mismatch\n"); ++ return -EINVAL; ++ } ++ ++ success = nmbm_load_existing(ni); ++ if (!success) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++/* ++ * nmbm_detach - Detach from a lower device, and save all tables ++ * @ni: NMBM instance structure ++ */ ++int nmbm_detach(struct nmbm_instance *ni) ++{ ++ if (!ni) ++ return -EINVAL; ++ ++ if (!(ni->lower.flags & NMBM_F_READ_ONLY)) ++ nmbm_update_info_table(ni); ++ ++ nmbm_mark_block_color_normal(ni, 0, ni->block_count - 1); ++ ++ return 0; ++} ++ ++/* ++ * nmbm_erase_logic_block - Erase a logic block ++ * @ni: NMBM instance structure ++ * @nmbm_erase_logic_block: logic block address ++ * ++ * Logic block will be mapped to physical block before erasing. ++ * Bad block found during erasinh will be remapped to a good block if there is ++ * still at least one good spare block available. ++ */ ++static int nmbm_erase_logic_block(struct nmbm_instance *ni, uint32_t block_addr) ++{ ++ uint32_t pb; ++ bool success; ++ ++retry: ++ /* Map logic block to physical block */ ++ pb = ni->block_mapping[block_addr]; ++ ++ /* Whether the logic block is good (has valid mapping) */ ++ if ((int32_t)pb < 0) { ++ nlog_debug(ni, "Logic block %u is a bad block\n", block_addr); ++ return -EIO; ++ } ++ ++ /* Remap logic block if current physical block is a bad block */ ++ if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD || ++ nmbm_get_block_state(ni, pb) == BLOCK_ST_NEED_REMAP) ++ goto remap_logic_block; ++ ++ /* Insurance to detect unexpected bad block marked by user */ ++ if (nmbm_check_bad_phys_block(ni, pb)) { ++ nlog_warn(ni, "Found unexpected bad block possibly marked by user\n"); ++ nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); ++ goto remap_logic_block; ++ } ++ ++ success = nmbm_erase_block_and_check(ni, pb); ++ if (success) ++ return 0; ++ ++ /* Mark bad block */ ++ nmbm_mark_phys_bad_block(ni, pb); ++ nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); ++ ++remap_logic_block: ++ /* Try to assign a new block */ ++ success = nmbm_map_block(ni, block_addr); ++ if (!success) { ++ /* Mark logic block unusable, and update info table */ ++ ni->block_mapping[block_addr] = -1; ++ if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP) ++ nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); ++ nmbm_update_info_table(ni); ++ return -EIO; ++ } ++ ++ /* Update info table before erasing */ ++ if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP) ++ nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); ++ nmbm_update_info_table(ni); ++ ++ goto retry; ++} ++ ++/* ++ * nmbm_erase_block_range - Erase logic blocks ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @size: erase range ++ * @failed_addr: return failed block address if error occurs ++ */ ++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr, ++ uint64_t size, uint64_t *failed_addr) ++{ ++ uint32_t start_ba, end_ba; ++ int ret; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ if (addr + size > ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Erase range 0xllxu is too large\n", size); ++ return -EINVAL; ++ } ++ ++ if (!size) { ++ nlog_warn(ni, "No blocks to be erased\n"); ++ return 0; ++ } ++ ++ start_ba = addr2ba(ni, addr); ++ end_ba = addr2ba(ni, addr + size - 1); ++ ++ while (start_ba <= end_ba) { ++ WATCHDOG_RESET(); ++ ++ ret = nmbm_erase_logic_block(ni, start_ba); ++ if (ret) { ++ if (failed_addr) ++ *failed_addr = ba2addr(ni, start_ba); ++ return ret; ++ } ++ ++ start_ba++; ++ } ++ ++ return 0; ++} ++ ++/* ++ * nmbm_read_logic_page - Read page based on logic address ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @data: buffer to store main data. optional. ++ * @oob: buffer to store oob data. optional. ++ * @mode: read mode ++ * ++ * Return 0 for success, positive value for corrected bitflip count, ++ * -EBADMSG for ecc error, other negative values for other errors ++ */ ++static int nmbm_read_logic_page(struct nmbm_instance *ni, uint64_t addr, ++ void *data, void *oob, enum nmbm_oob_mode mode) ++{ ++ uint32_t lb, pb, offset; ++ uint64_t paddr; ++ ++ /* Extract block address and in-block offset */ ++ lb = addr2ba(ni, addr); ++ offset = addr & ni->erasesize_mask; ++ ++ /* Map logic block to physical block */ ++ pb = ni->block_mapping[lb]; ++ ++ /* Whether the logic block is good (has valid mapping) */ ++ if ((int32_t)pb < 0) { ++ nlog_debug(ni, "Logic block %u is a bad block\n", lb); ++ return -EIO; ++ } ++ ++ /* Fail if physical block is marked bad */ ++ if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) ++ return -EIO; ++ ++ /* Assemble new address */ ++ paddr = ba2addr(ni, pb) + offset; ++ ++ return nmbm_read_phys_page(ni, paddr, data, oob, mode); ++} ++ ++/* ++ * nmbm_read_single_page - Read one page based on logic address ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @data: buffer to store main data. optional. ++ * @oob: buffer to store oob data. optional. ++ * @mode: read mode ++ * ++ * Return 0 for success, positive value for corrected bitflip count, ++ * -EBADMSG for ecc error, other negative values for other errors ++ */ ++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data, ++ void *oob, enum nmbm_oob_mode mode) ++{ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ return nmbm_read_logic_page(ni, addr, data, oob, mode); ++} ++ ++/* ++ * nmbm_read_range - Read data without oob ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @size: data size to read ++ * @data: buffer to store main data to be read ++ * @mode: read mode ++ * @retlen: return actual data size read ++ * ++ * Return 0 for success, positive value for corrected bitflip count, ++ * -EBADMSG for ecc error, other negative values for other errors ++ */ ++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size, ++ void *data, enum nmbm_oob_mode mode, size_t *retlen) ++{ ++ uint64_t off = addr; ++ uint8_t *ptr = data; ++ size_t sizeremain = size, chunksize, leading; ++ bool has_ecc_err = false; ++ int ret, max_bitflips = 0; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ if (addr + size > ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Read range 0x%llx is too large\n", size); ++ return -EINVAL; ++ } ++ ++ if (!size) { ++ nlog_warn(ni, "No data to be read\n"); ++ return 0; ++ } ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & ni->writesize_mask; ++ chunksize = ni->lower.writesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ if (chunksize == ni->lower.writesize) { ++ ret = nmbm_read_logic_page(ni, off - leading, ptr, ++ NULL, mode); ++ if (ret < 0 && ret != -EBADMSG) ++ break; ++ } else { ++ ret = nmbm_read_logic_page(ni, off - leading, ++ ni->page_cache, NULL, ++ mode); ++ if (ret < 0 && ret != -EBADMSG) ++ break; ++ ++ memcpy(ptr, ni->page_cache + leading, chunksize); ++ } ++ ++ if (ret == -EBADMSG) ++ has_ecc_err = true; ++ ++ if (ret > max_bitflips) ++ max_bitflips = ret; ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ if (retlen) ++ *retlen = size - sizeremain; ++ ++ if (ret < 0 && ret != -EBADMSG) ++ return ret; ++ ++ if (has_ecc_err) ++ return -EBADMSG; ++ ++ return max_bitflips; ++} ++ ++/* ++ * nmbm_write_logic_page - Read page based on logic address ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @data: buffer contains main data. optional. ++ * @oob: buffer contains oob data. optional. ++ * @mode: write mode ++ */ ++static int nmbm_write_logic_page(struct nmbm_instance *ni, uint64_t addr, ++ const void *data, const void *oob, ++ enum nmbm_oob_mode mode) ++{ ++ uint32_t lb, pb, offset; ++ uint64_t paddr; ++ bool success; ++ ++ /* Extract block address and in-block offset */ ++ lb = addr2ba(ni, addr); ++ offset = addr & ni->erasesize_mask; ++ ++ /* Map logic block to physical block */ ++ pb = ni->block_mapping[lb]; ++ ++ /* Whether the logic block is good (has valid mapping) */ ++ if ((int32_t)pb < 0) { ++ nlog_debug(ni, "Logic block %u is a bad block\n", lb); ++ return -EIO; ++ } ++ ++ /* Fail if physical block is marked bad */ ++ if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) ++ return -EIO; ++ ++ /* Assemble new address */ ++ paddr = ba2addr(ni, pb) + offset; ++ ++ success = nmbm_write_phys_page(ni, paddr, data, oob, mode); ++ if (success) ++ return 0; ++ ++ /* ++ * Do not remap bad block here. Just mark this block in state table. ++ * Remap this block on erasing. ++ */ ++ nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP); ++ nmbm_update_info_table(ni); ++ ++ return -EIO; ++} ++ ++/* ++ * nmbm_write_single_page - Write one page based on logic address ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @data: buffer contains main data. optional. ++ * @oob: buffer contains oob data. optional. ++ * @mode: write mode ++ */ ++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr, ++ const void *data, const void *oob, ++ enum nmbm_oob_mode mode) ++{ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ return nmbm_write_logic_page(ni, addr, data, oob, mode); ++} ++ ++/* ++ * nmbm_write_range - Write data without oob ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ * @size: data size to write ++ * @data: buffer contains data to be written ++ * @mode: write mode ++ * @retlen: return actual data size written ++ */ ++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size, ++ const void *data, enum nmbm_oob_mode mode, ++ size_t *retlen) ++{ ++ uint64_t off = addr; ++ const uint8_t *ptr = data; ++ size_t sizeremain = size, chunksize, leading; ++ int ret; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ if (addr + size > ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Write size 0x%zx is too large\n", size); ++ return -EINVAL; ++ } ++ ++ if (!size) { ++ nlog_warn(ni, "No data to be written\n"); ++ return 0; ++ } ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & ni->writesize_mask; ++ chunksize = ni->lower.writesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ if (chunksize == ni->lower.writesize) { ++ ret = nmbm_write_logic_page(ni, off - leading, ptr, ++ NULL, mode); ++ if (ret) ++ break; ++ } else { ++ memset(ni->page_cache, 0xff, leading); ++ memcpy(ni->page_cache + leading, ptr, chunksize); ++ ++ ret = nmbm_write_logic_page(ni, off - leading, ++ ni->page_cache, NULL, ++ mode); ++ if (ret) ++ break; ++ } ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ if (retlen) ++ *retlen = size - sizeremain; ++ ++ return ret; ++} ++ ++/* ++ * nmbm_check_bad_block - Check whether a logic block is usable ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ */ ++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr) ++{ ++ uint32_t lb, pb; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ lb = addr2ba(ni, addr); ++ ++ /* Map logic block to physical block */ ++ pb = ni->block_mapping[lb]; ++ ++ if ((int32_t)pb < 0) ++ return 1; ++ ++ if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD) ++ return 1; ++ ++ return 0; ++} ++ ++/* ++ * nmbm_mark_bad_block - Mark a logic block unusable ++ * @ni: NMBM instance structure ++ * @addr: logic linear address ++ */ ++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr) ++{ ++ uint32_t lb, pb; ++ ++ if (!ni) ++ return -EINVAL; ++ ++ /* Sanity check */ ++ if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) { ++ nlog_debug(ni, "Device is forced read-only\n"); ++ return -EROFS; ++ } ++ ++ if (addr >= ba2addr(ni, ni->data_block_count)) { ++ nlog_err(ni, "Address 0x%llx is invalid\n", addr); ++ return -EINVAL; ++ } ++ ++ lb = addr2ba(ni, addr); ++ ++ /* Map logic block to physical block */ ++ pb = ni->block_mapping[lb]; ++ ++ if ((int32_t)pb < 0) ++ return 0; ++ ++ ni->block_mapping[lb] = -1; ++ nmbm_mark_phys_bad_block(ni, pb); ++ nmbm_set_block_state(ni, pb, BLOCK_ST_BAD); ++ nmbm_update_info_table(ni); ++ ++ return 0; ++} ++ ++/* ++ * nmbm_get_avail_size - Get available user data size ++ * @ni: NMBM instance structure ++ */ ++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni) ++{ ++ if (!ni) ++ return 0; ++ ++ return (uint64_t)ni->data_block_count << ni->erasesize_shift; ++} ++ ++/* ++ * nmbm_get_lower_device - Get lower device structure ++ * @ni: NMBM instance structure ++ * @nld: pointer to hold the data of lower device structure ++ */ ++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld) ++{ ++ if (!ni) ++ return -EINVAL; ++ ++ if (nld) ++ memcpy(nld, &ni->lower, sizeof(*nld)); ++ ++ return 0; ++} ++ ++#include "nmbm-debug.inl" +--- /dev/null ++++ b/drivers/mtd/nmbm/nmbm-debug.h +@@ -0,0 +1,37 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Debug addons for NAND Mapped-block Management (NMBM) ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _NMBM_DEBUG_H_ ++#define _NMBM_DEBUG_H_ ++ ++#include "nmbm-private.h" ++ ++#define nmbm_mark_block_color_normal(ni, start_ba, end_ba) ++#define nmbm_mark_block_color_bad(ni, ba) ++#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba) ++#define nmbm_mark_block_color_signature(ni, ba) ++#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba) ++#define nmbm_mark_block_color_mapped(ni, ba) ++ ++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba); ++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba); ++ ++enum nmmb_block_type { ++ NMBM_BLOCK_GOOD_DATA, ++ NMBM_BLOCK_GOOD_MGMT, ++ NMBM_BLOCK_BAD, ++ NMBM_BLOCK_MAIN_INFO_TABLE, ++ NMBM_BLOCK_BACKUP_INFO_TABLE, ++ NMBM_BLOCK_REMAPPED, ++ NMBM_BLOCK_SIGNATURE, ++ ++ __NMBM_BLOCK_TYPE_MAX ++}; ++ ++#endif /* _NMBM_DEBUG_H_ */ +--- /dev/null ++++ b/drivers/mtd/nmbm/nmbm-debug.inl +@@ -0,0 +1,39 @@ ++ ++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba) ++{ ++ return nmbm_get_block_state(ni, ba); ++} ++ ++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba) ++{ ++ uint32_t eba, limit; ++ bool success; ++ ++ if (nmbm_get_block_state(ni, ba) == BLOCK_ST_BAD) ++ return NMBM_BLOCK_BAD; ++ ++ if (ba < ni->data_block_count) ++ return NMBM_BLOCK_GOOD_DATA; ++ ++ if (ba == ni->signature_ba) ++ return NMBM_BLOCK_SIGNATURE; ++ ++ if (ni->main_table_ba) { ++ limit = ni->backup_table_ba ? ni->backup_table_ba : ++ ni->mapping_blocks_ba; ++ ++ success = nmbm_block_walk_asc(ni, ni->main_table_ba, &eba, ++ size2blk(ni, ni->info_table_size), limit); ++ ++ if (success && ba >= ni->main_table_ba && ba < eba) ++ return NMBM_BLOCK_MAIN_INFO_TABLE; ++ } ++ ++ if (ba >= ni->backup_table_ba && ba < ni->mapping_blocks_ba) ++ return NMBM_BLOCK_BACKUP_INFO_TABLE; ++ ++ if (ba > ni->mapping_blocks_top_ba && ba < ni->signature_ba) ++ return NMBM_BLOCK_REMAPPED; ++ ++ return NMBM_BLOCK_GOOD_MGMT; ++} +--- /dev/null ++++ b/drivers/mtd/nmbm/nmbm-private.h +@@ -0,0 +1,137 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Definitions for NAND Mapped-block Management (NMBM) ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _NMBM_PRIVATE_H_ ++#define _NMBM_PRIVATE_H_ ++ ++#include ++ ++#define NMBM_MAGIC_SIGNATURE 0x304d4d4e /* NMM0 */ ++#define NMBM_MAGIC_INFO_TABLE 0x314d4d4e /* NMM1 */ ++ ++#define NMBM_VERSION_MAJOR_S 0 ++#define NMBM_VERSION_MAJOR_M 0xffff ++#define NMBM_VERSION_MINOR_S 16 ++#define NMBM_VERSION_MINOR_M 0xffff ++#define NMBM_VERSION_MAKE(major, minor) (((major) & NMBM_VERSION_MAJOR_M) | \ ++ (((minor) & NMBM_VERSION_MINOR_M) << \ ++ NMBM_VERSION_MINOR_S)) ++#define NMBM_VERSION_MAJOR_GET(ver) (((ver) >> NMBM_VERSION_MAJOR_S) & \ ++ NMBM_VERSION_MAJOR_M) ++#define NMBM_VERSION_MINOR_GET(ver) (((ver) >> NMBM_VERSION_MINOR_S) & \ ++ NMBM_VERSION_MINOR_M) ++ ++typedef uint32_t nmbm_bitmap_t; ++#define NMBM_BITMAP_UNIT_SIZE (sizeof(nmbm_bitmap_t)) ++#define NMBM_BITMAP_BITS_PER_BLOCK 2 ++#define NMBM_BITMAP_BITS_PER_UNIT (8 * sizeof(nmbm_bitmap_t)) ++#define NMBM_BITMAP_BLOCKS_PER_UNIT (NMBM_BITMAP_BITS_PER_UNIT / \ ++ NMBM_BITMAP_BITS_PER_BLOCK) ++ ++#define NMBM_SPARE_BLOCK_MULTI 1 ++#define NMBM_SPARE_BLOCK_DIV 2 ++#define NMBM_SPARE_BLOCK_MIN 2 ++ ++#define NMBM_MGMT_DIV 16 ++#define NMBM_MGMT_BLOCKS_MIN 32 ++ ++#define NMBM_TRY_COUNT 3 ++ ++#define BLOCK_ST_BAD 0 ++#define BLOCK_ST_NEED_REMAP 2 ++#define BLOCK_ST_GOOD 3 ++#define BLOCK_ST_MASK 3 ++ ++struct nmbm_header { ++ uint32_t magic; ++ uint32_t version; ++ uint32_t size; ++ uint32_t checksum; ++}; ++ ++struct nmbm_signature { ++ struct nmbm_header header; ++ uint64_t nand_size; ++ uint32_t block_size; ++ uint32_t page_size; ++ uint32_t spare_size; ++ uint32_t mgmt_start_pb; ++ uint8_t max_try_count; ++ uint8_t padding[3]; ++}; ++ ++struct nmbm_info_table_header { ++ struct nmbm_header header; ++ uint32_t write_count; ++ uint32_t state_table_off; ++ uint32_t mapping_table_off; ++ uint32_t padding; ++}; ++ ++struct nmbm_instance { ++ struct nmbm_lower_device lower; ++ ++ uint32_t rawpage_size; ++ uint32_t rawblock_size; ++ uint32_t rawchip_size; ++ ++ uint32_t writesize_mask; ++ uint32_t erasesize_mask; ++ uint16_t writesize_shift; ++ uint16_t erasesize_shift; ++ ++ struct nmbm_signature signature; ++ ++ uint8_t *info_table_cache; ++ uint32_t info_table_size; ++ uint32_t info_table_spare_blocks; ++ struct nmbm_info_table_header info_table; ++ ++ nmbm_bitmap_t *block_state; ++ uint32_t block_state_changed; ++ uint32_t state_table_size; ++ ++ int32_t *block_mapping; ++ uint32_t block_mapping_changed; ++ uint32_t mapping_table_size; ++ ++ uint8_t *page_cache; ++ ++ int protected; ++ ++ uint32_t block_count; ++ uint32_t data_block_count; ++ ++ uint32_t mgmt_start_ba; ++ uint32_t main_table_ba; ++ uint32_t backup_table_ba; ++ uint32_t mapping_blocks_ba; ++ uint32_t mapping_blocks_top_ba; ++ uint32_t signature_ba; ++ ++ enum nmbm_log_category log_display_level; ++}; ++ ++/* Log utilities */ ++#define nlog_debug(ni, fmt, ...) \ ++ nmbm_log(ni, NMBM_LOG_DEBUG, fmt, ##__VA_ARGS__) ++ ++#define nlog_info(ni, fmt, ...) \ ++ nmbm_log(ni, NMBM_LOG_INFO, fmt, ##__VA_ARGS__) ++ ++#define nlog_warn(ni, fmt, ...) \ ++ nmbm_log(ni, NMBM_LOG_WARN, fmt, ##__VA_ARGS__) ++ ++#define nlog_err(ni, fmt, ...) \ ++ nmbm_log(ni, NMBM_LOG_ERR, fmt, ##__VA_ARGS__) ++ ++#define nlog_emerg(ni, fmt, ...) \ ++ nmbm_log(ni, NMBM_LOG_EMERG, fmt, ##__VA_ARGS__) ++ ++#endif /* _NMBM_PRIVATE_H_ */ +--- /dev/null ++++ b/include/nmbm/nmbm-os.h +@@ -0,0 +1,66 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * OS-dependent definitions for NAND Mapped-block Management (NMBM) ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _NMBM_OS_H_ ++#define _NMBM_OS_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static inline uint32_t nmbm_crc32(uint32_t crcval, const void *buf, size_t size) ++{ ++ uint chksz; ++ const unsigned char *p = buf; ++ ++ while (size) { ++ if (size > UINT_MAX) ++ chksz = UINT_MAX; ++ else ++ chksz = (uint)size; ++ ++ crcval = crc32_no_comp(crcval, p, chksz); ++ size -= chksz; ++ p += chksz; ++ } ++ ++ return crcval; ++} ++ ++static inline uint32_t nmbm_lldiv(uint64_t dividend, uint32_t divisor) ++{ ++#if BITS_PER_LONG == 64 ++ return dividend / divisor; ++#else ++ __div64_32(÷nd, divisor); ++ return dividend; ++#endif ++} ++ ++#ifdef CONFIG_NMBM_LOG_LEVEL_DEBUG ++#define NMBM_DEFAULT_LOG_LEVEL 0 ++#elif defined(NMBM_LOG_LEVEL_INFO) ++#define NMBM_DEFAULT_LOG_LEVEL 1 ++#elif defined(NMBM_LOG_LEVEL_WARN) ++#define NMBM_DEFAULT_LOG_LEVEL 2 ++#elif defined(NMBM_LOG_LEVEL_ERR) ++#define NMBM_DEFAULT_LOG_LEVEL 3 ++#elif defined(NMBM_LOG_LEVEL_EMERG) ++#define NMBM_DEFAULT_LOG_LEVEL 4 ++#elif defined(NMBM_LOG_LEVEL_NONE) ++#define NMBM_DEFAULT_LOG_LEVEL 5 ++#else ++#define NMBM_DEFAULT_LOG_LEVEL 1 ++#endif ++ ++#endif /* _NMBM_OS_H_ */ +--- /dev/null ++++ b/include/nmbm/nmbm.h +@@ -0,0 +1,102 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Definitions for NAND Mapped-block Management (NMBM) ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _NMBM_H_ ++#define _NMBM_H_ ++ ++#include ++ ++enum nmbm_log_category { ++ NMBM_LOG_DEBUG, ++ NMBM_LOG_INFO, ++ NMBM_LOG_WARN, ++ NMBM_LOG_ERR, ++ NMBM_LOG_EMERG, ++ ++ __NMBM_LOG_MAX ++}; ++ ++enum nmbm_oob_mode { ++ NMBM_MODE_PLACE_OOB, ++ NMBM_MODE_AUTO_OOB, ++ NMBM_MODE_RAW, ++ ++ __NMBM_MODE_MAX ++}; ++ ++struct nmbm_lower_device { ++ uint32_t max_ratio; ++ uint32_t max_reserved_blocks; ++ int flags; ++ ++ uint64_t size; ++ uint32_t erasesize; ++ uint32_t writesize; ++ uint32_t oobsize; ++ uint32_t oobavail; ++ ++ void *arg; ++ int (*reset_chip)(void *arg); ++ ++ /* ++ * read_page: ++ * return 0 if succeeds ++ * return positive number for ecc error ++ * return negative number for other errors ++ */ ++ int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode); ++ int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode); ++ int (*erase_block)(void *arg, uint64_t addr); ++ ++ int (*is_bad_block)(void *arg, uint64_t addr); ++ int (*mark_bad_block)(void *arg, uint64_t addr); ++ ++ /* OS-dependent logging function */ ++ void (*logprint)(void *arg, enum nmbm_log_category level, const char *fmt, va_list ap); ++}; ++ ++struct nmbm_instance; ++ ++/* Create NMBM if management area not found, or not complete */ ++#define NMBM_F_CREATE 0x01 ++ ++/* Empty page is also protected by ECC, and bitflip(s) can be corrected */ ++#define NMBM_F_EMPTY_PAGE_ECC_OK 0x02 ++ ++/* Do not write anything back to flash */ ++#define NMBM_F_READ_ONLY 0x04 ++ ++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld); ++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni); ++int nmbm_detach(struct nmbm_instance *ni); ++ ++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni, ++ enum nmbm_log_category level); ++ ++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr, ++ uint64_t size, uint64_t *failed_addr); ++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data, ++ void *oob, enum nmbm_oob_mode mode); ++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size, ++ void *data, enum nmbm_oob_mode mode, size_t *retlen); ++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr, ++ const void *data, const void *oob, ++ enum nmbm_oob_mode mode); ++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size, ++ const void *data, enum nmbm_oob_mode mode, ++ size_t *retlen); ++ ++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr); ++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr); ++ ++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni); ++ ++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld); ++ ++#endif /* _NMBM_H_ */ diff --git a/package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch b/package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch new file mode 100644 index 00000000000..644ac8f105c --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch @@ -0,0 +1,958 @@ +From 0524995f07fcd216a1a7e267fdb5cf2b0ede8489 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:42:12 +0800 +Subject: [PATCH 41/71] mtd: nmbm: add support for mtd + +Add support to create NMBM based on MTD devices + +Signed-off-by: Weijie Gao +--- + drivers/mtd/nmbm/Kconfig | 5 + + drivers/mtd/nmbm/Makefile | 1 + + drivers/mtd/nmbm/nmbm-mtd.c | 890 ++++++++++++++++++++++++++++++++++++ + include/nmbm/nmbm-mtd.h | 27 ++ + 4 files changed, 923 insertions(+) + create mode 100644 drivers/mtd/nmbm/nmbm-mtd.c + create mode 100644 include/nmbm/nmbm-mtd.h + +--- a/drivers/mtd/nmbm/Kconfig ++++ b/drivers/mtd/nmbm/Kconfig +@@ -27,3 +27,8 @@ config NMBM_LOG_LEVEL_NONE + bool "5 - None" + + endchoice ++ ++config NMBM_MTD ++ bool "Enable MTD based NAND mapping block management" ++ default n ++ depends on NMBM +--- a/drivers/mtd/nmbm/Makefile ++++ b/drivers/mtd/nmbm/Makefile +@@ -3,3 +3,4 @@ + # (C) Copyright 2020 MediaTek Inc. All rights reserved. + + obj-$(CONFIG_NMBM) += nmbm-core.o ++obj-$(CONFIG_NMBM_MTD) += nmbm-mtd.o +--- /dev/null ++++ b/drivers/mtd/nmbm/nmbm-mtd.c +@@ -0,0 +1,890 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "nmbm-debug.h" ++ ++#define NMBM_UPPER_MTD_NAME "nmbm" ++ ++static uint32_t nmbm_id_cnt; ++static LIST_HEAD(nmbm_devs); ++ ++struct nmbm_mtd { ++ struct mtd_info upper; ++ char *name; ++ uint32_t id; ++ ++ struct mtd_info *lower; ++ ++ struct nmbm_instance *ni; ++ uint8_t *page_cache; ++ ++ struct list_head node; ++}; ++ ++static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob, ++ enum nmbm_oob_mode mode) ++{ ++ struct nmbm_mtd *nm = arg; ++ struct mtd_oob_ops ops; ++ int ret; ++ ++ memset(&ops, 0, sizeof(ops)); ++ ++ switch (mode) { ++ case NMBM_MODE_PLACE_OOB: ++ ops.mode = MTD_OPS_PLACE_OOB; ++ break; ++ case NMBM_MODE_AUTO_OOB: ++ ops.mode = MTD_OPS_AUTO_OOB; ++ break; ++ case NMBM_MODE_RAW: ++ ops.mode = MTD_OPS_RAW; ++ break; ++ default: ++ pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode); ++ return -ENOTSUPP; ++ } ++ ++ if (buf) { ++ ops.datbuf = buf; ++ ops.len = nm->lower->writesize; ++ } ++ ++ if (oob) { ++ ops.oobbuf = oob; ++ ops.ooblen = mtd_oobavail(nm->lower, &ops); ++ } ++ ++ ret = mtd_read_oob(nm->lower, addr, &ops); ++ nm->upper.ecc_stats.corrected = nm->lower->ecc_stats.corrected; ++ nm->upper.ecc_stats.failed = nm->lower->ecc_stats.failed; ++ ++ /* Report error on failure (including ecc error) */ ++ if (ret < 0 && ret != -EUCLEAN) ++ return ret; ++ ++ /* ++ * Since mtd_read_oob() won't report exact bitflips, what we can know ++ * is whether bitflips exceeds the threshold. ++ * We want the -EUCLEAN to be passed to the upper layer, but not the ++ * error value itself. To achieve this, report bitflips above the ++ * threshold. ++ */ ++ ++ if (ret == -EUCLEAN) { ++ return min_t(u32, nm->lower->bitflip_threshold + 1, ++ nm->lower->ecc_strength); ++ } ++ ++ /* For bitflips less than the threshold, return 0 */ ++ ++ return 0; ++} ++ ++static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf, ++ const void *oob, enum nmbm_oob_mode mode) ++{ ++ struct nmbm_mtd *nm = arg; ++ struct mtd_oob_ops ops; ++ ++ memset(&ops, 0, sizeof(ops)); ++ ++ switch (mode) { ++ case NMBM_MODE_PLACE_OOB: ++ ops.mode = MTD_OPS_PLACE_OOB; ++ break; ++ case NMBM_MODE_AUTO_OOB: ++ ops.mode = MTD_OPS_AUTO_OOB; ++ break; ++ case NMBM_MODE_RAW: ++ ops.mode = MTD_OPS_RAW; ++ break; ++ default: ++ pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode); ++ return -ENOTSUPP; ++ } ++ ++ if (buf) { ++ ops.datbuf = (uint8_t *)buf; ++ ops.len = nm->lower->writesize; ++ } ++ ++ if (oob) { ++ ops.oobbuf = (uint8_t *)oob; ++ ops.ooblen = mtd_oobavail(nm->lower, &ops); ++ } ++ ++ return mtd_write_oob(nm->lower, addr, &ops); ++} ++ ++static int nmbm_lower_erase_block(void *arg, uint64_t addr) ++{ ++ struct nmbm_mtd *nm = arg; ++ struct erase_info ei; ++ ++ memset(&ei, 0, sizeof(ei)); ++ ++ ei.mtd = nm->lower; ++ ei.addr = addr; ++ ei.len = nm->lower->erasesize; ++ ++ return mtd_erase(nm->lower, &ei); ++} ++ ++static int nmbm_lower_is_bad_block(void *arg, uint64_t addr) ++{ ++ struct nmbm_mtd *nm = arg; ++ ++ return mtd_block_isbad(nm->lower, addr); ++} ++ ++static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr) ++{ ++ struct nmbm_mtd *nm = arg; ++ ++ return mtd_block_markbad(nm->lower, addr); ++} ++ ++static void nmbm_lower_log(void *arg, enum nmbm_log_category level, ++ const char *fmt, va_list ap) ++{ ++ vprintf(fmt, ap); ++} ++ ++static int nmbm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ ++ /* Do not allow read past end of device */ ++ if ((from + len) > mtd->size) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return nmbm_read_range(nm->ni, from, len, buf, MTD_OPS_PLACE_OOB, ++ retlen); ++} ++ ++static int nmbm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ ++ /* Do not allow write past end of device */ ++ if ((to + len) > mtd->size) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return nmbm_write_range(nm->ni, to, len, buf, MTD_OPS_PLACE_OOB, ++ retlen); ++} ++ ++static int nmbm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ int ret; ++ ++ instr->state = MTD_ERASING; ++ ++ ret = nmbm_erase_block_range(nm->ni, instr->addr, instr->len, ++ &instr->fail_addr); ++ if (ret) ++ instr->state = MTD_ERASE_FAILED; ++ else ++ instr->state = MTD_ERASE_DONE; ++ ++ if (!ret) ++ /* FIXME */ ++ /* mtd_erase_callback(instr); */ ++ return ret; ++ else ++ ret = -EIO; ++ ++ return ret; ++} ++ ++static int nmbm_mtd_read_data(struct nmbm_mtd *nm, uint64_t addr, ++ struct mtd_oob_ops *ops, enum nmbm_oob_mode mode) ++{ ++ size_t len, ooblen, maxooblen, chklen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ bool has_ecc_err = false; ++ int ret, max_bitflips = 0; ++ ++ col = addr & nm->lower->writesize_mask; ++ addr &= ~nm->lower->writesize_mask; ++ maxooblen = mtd_oobavail(nm->lower, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? nm->page_cache : NULL; ++ oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ ret = nmbm_read_single_page(nm->ni, addr, datcache, oobcache, ++ mode); ++ if (ret < 0 && ret != -EBADMSG) ++ return ret; ++ ++ /* Continue reading on ecc error */ ++ if (ret == -EBADMSG) ++ has_ecc_err = true; ++ ++ /* Record the maximum bitflips between pages */ ++ if (ret > max_bitflips) ++ max_bitflips = ret; ++ ++ if (len) { ++ /* Move data */ ++ chklen = nm->lower->writesize - col; ++ if (chklen > len) ++ chklen = len; ++ ++ memcpy(ops->datbuf + ops->retlen, datcache + col, ++ chklen); ++ len -= chklen; ++ col = 0; /* (col + chklen) % */ ++ ops->retlen += chklen; ++ } ++ ++ if (ooblen) { ++ /* Move oob */ ++ chklen = maxooblen - ooboffs; ++ if (chklen > ooblen) ++ chklen = ooblen; ++ ++ memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs, ++ chklen); ++ ooblen -= chklen; ++ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ ++ ops->oobretlen += chklen; ++ } ++ ++ addr += nm->lower->writesize; ++ } ++ ++ if (has_ecc_err) ++ return -EBADMSG; ++ ++ return max_bitflips; ++} ++ ++static int nmbm_mtd_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ uint32_t maxooblen; ++ enum nmbm_oob_mode mode; ++ ++ if (!ops->oobbuf && !ops->datbuf) { ++ if (ops->ooblen || ops->len) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ switch (ops->mode) { ++ case MTD_OPS_PLACE_OOB: ++ mode = NMBM_MODE_PLACE_OOB; ++ break; ++ case MTD_OPS_AUTO_OOB: ++ mode = NMBM_MODE_AUTO_OOB; ++ break; ++ case MTD_OPS_RAW: ++ mode = NMBM_MODE_RAW; ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); ++ return -ENOTSUPP; ++ } ++ ++ maxooblen = mtd_oobavail(mtd, ops); ++ ++ /* Do not allow read past end of device */ ++ if (ops->datbuf && (from + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (!ops->oobbuf) { ++ /* Optimized for reading data only */ ++ return nmbm_read_range(nm->ni, from, ops->len, ops->datbuf, ++ mode, &ops->retlen); ++ } ++ ++ if (unlikely(ops->ooboffs >= maxooblen)) { ++ pr_debug("%s: attempt to start read outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(from >= mtd->size || ++ ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - ++ (from >> mtd->writesize_shift)) * maxooblen)) { ++ pr_debug("%s: attempt to read beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return nmbm_mtd_read_data(nm, from, ops, mode); ++} ++ ++static int nmbm_mtd_write_data(struct nmbm_mtd *nm, uint64_t addr, ++ struct mtd_oob_ops *ops, enum nmbm_oob_mode mode) ++{ ++ size_t len, ooblen, maxooblen, chklen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ int ret; ++ ++ col = addr & nm->lower->writesize_mask; ++ addr &= ~nm->lower->writesize_mask; ++ maxooblen = mtd_oobavail(nm->lower, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? nm->page_cache : NULL; ++ oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ if (len) { ++ /* Move data */ ++ chklen = nm->lower->writesize - col; ++ if (chklen > len) ++ chklen = len; ++ ++ memset(datcache, 0xff, col); ++ memcpy(datcache + col, ops->datbuf + ops->retlen, ++ chklen); ++ memset(datcache + col + chklen, 0xff, ++ nm->lower->writesize - col - chklen); ++ len -= chklen; ++ col = 0; /* (col + chklen) % */ ++ ops->retlen += chklen; ++ } ++ ++ if (ooblen) { ++ /* Move oob */ ++ chklen = maxooblen - ooboffs; ++ if (chklen > ooblen) ++ chklen = ooblen; ++ ++ memset(oobcache, 0xff, ooboffs); ++ memcpy(oobcache + ooboffs, ++ ops->oobbuf + ops->oobretlen, chklen); ++ memset(oobcache + ooboffs + chklen, 0xff, ++ nm->lower->oobsize - ooboffs - chklen); ++ ooblen -= chklen; ++ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ ++ ops->oobretlen += chklen; ++ } ++ ++ ret = nmbm_write_single_page(nm->ni, addr, datcache, oobcache, ++ mode); ++ if (ret) ++ return ret; ++ ++ addr += nm->lower->writesize; ++ } ++ ++ return 0; ++} ++ ++static int nmbm_mtd_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ enum nmbm_oob_mode mode; ++ uint32_t maxooblen; ++ ++ if (!ops->oobbuf && !ops->datbuf) { ++ if (ops->ooblen || ops->len) ++ return -EINVAL; ++ ++ return 0; ++ } ++ ++ switch (ops->mode) { ++ case MTD_OPS_PLACE_OOB: ++ mode = NMBM_MODE_PLACE_OOB; ++ break; ++ case MTD_OPS_AUTO_OOB: ++ mode = NMBM_MODE_AUTO_OOB; ++ break; ++ case MTD_OPS_RAW: ++ mode = NMBM_MODE_RAW; ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ++ ops->mode); ++ return -ENOTSUPP; ++ } ++ ++ maxooblen = mtd_oobavail(mtd, ops); ++ ++ /* Do not allow write past end of device */ ++ if (ops->datbuf && (to + ops->len) > mtd->size) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (!ops->oobbuf) { ++ /* Optimized for writing data only */ ++ return nmbm_write_range(nm->ni, to, ops->len, ops->datbuf, ++ mode, &ops->retlen); ++ } ++ ++ if (unlikely(ops->ooboffs >= maxooblen)) { ++ pr_debug("%s: attempt to start write outside oob\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(to >= mtd->size || ++ ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) - ++ (to >> mtd->writesize_shift)) * maxooblen)) { ++ pr_debug("%s: attempt to write beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ return nmbm_mtd_write_data(nm, to, ops, mode); ++} ++ ++static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ ++ return nmbm_check_bad_block(nm->ni, offs); ++} ++ ++static int nmbm_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper); ++ ++ return nmbm_mark_bad_block(nm->ni, offs); ++} ++ ++int nmbm_attach_mtd(struct mtd_info *lower, int flags, uint32_t max_ratio, ++ uint32_t max_reserved_blocks, struct mtd_info **upper) ++{ ++ struct nmbm_lower_device nld; ++ struct nmbm_instance *ni; ++ struct mtd_info *mtd; ++ struct nmbm_mtd *nm; ++ size_t namelen, alloc_size; ++ int ret; ++ ++ if (!lower) ++ return -EINVAL; ++ ++ if (lower->type != MTD_NANDFLASH || lower->flags != MTD_CAP_NANDFLASH) ++ return -ENOTSUPP; ++ ++ namelen = strlen(NMBM_UPPER_MTD_NAME) + 16; ++ ++ nm = calloc(sizeof(*nm) + lower->writesize + lower->oobsize + namelen + 1, 1); ++ if (!nm) ++ return -ENOMEM; ++ ++ nm->lower = lower; ++ nm->name = (char *)nm + sizeof(*nm); ++ nm->page_cache = (uint8_t *)nm->name + namelen + 1; ++ ++ nm->id = nmbm_id_cnt++; ++ snprintf(nm->name, namelen + 1, "%s%u", NMBM_UPPER_MTD_NAME, nm->id); ++ ++ memset(&nld, 0, sizeof(nld)); ++ ++ nld.flags = flags; ++ nld.max_ratio = max_ratio; ++ nld.max_reserved_blocks = max_reserved_blocks; ++ ++ nld.size = lower->size; ++ nld.erasesize = lower->erasesize; ++ nld.writesize = lower->writesize; ++ nld.oobsize = lower->oobsize; ++ nld.oobavail = lower->oobavail; ++ ++ nld.arg = nm; ++ nld.read_page = nmbm_lower_read_page; ++ nld.write_page = nmbm_lower_write_page; ++ nld.erase_block = nmbm_lower_erase_block; ++ nld.is_bad_block = nmbm_lower_is_bad_block; ++ nld.mark_bad_block = nmbm_lower_mark_bad_block; ++ ++ nld.logprint = nmbm_lower_log; ++ ++ alloc_size = nmbm_calc_structure_size(&nld); ++ ni = calloc(alloc_size, 1); ++ if (!ni) { ++ free(nm); ++ return -ENOMEM; ++ } ++ ++ ret = nmbm_attach(&nld, ni); ++ if (ret) { ++ free(ni); ++ free(nm); ++ return ret; ++ } ++ ++ nm->ni = ni; ++ ++ /* Initialize upper mtd */ ++ mtd = &nm->upper; ++ ++ mtd->name = nm->name; ++ mtd->type = MTD_DEV_TYPE_NMBM; ++ mtd->flags = lower->flags; ++ ++ mtd->size = (uint64_t)ni->data_block_count * ni->lower.erasesize; ++ mtd->erasesize = lower->erasesize; ++ mtd->writesize = lower->writesize; ++ mtd->writebufsize = lower->writesize; ++ mtd->oobsize = lower->oobsize; ++ mtd->oobavail = lower->oobavail; ++ ++ mtd->erasesize_shift = lower->erasesize_shift; ++ mtd->writesize_shift = lower->writesize_shift; ++ mtd->erasesize_mask = lower->erasesize_mask; ++ mtd->writesize_mask = lower->writesize_mask; ++ ++ mtd->bitflip_threshold = lower->bitflip_threshold; ++ ++ /* XXX: should this be duplicated? */ ++ mtd->ooblayout = lower->ooblayout; ++ mtd->ecclayout = lower->ecclayout; ++ ++ mtd->ecc_step_size = lower->ecc_step_size; ++ mtd->ecc_strength = lower->ecc_strength; ++ ++ mtd->numeraseregions = lower->numeraseregions; ++ mtd->eraseregions = lower->eraseregions; ++ ++ mtd->_read = nmbm_mtd_read; ++ mtd->_write = nmbm_mtd_write; ++ mtd->_erase = nmbm_mtd_erase; ++ mtd->_read_oob = nmbm_mtd_read_oob; ++ mtd->_write_oob = nmbm_mtd_write_oob; ++ mtd->_block_isbad = nmbm_mtd_block_isbad; ++ mtd->_block_markbad = nmbm_mtd_block_markbad; ++ ++ *upper = mtd; ++ ++ list_add_tail(&nm->node, &nmbm_devs); ++ ++ return 0; ++} ++ ++int nmbm_free_mtd(struct mtd_info *upper) ++{ ++ struct nmbm_mtd *pos; ++ ++ if (!upper) ++ return -EINVAL; ++ ++ list_for_each_entry(pos, &nmbm_devs, node) { ++ if (&pos->upper == upper) { ++ list_del(&pos->node); ++ ++ nmbm_detach(pos->ni); ++ free(pos->ni); ++ free(pos); ++ ++ return 0; ++ } ++ } ++ ++ return -ENODEV; ++} ++ ++struct mtd_info *nmbm_mtd_get_upper_by_index(uint32_t index) ++{ ++ struct nmbm_mtd *nm; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (nm->id == index) ++ return &nm->upper; ++ } ++ ++ return NULL; ++} ++ ++struct mtd_info *nmbm_mtd_get_upper(struct mtd_info *lower) ++{ ++ struct nmbm_mtd *nm; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (nm->lower == lower) ++ return &nm->upper; ++ } ++ ++ return NULL; ++} ++ ++void nmbm_mtd_list_devices(void) ++{ ++ struct nmbm_mtd *nm; ++ ++ printf("Index NMBM device Lower device\n"); ++ printf("========================================\n"); ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ printf("%-8u%-20s%s\n", nm->id, nm->name, nm->lower->name); ++ } ++} ++ ++int nmbm_mtd_print_info(const char *name) ++{ ++ struct nmbm_mtd *nm; ++ bool found = false; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (!strcmp(nm->name, name)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ printf("Error: NMBM device '%s' not found\n", name); ++ return -ENODEV; ++ } ++ ++ printf("%s:\n", name); ++ printf("Total blocks: %u\n", nm->ni->block_count); ++ printf("Data blocks: %u\n", nm->ni->data_block_count); ++ printf("Management start block: %u\n", nm->ni->mgmt_start_ba); ++ printf("Info table size: 0x%x\n", nm->ni->info_table_size); ++ ++ if (nm->ni->main_table_ba) ++ printf("Main info table start block: %u\n", nm->ni->main_table_ba); ++ else ++ printf("Main info table start block: Not exist\n"); ++ ++ if (nm->ni->backup_table_ba) ++ printf("Backup info table start block: %u\n", nm->ni->backup_table_ba); ++ else ++ printf("Backup info table start block: Not exist\n"); ++ ++ printf("Signature block: %u\n", nm->ni->signature_ba); ++ printf("Mapping blocks top address: %u\n", nm->ni->mapping_blocks_top_ba); ++ printf("Mapping blocks limit address: %u\n", nm->ni->mapping_blocks_ba); ++ ++ return 0; ++} ++ ++static const char nmbm_block_legends[] = { ++ [NMBM_BLOCK_GOOD_DATA] = '-', ++ [NMBM_BLOCK_GOOD_MGMT] = '+', ++ [NMBM_BLOCK_BAD] = 'B', ++ [NMBM_BLOCK_MAIN_INFO_TABLE] = 'I', ++ [NMBM_BLOCK_BACKUP_INFO_TABLE] = 'i', ++ [NMBM_BLOCK_REMAPPED] = 'M', ++ [NMBM_BLOCK_SIGNATURE] = 'S', ++}; ++ ++int nmbm_mtd_print_states(const char *name) ++{ ++ struct nmbm_mtd *nm; ++ enum nmmb_block_type bt; ++ bool found = false; ++ uint32_t i; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (!strcmp(nm->name, name)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ printf("Error: NMBM device '%s' not found\n", name); ++ return -ENODEV; ++ } ++ ++ printf("Physical blocks:\n"); ++ printf("\n"); ++ ++ printf("Legends:\n"); ++ printf(" - Good data block\n"); ++ printf(" + Good management block\n"); ++ printf(" B Bad block\n"); ++ printf(" I Main info table\n"); ++ printf(" i Backup info table\n"); ++ printf(" M Remapped spare block\n"); ++ printf(" S Signature block\n"); ++ printf("\n"); ++ ++ for (i = 0; i < nm->ni->block_count; i++) { ++ if (i % 64 == 0) ++ printf(" "); ++ ++ bt = nmbm_debug_get_phys_block_type(nm->ni, i); ++ if (bt < __NMBM_BLOCK_TYPE_MAX) ++ putc(nmbm_block_legends[bt]); ++ else ++ putc('?'); ++ ++ if (i % 64 == 63) ++ printf("\n"); ++ } ++ ++ printf("\n"); ++ printf("Logical blocks:\n"); ++ printf("\n"); ++ ++ printf("Legends:\n"); ++ printf(" - Good block\n"); ++ printf(" + Initially remapped block\n"); ++ printf(" M Remapped block\n"); ++ printf(" B Bad/Unmapped block\n"); ++ printf("\n"); ++ ++ for (i = 0; i < nm->ni->data_block_count; i++) { ++ if (i % 64 == 0) ++ printf(" "); ++ ++ if (nm->ni->block_mapping[i] < 0) ++ putc('B'); ++ else if (nm->ni->block_mapping[i] == i) ++ putc('-'); ++ else if (nm->ni->block_mapping[i] < nm->ni->data_block_count) ++ putc('+'); ++ else if (nm->ni->block_mapping[i] > nm->ni->mapping_blocks_top_ba && ++ nm->ni->block_mapping[i] < nm->ni->signature_ba) ++ putc('M'); ++ else ++ putc('?'); ++ ++ if (i % 64 == 63) ++ printf("\n"); ++ } ++ ++ return 0; ++} ++ ++int nmbm_mtd_print_bad_blocks(const char *name) ++{ ++ struct nmbm_mtd *nm; ++ bool found = false; ++ uint32_t i; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (!strcmp(nm->name, name)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ printf("Error: NMBM device '%s' not found\n", name); ++ return -ENODEV; ++ } ++ ++ printf("Physical blocks:\n"); ++ ++ for (i = 0; i < nm->ni->block_count; i++) { ++ switch (nmbm_debug_get_block_state(nm->ni, i)) { ++ case BLOCK_ST_BAD: ++ printf("%-12u [0x%08llx] - Bad\n", i, ++ (uint64_t)i << nm->ni->erasesize_shift); ++ break; ++ case BLOCK_ST_NEED_REMAP: ++ printf("%-12u [0x%08llx] - Awaiting remapping\n", i, ++ (uint64_t)i << nm->ni->erasesize_shift); ++ break; ++ } ++ } ++ ++ printf("\n"); ++ printf("Logical blocks:\n"); ++ ++ for (i = 0; i < nm->ni->data_block_count; i++) { ++ if (nm->ni->block_mapping[i] < 0) { ++ printf("%-12u [0x%08llx] - Bad\n", i, ++ (uint64_t)i << nm->ni->erasesize_shift); ++ } ++ } ++ ++ return 0; ++} ++ ++int nmbm_mtd_print_mappings(const char *name, int printall) ++{ ++ struct nmbm_mtd *nm; ++ bool found = false; ++ int32_t pb; ++ uint32_t i; ++ ++ list_for_each_entry(nm, &nmbm_devs, node) { ++ if (!strcmp(nm->name, name)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ printf("Error: NMBM device '%s' not found\n", name); ++ return -ENODEV; ++ } ++ ++ printf("Logical Block Physical Block\n"); ++ printf("==================================\n"); ++ ++ if (!printall) { ++ for (i = 0; i < nm->ni->data_block_count; i++) { ++ pb = nm->ni->block_mapping[i]; ++ if (pb < 0) ++ printf("%-20uUnmapped\n", i); ++ else if ((uint32_t)pb > nm->ni->mapping_blocks_top_ba && ++ (uint32_t)pb < nm->ni->signature_ba) ++ printf("%-20u%u\n", i, pb); ++ } ++ ++ return 0; ++ } ++ ++ for (i = 0; i < nm->ni->data_block_count; i++) { ++ pb = nm->ni->block_mapping[i]; ++ ++ if (pb >= 0) ++ printf("%-20u%u\n", i, pb); ++ else ++ printf("%-20uUnmapped\n", i); ++ } ++ ++ return 0; ++} +--- /dev/null ++++ b/include/nmbm/nmbm-mtd.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _NMBM_MTD_H_ ++#define _NMBM_MTD_H_ ++ ++#include ++ ++int nmbm_attach_mtd(struct mtd_info *lower, int flags, uint32_t max_ratio, ++ uint32_t max_reserved_blocks, struct mtd_info **upper); ++ ++int nmbm_free_mtd(struct mtd_info *upper); ++ ++struct mtd_info *nmbm_mtd_get_upper_by_index(uint32_t index); ++struct mtd_info *nmbm_mtd_get_upper(struct mtd_info *lower); ++ ++void nmbm_mtd_list_devices(void); ++int nmbm_mtd_print_info(const char *name); ++int nmbm_mtd_print_states(const char *name); ++int nmbm_mtd_print_bad_blocks(const char *name); ++int nmbm_mtd_print_mappings(const char *name, int printall); ++ ++#endif /* _NMBM_MTD_H_ */ diff --git a/package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch b/package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch new file mode 100644 index 00000000000..3dab0536774 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch @@ -0,0 +1,46 @@ +From dcf24c8deeb43a4406ae18136c8700dc2f867415 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 11:18:03 +0800 +Subject: [PATCH 42/71] common: board_r: add support to initialize NMBM after + nand initialization + +This patch add support to initialize NMBM after nand initialized. + +Signed-off-by: Weijie Gao +--- + common/board_r.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/common/board_r.c ++++ b/common/board_r.c +@@ -382,6 +382,20 @@ static int initr_nand(void) + } + #endif + ++#ifdef CONFIG_NMBM_MTD ++ ++__weak int board_nmbm_init(void) ++{ ++ return 0; ++} ++ ++/* go init the NMBM */ ++static int initr_nmbm(void) ++{ ++ return board_nmbm_init(); ++} ++#endif ++ + #if defined(CONFIG_CMD_ONENAND) + /* go init the NAND */ + static int initr_onenand(void) +@@ -703,6 +717,9 @@ static init_fnc_t init_sequence_r[] = { + #ifdef CONFIG_CMD_ONENAND + initr_onenand, + #endif ++#ifdef CONFIG_NMBM_MTD ++ initr_nmbm, ++#endif + #ifdef CONFIG_MMC + initr_mmc, + #endif diff --git a/package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch b/package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch new file mode 100644 index 00000000000..e6e155bc146 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch @@ -0,0 +1,370 @@ +From 0af8d0aac77f4df4bc7dadbcdea5d9a16f5f3e45 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:44:57 +0800 +Subject: [PATCH 43/71] cmd: add nmbm command + +Add nmbm command for debugging, data operations and image-booting support + +Signed-off-by: Weijie Gao +--- + cmd/Kconfig | 6 + + cmd/Makefile | 1 + + cmd/nmbm.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 334 insertions(+) + create mode 100644 cmd/nmbm.c + +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -1260,6 +1260,12 @@ config CMD_NAND_TORTURE + + endif # CMD_NAND + ++config CMD_NMBM ++ depends on NMBM_MTD ++ bool "nmbm" ++ help ++ NAND mapping block management (NMBM) utility ++ + config CMD_NVME + bool "nvme" + depends on NVME +--- a/cmd/Makefile ++++ b/cmd/Makefile +@@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o + endif + obj-$(CONFIG_CMD_MUX) += mux.o + obj-$(CONFIG_CMD_NAND) += nand.o ++obj-$(CONFIG_CMD_NMBM) += nmbm.o + obj-$(CONFIG_CMD_NET) += net.o + obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o + obj-$(CONFIG_CMD_ONENAND) += onenand.o +--- /dev/null ++++ b/cmd/nmbm.c +@@ -0,0 +1,327 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static int nmbm_parse_offset_size(struct mtd_info *mtd, char *off_str, ++ char *size_str, uint64_t *off, ++ uint64_t *size) ++{ ++ char *end; ++ ++ *off = simple_strtoull(off_str, &end, 16); ++ if (end == off_str) { ++ printf("Error: offset '%s' is invalid\n", off_str); ++ return -EINVAL; ++ } ++ ++ if (*off >= mtd->size) { ++ printf("Error: offset '0x%llx' is beyond the end of device\n", ++ *off); ++ return -EINVAL; ++ } ++ ++ *size = simple_strtoull(size_str, &end, 16); ++ if (end == off_str) { ++ printf("Error: size '%s' is invalid\n", off_str); ++ return -EINVAL; ++ } ++ ++ if (*off + *size > mtd->size) { ++ printf("Error: size '0x%llx' is too large\n", *size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int do_nmbm_erase(struct mtd_info *mtd, uint64_t offset, uint64_t size) ++{ ++ struct erase_info ei; ++ int ret; ++ ++ memset(&ei, 0, sizeof(ei)); ++ ++ ei.mtd = mtd; ++ ei.addr = offset; ++ ei.len = size; ++ ++ printf("Erasing from 0x%llx, size 0x%llx ...\n", offset, size); ++ ++ ret = mtd_erase(mtd, &ei); ++ ++ if (!ret) { ++ printf("Succeeded\n"); ++ return CMD_RET_SUCCESS; ++ } ++ ++ printf("Failed at 0x%llx\n", ei.fail_addr); ++ ++ return CMD_RET_FAILURE; ++} ++ ++static int do_nmbm_rw(int read, struct mtd_info *mtd, uintptr_t addr, ++ uint64_t offset, size_t size) ++{ ++ size_t retlen; ++ int ret; ++ ++ printf("%s 0x%llx, size 0x%zx\n", read ? "Reading from" : "Writing to", ++ offset, size); ++ ++ if (read) ++ ret = mtd_read(mtd, offset, size, &retlen, (void *)addr); ++ else ++ ret = mtd_write(mtd, offset, size, &retlen, (void *)addr); ++ ++ if (!ret) { ++ printf("Succeeded\n"); ++ return CMD_RET_SUCCESS; ++ } ++ ++ printf("Failed at 0x%llx\n", offset + retlen); ++ ++ return CMD_RET_FAILURE; ++} ++ ++static int do_nmbm_mtd_boot(struct cmd_tbl *cmdtp, struct mtd_info *mtd, ++ int argc, char *const argv[]) ++{ ++ bool print_image_contents = true; ++ uintptr_t loadaddr = image_load_addr; ++ char *end, *image_name; ++ const char *ep; ++ size_t retlen; ++ uint32_t size; ++ uint64_t off; ++ int ret; ++ ++#if defined(CONFIG_CMD_MTDPARTS) ++ struct mtd_device *partdev; ++ struct mtd_info *partmtd; ++ struct part_info *part; ++ u8 pnum; ++#endif ++ ++ ep = env_get("autostart"); ++ ++ if (ep && !strcmp(ep, "yes")) ++ print_image_contents = false; ++ ++ if (argc == 2) { ++ loadaddr = simple_strtoul(argv[0], &end, 0); ++ if (*end || end == argv[0]) { ++ printf("'%s' is not a valid address\n", argv[0]); ++ return CMD_RET_FAILURE; ++ } ++ ++ argc--; ++ argv++; ++ } ++ ++ off = simple_strtoull(argv[0], &end, 0); ++ if (*end || end == argv[0]) { ++#if defined(CONFIG_CMD_MTDPARTS) ++ ret = mtdparts_init(); ++ if (ret) ++ return CMD_RET_FAILURE; ++ ++ ret = find_dev_and_part(argv[0], &partdev, &pnum, &part); ++ if (ret) ++ return CMD_RET_FAILURE; ++ ++ if (partdev->id->type != MTD_DEV_TYPE_NMBM) { ++ printf("'%s' is not a NMBM device partition\n", ++ argv[0]); ++ return CMD_RET_FAILURE; ++ } ++ ++ partmtd = nmbm_mtd_get_upper_by_index(partdev->id->num); ++ ++ if (partmtd != mtd) { ++ printf("'%s' does not belong to this device\n", ++ argv[0]); ++ return CMD_RET_FAILURE; ++ } ++ ++ off = part->offset; ++#else ++ printf("'%s' is not a valid offset\n", argv[0]); ++ return CMD_RET_FAILURE; ++#endif ++ } ++ ++ ret = mtd_read(mtd, off, sizeof(image_header_t), &retlen, ++ (void *)loadaddr); ++ if (ret || retlen != sizeof(image_header_t)) { ++ printf("Failed to read NMBM at offset 0x%08llx\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ switch (genimg_get_format((void *)loadaddr)) { ++#if defined(CONFIG_LEGACY_IMAGE_FORMAT) ++ case IMAGE_FORMAT_LEGACY: ++ size = image_get_image_size((image_header_t *)loadaddr); ++ image_name = "legacy"; ++ break; ++#endif ++#if defined(CONFIG_FIT) ++ case IMAGE_FORMAT_FIT: ++ size = fit_get_size((const void *)loadaddr); ++ image_name = "FIT"; ++ break; ++#endif ++ default: ++ printf("Error: no Image found at offset 0x%08llx\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ printf("Loading %s image at offset 0x%llx to memory 0x%08lx, size 0x%x ...\n", ++ image_name, off, loadaddr, size); ++ ++ ret = mtd_read(mtd, off, size, &retlen, (void *)loadaddr); ++ if (ret || retlen != size) { ++ printf("Error: Failed to load image at offset 0x%08llx\n", ++ off + retlen); ++ return CMD_RET_FAILURE; ++ } ++ ++ switch (genimg_get_format((void *)loadaddr)) { ++#if defined(CONFIG_LEGACY_IMAGE_FORMAT) ++ case IMAGE_FORMAT_LEGACY: ++ if (print_image_contents) ++ image_print_contents((void *)loadaddr); ++ break; ++#endif ++#if defined(CONFIG_FIT) ++ case IMAGE_FORMAT_FIT: ++ if (print_image_contents) ++ fit_print_contents((void *)loadaddr); ++ break; ++#endif ++ } ++ ++ image_load_addr = loadaddr; ++ ++ return bootm_maybe_autostart(cmdtp, "nmbm"); ++} ++ ++static int do_nmbm(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd; ++ uint64_t offset, size; ++ char *end; ++ uintptr_t addr; ++ int ret, all = 0; ++ ++ if (argc == 1) ++ return CMD_RET_USAGE; ++ ++ if (!strcmp(argv[1], "list")) { ++ nmbm_mtd_list_devices(); ++ return CMD_RET_SUCCESS; ++ } ++ ++ if (argc < 3) ++ return CMD_RET_USAGE; ++ ++ if (!strcmp(argv[2], "info")) ++ return !!nmbm_mtd_print_info(argv[1]); ++ ++ if (!strcmp(argv[2], "state")) ++ return !!nmbm_mtd_print_states(argv[1]); ++ ++ if (!strcmp(argv[2], "bad")) ++ return !!nmbm_mtd_print_bad_blocks(argv[1]); ++ ++ if (!strcmp(argv[2], "mapping")) { ++ if (argc >= 4) { ++ if (!strcmp(argv[3], "all")) ++ all = 1; ++ } ++ ++ return nmbm_mtd_print_mappings(argv[1], all); ++ } ++ ++ if (argc < 4) ++ return CMD_RET_USAGE; ++ ++ mtd = get_mtd_device_nm(argv[1]); ++ if (IS_ERR(mtd)) { ++ printf("Error: NMBM device '%s' not found\n", argv[1]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (mtd->type != MTD_DEV_TYPE_NMBM) { ++ printf("Error: '%s' is not a NMBM device\n", argv[1]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (!strcmp(argv[2], "boot")) ++ return do_nmbm_mtd_boot(cmdtp, mtd, argc - 3, argv + 3); ++ ++ if (argc < 5) ++ return CMD_RET_USAGE; ++ ++ if (!strcmp(argv[2], "erase")) { ++ ret = nmbm_parse_offset_size(mtd, argv[3], argv[4], &offset, ++ &size); ++ if (ret) ++ return CMD_RET_FAILURE; ++ ++ return do_nmbm_erase(mtd, offset, size); ++ } ++ ++ if (argc < 6) ++ return CMD_RET_USAGE; ++ ++ ret = nmbm_parse_offset_size(mtd, argv[4], argv[5], &offset, &size); ++ if (ret) ++ return CMD_RET_FAILURE; ++ ++ if (size > SIZE_MAX) { ++ printf("Error: size 0x%llx is too large\n", size); ++ return -EINVAL; ++ } ++ ++ addr = simple_strtoul(argv[3], &end, 16); ++ if (end == argv[3]) { ++ printf("Error: addr '%s' is invalid\n", argv[3]); ++ return -EINVAL; ++ } ++ ++ if (!strcmp(argv[2], "read")) ++ return do_nmbm_rw(1, mtd, addr, offset, (size_t)size); ++ ++ if (!strcmp(argv[2], "write")) ++ return do_nmbm_rw(0, mtd, addr, offset, (size_t)size); ++ ++ return CMD_RET_USAGE; ++} ++ ++U_BOOT_CMD( ++ nmbm, CONFIG_SYS_MAXARGS, 0, do_nmbm, ++ "NMBM utility commands", ++ "\n" ++ "nmbm list - List NMBM devices\n" ++ "nmbm info - Display NMBM information\n" ++ "nmbm state - Display block states\n" ++ "nmbm bad - Display bad blocks\n" ++ "nmbm boot - Boot from NMBM\n" ++ "nmbm mapping [all] - Display block mapping\n" ++ "nmbm erase - Erase blocks\n" ++ "nmbm read - Read data\n" ++ "nmbm write - Write data\n" ++); diff --git a/package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch b/package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch new file mode 100644 index 00000000000..6e38ec4ac90 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch @@ -0,0 +1,80 @@ +From 6dbbc8affb6ab22f940d13d0e928d5e881127ca4 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 11:22:57 +0800 +Subject: [PATCH 44/71] cmd: mtd: add markbad subcommand for NMBM testing + +This patch adds: +* Mark bad block on lower mtd device and erase on upper mtd +device, which will trigger remapping: +$ mtd markbad spi-nand0 0x20000 (mark block1 as bad) +$ mtd erase nmbm0 0x20000 0x20000 (let nmbm detect the bad block and remap it) + +* Clear bad block mark through: +$ mtd erase.dontskipbad spi-nand0 0x20000 0x20000 +(After cleaning bad block mark, we need to rebuild nmbm manage table.) + +Signed-off-by: SkyLake.Huang +--- + cmd/mtd.c | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +--- a/cmd/mtd.c ++++ b/cmd/mtd.c +@@ -492,6 +492,42 @@ out_put_mtd: + return CMD_RET_SUCCESS; + } + ++static int do_mtd_markbad(struct cmd_tbl *cmdtp, int flag, int argc, ++ char * const argv[]) ++{ ++ struct mtd_info *mtd; ++ loff_t off; ++ int ret; ++ ++ if (argc < 3) ++ return CMD_RET_USAGE; ++ ++ mtd = get_mtd_by_name(argv[1]); ++ if (IS_ERR(mtd) || !mtd) ++ return CMD_RET_FAILURE; ++ ++ if (!mtd_can_have_bb(mtd)) { ++ printf("Only NAND-based devices can have mark blocks\n"); ++ goto out_put_mtd; ++ } ++ ++ off = simple_strtoull(argv[2], NULL, 0); ++ ++ ret = mtd_block_markbad(mtd, off); ++ if (!ret) { ++ printf("MTD device %s block at 0x%08llx marked bad\n", ++ mtd->name, off); ++ } else { ++ printf("MTD device %s block at 0x%08llx mark bad failed\n", ++ mtd->name, off); ++ } ++ ++out_put_mtd: ++ put_mtd_device(mtd); ++ ++ return CMD_RET_SUCCESS; ++} ++ + #ifdef CONFIG_AUTO_COMPLETE + static int mtd_name_complete(int argc, char *const argv[], char last_char, + int maxv, char *cmdv[]) +@@ -540,6 +576,7 @@ static char mtd_help_text[] = + "\n" + "Specific functions:\n" + "mtd bad \n" ++ "mtd markbad \n" + "\n" + "With:\n" + "\t: NAND partition/chip name (or corresponding DM device name or OF path)\n" +@@ -565,4 +602,6 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils" + U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase, + mtd_name_complete), + U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad, ++ mtd_name_complete), ++ U_BOOT_SUBCMD_MKENT_COMPLETE(markbad, 3, 1, do_mtd_markbad, + mtd_name_complete)); diff --git a/package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch b/package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch new file mode 100644 index 00000000000..2791332b049 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch @@ -0,0 +1,280 @@ +From 240d98e6ad0aed3c11236aa40a60bbd6fe01fae5 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:50:46 +0800 +Subject: [PATCH 45/71] env: add support for NMBM upper MTD layer + +Add an env driver for NMBM upper MTD layer + +Signed-off-by: Weijie Gao +--- + cmd/nvedit.c | 3 +- + env/Kconfig | 19 ++++- + env/Makefile | 1 + + env/env.c | 3 + + env/nmbm.c | 155 +++++++++++++++++++++++++++++++++++++++++ + include/env_internal.h | 1 + + tools/Makefile | 1 + + 7 files changed, 180 insertions(+), 3 deletions(-) + create mode 100644 env/nmbm.c + +--- a/cmd/nvedit.c ++++ b/cmd/nvedit.c +@@ -50,6 +50,7 @@ DECLARE_GLOBAL_DATA_PTR; + defined(CONFIG_ENV_IS_IN_EXT4) || \ + defined(CONFIG_ENV_IS_IN_MTD) || \ + defined(CONFIG_ENV_IS_IN_NAND) || \ ++ defined(CONFIG_ENV_IS_IN_NMBM) || \ + defined(CONFIG_ENV_IS_IN_NVRAM) || \ + defined(CONFIG_ENV_IS_IN_ONENAND) || \ + defined(CONFIG_ENV_IS_IN_SATA) || \ +@@ -64,7 +65,7 @@ DECLARE_GLOBAL_DATA_PTR; + #if !defined(ENV_IS_IN_DEVICE) && \ + !defined(CONFIG_ENV_IS_NOWHERE) + # error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\ +-NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE ++NAND|NMBM|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE + #endif + + /* +--- a/env/Kconfig ++++ b/env/Kconfig +@@ -37,7 +37,7 @@ config ENV_IS_NOWHERE + !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \ + !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \ + !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \ +- !ENV_IS_IN_UBI && !ENV_IS_IN_MTD ++ !ENV_IS_IN_UBI && !ENV_IS_IN_NMBM && !ENV_IS_IN_MTD + help + Define this if you don't want to or can't have an environment stored + on a storage medium. In this case the environment will still exist +@@ -285,6 +285,21 @@ config ENV_IS_IN_NAND + Currently, CONFIG_ENV_OFFSET_REDUND is not supported when + using CONFIG_ENV_OFFSET_OOB. + ++config ENV_IS_IN_NMBM ++ bool "Environment in a NMBM upper MTD layer" ++ depends on !CHAIN_OF_TRUST ++ depends on NMBM_MTD ++ help ++ Define this if you have a NMBM upper MTD which you want to use for ++ the environment. ++ ++ - CONFIG_ENV_OFFSET: ++ - CONFIG_ENV_SIZE: ++ ++ These two #defines specify the offset and size of the environment ++ area within the first NAND device. CONFIG_ENV_OFFSET must be ++ aligned to an erase block boundary. ++ + config ENV_IS_IN_NVRAM + bool "Environment in a non-volatile RAM" + depends on !CHAIN_OF_TRUST +@@ -561,7 +576,7 @@ config ENV_MTD_NAME + config ENV_OFFSET + hex "Environment offset" + depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \ +- ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD ++ ENV_IS_IN_SPI_FLASH || ENV_IS_IN_NMBM || ENV_IS_IN_MTD + default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC + default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH + default 0xF0000 if ARCH_SUNXI +--- a/env/Makefile ++++ b/env/Makefile +@@ -28,6 +28,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) + + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o ++obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NMBM) += nmbm.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o + +--- a/env/env.c ++++ b/env/env.c +@@ -75,6 +75,9 @@ static enum env_location env_locations[] + #ifdef CONFIG_ENV_IS_IN_NAND + ENVL_NAND, + #endif ++#ifdef CONFIG_ENV_IS_IN_NMBM ++ ENVL_NMBM, ++#endif + #ifdef CONFIG_ENV_IS_IN_NVRAM + ENVL_NVRAM, + #endif +--- /dev/null ++++ b/env/nmbm.c +@@ -0,0 +1,155 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_NMBM_MTD) ++#define CMD_SAVEENV ++#endif ++ ++#if defined(ENV_IS_EMBEDDED) ++env_t *env_ptr = &environment; ++#else /* ! ENV_IS_EMBEDDED */ ++env_t *env_ptr; ++#endif /* ENV_IS_EMBEDDED */ ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++static int env_nmbm_init(void) ++{ ++#if defined(ENV_IS_EMBEDDED) ++ int crc1_ok = 0, crc2_ok = 0; ++ env_t *tmp_env1; ++ ++ tmp_env1 = env_ptr; ++ crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc; ++ ++ if (!crc1_ok && !crc2_ok) { ++ gd->env_addr = 0; ++ gd->env_valid = ENV_INVALID; ++ ++ return 0; ++ } else if (crc1_ok && !crc2_ok) { ++ gd->env_valid = ENV_VALID; ++ } ++ ++ if (gd->env_valid == ENV_VALID) ++ env_ptr = tmp_env1; ++ ++ gd->env_addr = (ulong)env_ptr->data; ++ ++#else /* ENV_IS_EMBEDDED */ ++ gd->env_addr = (ulong)&default_environment[0]; ++ gd->env_valid = ENV_VALID; ++#endif /* ENV_IS_EMBEDDED */ ++ ++ return 0; ++} ++ ++#ifdef CMD_SAVEENV ++static int env_nmbm_save(void) ++{ ++ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); ++ struct mtd_info *mtd; ++ struct erase_info ei; ++ int ret = 0; ++ ++ ret = env_export(env_new); ++ if (ret) ++ return ret; ++ ++ mtd = nmbm_mtd_get_upper_by_index(0); ++ if (!mtd) ++ return 1; ++ ++ printf("Erasing on NMBM...\n"); ++ memset(&ei, 0, sizeof(ei)); ++ ++ ei.mtd = mtd; ++ ei.addr = CONFIG_ENV_OFFSET; ++ ei.len = CONFIG_ENV_SIZE; ++ ++ if (mtd_erase(mtd, &ei)) ++ return 1; ++ ++ printf("Writing on NMBM... "); ++ ret = mtd_write(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, NULL, ++ (u_char *)env_new); ++ puts(ret ? "FAILED!\n" : "OK\n"); ++ ++ return !!ret; ++} ++#endif /* CMD_SAVEENV */ ++ ++static int readenv(size_t offset, u_char *buf) ++{ ++ struct mtd_info *mtd; ++ struct mtd_oob_ops ops; ++ int ret; ++ size_t len = CONFIG_ENV_SIZE; ++ ++ mtd = nmbm_mtd_get_upper_by_index(0); ++ if (!mtd) ++ return 1; ++ ++ ops.mode = MTD_OPS_AUTO_OOB; ++ ops.ooblen = 0; ++ while(len > 0) { ++ ops.datbuf = buf; ++ ops.len = min(len, (size_t)mtd->writesize); ++ ops.oobbuf = NULL; ++ ++ ret = mtd_read_oob(mtd, offset, &ops); ++ if (ret) ++ return 1; ++ ++ buf += mtd->writesize; ++ len -= mtd->writesize; ++ offset += mtd->writesize; ++ } ++ ++ return 0; ++} ++ ++static int env_nmbm_load(void) ++{ ++#if !defined(ENV_IS_EMBEDDED) ++ ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE); ++ int ret; ++ ++ ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf); ++ if (ret) { ++ env_set_default("readenv() failed", 0); ++ return -EIO; ++ } ++ ++ return env_import(buf, 1, H_EXTERNAL); ++#endif /* ! ENV_IS_EMBEDDED */ ++ ++ return 0; ++} ++ ++U_BOOT_ENV_LOCATION(nmbm) = { ++ .location = ENVL_NMBM, ++ ENV_NAME("NMBM") ++ .load = env_nmbm_load, ++#if defined(CMD_SAVEENV) ++ .save = env_save_ptr(env_nmbm_save), ++#endif ++ .init = env_nmbm_init, ++}; +--- a/include/env_internal.h ++++ b/include/env_internal.h +@@ -132,6 +132,7 @@ enum env_location { + ENVL_MMC, + ENVL_MTD, + ENVL_NAND, ++ ENVL_NMBM, + ENVL_NVRAM, + ENVL_ONENAND, + ENVL_REMOTE, +--- a/tools/Makefile ++++ b/tools/Makefile +@@ -43,6 +43,7 @@ ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y + ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y + ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y + ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y ++ENVCRC-$(CONFIG_ENV_IS_IN_NMBM) = y + ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y + ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y + CONFIG_BUILD_ENVCRC ?= $(ENVCRC-y) diff --git a/package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch new file mode 100644 index 00000000000..32b21be2551 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch @@ -0,0 +1,173 @@ +From 9e8ac4fc7125795ac5e8834aaf454fd45b99c580 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:53:03 +0800 +Subject: [PATCH 46/71] mtd: mtk-snand: add NMBM support for SPL + +Add NMBM support for mtk-snand SPL loader + +Signed-off-by: Weijie Gao +--- + drivers/mtd/mtk-snand/mtk-snand-spl.c | 127 ++++++++++++++++++++++++++ + 1 file changed, 127 insertions(+) + +--- a/drivers/mtd/mtk-snand/mtk-snand-spl.c ++++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c +@@ -13,12 +13,134 @@ + #include + #include + ++#include ++ + #include "mtk-snand.h" + + static struct mtk_snand *snf; + static struct mtk_snand_chip_info cinfo; + static u32 oobavail; + ++#ifdef CONFIG_ENABLE_NAND_NMBM ++static struct nmbm_instance *ni; ++ ++static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob, ++ enum nmbm_oob_mode mode) ++{ ++ int ret; ++ bool raw = mode == NMBM_MODE_RAW ? true : false; ++ ++ if (mode == NMBM_MODE_AUTO_OOB) { ++ ret = mtk_snand_read_page_auto_oob(snf, addr, buf, oob, ++ oobavail, NULL, false); ++ } else { ++ ret = mtk_snand_read_page(snf, addr, buf, oob, raw); ++ } ++ ++ if (ret == -EBADMSG) ++ return 1; ++ else if (ret >= 0) ++ return 0; ++ ++ return ret; ++} ++ ++static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf, ++ const void *oob, enum nmbm_oob_mode mode) ++{ ++ bool raw = mode == NMBM_MODE_RAW ? true : false; ++ ++ if (mode == NMBM_MODE_AUTO_OOB) { ++ return mtk_snand_write_page_auto_oob(snf, addr, buf, oob, ++ oobavail, NULL, false); ++ } ++ ++ return mtk_snand_write_page(snf, addr, buf, oob, raw); ++} ++ ++static int nmbm_lower_erase_block(void *arg, uint64_t addr) ++{ ++ return mtk_snand_erase_block(snf, addr); ++} ++ ++static int nmbm_lower_is_bad_block(void *arg, uint64_t addr) ++{ ++ return mtk_snand_block_isbad(snf, addr); ++} ++ ++static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr) ++{ ++ return mtk_snand_block_markbad(snf, addr); ++} ++ ++static void nmbm_lower_log(void *arg, enum nmbm_log_category level, ++ const char *fmt, va_list ap) ++{ ++ vprintf(fmt, ap); ++} ++ ++static int nmbm_init(void) ++{ ++ struct nmbm_lower_device nld; ++ size_t ni_size; ++ int ret; ++ ++ memset(&nld, 0, sizeof(nld)); ++ ++ nld.flags = NMBM_F_CREATE; ++ nld.max_ratio = CONFIG_NMBM_MAX_RATIO; ++ nld.max_reserved_blocks = CONFIG_NMBM_MAX_BLOCKS; ++ ++ nld.size = cinfo.chipsize; ++ nld.erasesize = cinfo.blocksize; ++ nld.writesize = cinfo.pagesize; ++ nld.oobsize = cinfo.sparesize; ++ nld.oobavail = oobavail; ++ ++ nld.read_page = nmbm_lower_read_page; ++ nld.write_page = nmbm_lower_write_page; ++ nld.erase_block = nmbm_lower_erase_block; ++ nld.is_bad_block = nmbm_lower_is_bad_block; ++ nld.mark_bad_block = nmbm_lower_mark_bad_block; ++ ++ nld.logprint = nmbm_lower_log; ++ ++ ni_size = nmbm_calc_structure_size(&nld); ++ ni = malloc(ni_size); ++ if (!ni) { ++ printf("Failed to allocate memory (0x%u) for NMBM instance\n", ++ ni_size); ++ return -ENOMEM; ++ } ++ ++ memset(ni, 0, ni_size); ++ ++ printf("Initializing NMBM ...\n"); ++ ++ ret = nmbm_attach(&nld, ni); ++ if (ret) { ++ ni = NULL; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) ++{ ++ size_t retlen; ++ ++ if (!ni) ++ return -ENODEV; ++ ++ nmbm_read_range(ni, offs, size, dst, NMBM_MODE_PLACE_OOB, &retlen); ++ if (retlen != size) ++ return -EIO; ++ ++ return 0; ++} ++ ++#else + static u8 *page_cache; + + int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +@@ -60,6 +182,7 @@ int nand_spl_load_image(uint32_t offs, u + + return ret; + } ++#endif + + void nand_init(void) + { +@@ -105,11 +228,15 @@ void nand_init(void) + printf("SPI-NAND: %s (%uMB)\n", cinfo.model, + (u32)(cinfo.chipsize >> 20)); + ++#ifdef CONFIG_ENABLE_NAND_NMBM ++ nmbm_init(); ++#else + page_cache = malloc(cinfo.pagesize + cinfo.sparesize); + if (!page_cache) { + mtk_snand_cleanup(snf); + printf("mtk-snand-spl: failed to allocate page cache\n"); + } ++#endif + } + + void nand_deselect(void) diff --git a/package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch b/package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch new file mode 100644 index 00000000000..bf4ed97f13d --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch @@ -0,0 +1,1118 @@ +From 88271cb3ae9c68dc200d627653df96fc557c2a64 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 10:55:35 +0800 +Subject: [PATCH 47/71] cmd: add a new command for NAND flash debugging + +Add a command 'nand-ext' for NAND flash debugging: +- Dump a page with oob, with optional raw read support +- Display all bad blocks +- Mark a block as bad block +- Set a bitflip on a page +- Erase +- Read / write data from/to any offset with any size +- Read / write pages with oob +- Erase, read and write support skip bad block or forced mode, support + raw mode, supporot auto-oob mode +- Supports operating on a specific partition +- No need to specify NAND device name + +Signed-off-by: Weijie Gao +--- + cmd/Kconfig | 8 + + cmd/Makefile | 1 + + cmd/nand-ext.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1071 insertions(+) + create mode 100644 cmd/nand-ext.c + +--- a/cmd/Kconfig ++++ b/cmd/Kconfig +@@ -1260,6 +1260,14 @@ config CMD_NAND_TORTURE + + endif # CMD_NAND + ++config CMD_NAND_EXT ++ bool "nand - extended nand utility for debugging" ++ depends on !CMD_NAND ++ default y if MTD_RAW_NAND || MTD_SPI_NAND || MTK_SPI_NAND ++ select MTD_PARTITIONS ++ help ++ NAND flash R/W and debugging support. ++ + config CMD_NMBM + depends on NMBM_MTD + bool "nmbm" +--- a/cmd/Makefile ++++ b/cmd/Makefile +@@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o + endif + obj-$(CONFIG_CMD_MUX) += mux.o + obj-$(CONFIG_CMD_NAND) += nand.o ++obj-$(CONFIG_CMD_NAND_EXT) += nand-ext.o + obj-$(CONFIG_CMD_NMBM) += nmbm.o + obj-$(CONFIG_CMD_NET) += net.o + obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o +--- /dev/null ++++ b/cmd/nand-ext.c +@@ -0,0 +1,1062 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct mtd_info *curr_dev; ++ ++static void mtd_show_parts(struct mtd_info *mtd, int level) ++{ ++ struct mtd_info *part; ++ int i; ++ ++ list_for_each_entry(part, &mtd->partitions, node) { ++ for (i = 0; i < level; i++) ++ printf("\t"); ++ printf(" - 0x%012llx-0x%012llx : \"%s\"\n", ++ part->offset, part->offset + part->size, part->name); ++ ++ mtd_show_parts(part, level + 1); ++ } ++} ++ ++static void mtd_show_device(struct mtd_info *mtd) ++{ ++ /* Device */ ++ printf("* %s\n", mtd->name); ++#if defined(CONFIG_DM) ++ if (mtd->dev) { ++ printf(" - device: %s\n", mtd->dev->name); ++ printf(" - parent: %s\n", mtd->dev->parent->name); ++ printf(" - driver: %s\n", mtd->dev->driver->name); ++ } ++#endif ++ ++ /* MTD device information */ ++ printf(" - type: "); ++ switch (mtd->type) { ++ case MTD_NANDFLASH: ++ printf("NAND flash\n"); ++ break; ++ case MTD_MLCNANDFLASH: ++ printf("MLC NAND flash\n"); ++ break; ++ case MTD_ABSENT: ++ default: ++ printf("Not supported\n"); ++ break; ++ } ++ ++ printf(" - block size: 0x%x bytes\n", mtd->erasesize); ++ printf(" - page size: 0x%x bytes\n", mtd->writesize); ++ printf(" - OOB size: %u bytes\n", mtd->oobsize); ++ printf(" - OOB available: %u bytes\n", mtd->oobavail); ++ ++ if (mtd->ecc_strength) { ++ printf(" - ECC strength: %u bits\n", mtd->ecc_strength); ++ printf(" - ECC step size: %u bytes\n", mtd->ecc_step_size); ++ printf(" - bitflip threshold: %u bits\n", ++ mtd->bitflip_threshold); ++ } ++ ++ printf(" - 0x%012llx-0x%012llx : \"%s\"\n", ++ mtd->offset, mtd->offset + mtd->size, mtd->name); ++ ++ /* MTD partitions, if any */ ++ mtd_show_parts(mtd, 1); ++} ++ ++static int do_nand_list(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd; ++ int dev_nb = 0; ++ ++ /* Ensure all devices (and their partitions) are probed */ ++ mtd_probe_devices(); ++ ++ printf("List of NAND devices:\n"); ++ mtd_for_each_device(mtd) { ++ if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) ++ continue; ++ ++ if (!mtd_is_partition(mtd)) ++ mtd_show_device(mtd); ++ ++ dev_nb++; ++ } ++ ++ if (!dev_nb) ++ printf("No NAND MTD device found\n"); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static struct mtd_info *nand_get_curr_dev(void) ++{ ++ struct mtd_info *mtd, *first_dev = NULL; ++ int err, dev_nb = 0; ++ ++ if (curr_dev) { ++ mtd = get_mtd_device(curr_dev, -1); ++ if (!IS_ERR_OR_NULL(mtd)) { ++ __put_mtd_device(mtd); ++ return mtd; ++ } ++ ++ curr_dev = NULL; ++ } ++ ++ /* Ensure all devices (and their partitions) are probed */ ++ mtd_probe_devices(); ++ ++ mtd_for_each_device(mtd) { ++ if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) ++ continue; ++ ++ if (!mtd_is_partition(mtd)) { ++ if (!first_dev) ++ first_dev = mtd; ++ dev_nb++; ++ } ++ } ++ ++ if (!dev_nb) { ++ printf("No NAND MTD device found\n"); ++ return NULL; ++ } ++ ++ if (dev_nb > 1) { ++ printf("No active NAND MTD device specified\n"); ++ return NULL; ++ } ++ ++ err = __get_mtd_device(first_dev); ++ if (err) { ++ printf("Failed to get MTD device '%s': err %d\n", ++ first_dev->name, err); ++ return NULL; ++ } ++ ++ curr_dev = first_dev; ++ ++ printf("'%s' is now active device\n", first_dev->name); ++ ++ return curr_dev; ++} ++ ++static struct mtd_info *nand_get_part(struct mtd_info *master, ++ const char *name) ++{ ++ struct mtd_info *slave; ++ ++ list_for_each_entry(slave, &master->partitions, node) { ++ if (!strcmp(slave->name, name)) ++ return slave; ++ } ++ ++ return NULL; ++} ++ ++static int do_nand_info(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(); ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ mtd_show_device(mtd); ++ ++ return 0; ++} ++ ++static int do_nand_select(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd, *old; ++ ++ if (argc < 2) { ++ printf("MTD device name must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ mtd = get_mtd_device_nm(argv[1]); ++ if (!mtd) { ++ printf("MTD device '%s' not found\n", argv[1]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (mtd_is_partition(mtd)) { ++ printf("Error: '%s' is a MTD partition\n", argv[1]); ++ __put_mtd_device(mtd); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) { ++ printf("Error: '%s' is not a NAND device\n", argv[1]); ++ __put_mtd_device(mtd); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (mtd == curr_dev) { ++ __put_mtd_device(mtd); ++ return CMD_RET_SUCCESS; ++ } ++ ++ if (curr_dev) { ++ old = get_mtd_device(curr_dev, -1); ++ if (!IS_ERR_OR_NULL(old)) { ++ __put_mtd_device(old); ++ __put_mtd_device(curr_dev); ++ } ++ ++ curr_dev = NULL; ++ } ++ ++ curr_dev = mtd; ++ ++ printf("'%s' is now active device\n", curr_dev->name); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static void dump_buf(const u8 *data, size_t size, u64 addr) ++{ ++ const u8 *p = data; ++ u32 i, chklen; ++ ++ while (size) { ++ chklen = 16; ++ if (chklen > size) ++ chklen = (u32)size; ++ ++ printf("%08llx: ", addr); ++ ++ for (i = 0; i < chklen; i++) { ++ if (i && (i % 4 == 0)) ++ printf(" "); ++ ++ printf("%02x ", p[i]); ++ } ++ ++ for (i = chklen; i < 16; i++) { ++ if (i && (i % 4 == 0)) ++ printf(" "); ++ ++ printf(" "); ++ } ++ printf(" "); ++ ++ for (i = 0; i < chklen; i++) { ++ if (p[i] < 32 || p[i] >= 0x7f) ++ printf("."); ++ else ++ printf("%c", p[i]); ++ } ++ printf("\n"); ++ ++ p += chklen; ++ size -= chklen; ++ addr += chklen; ++ } ++} ++ ++static int do_nand_dump(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(); ++ struct mtd_oob_ops io_op = {}; ++ bool raw = false; ++ int ret; ++ u64 off; ++ u8 *buf; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (strstr(argv[0], ".raw")) ++ raw = true; ++ ++ if (argc < 2) { ++ printf("Dump offset must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ off = simple_strtoull(argv[1], NULL, 0); ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ off &= ~(u64)mtd->writesize_mask; ++ ++ buf = malloc(mtd->writesize + mtd->oobsize); ++ if (!buf) { ++ printf("Failed to allocate buffer\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = buf; ++ io_op.ooblen = mtd->oobsize; ++ io_op.oobbuf = buf + mtd->writesize; ++ ++ ret = mtd_read_oob(mtd, off, &io_op); ++ if (ret < 0 && ret != -EUCLEAN && ret != -EBADMSG) { ++ printf("Failed to read page at 0x%llx, err %d\n", off, ret); ++ free(buf); ++ return CMD_RET_FAILURE; ++ } ++ ++ printf("Dump of %spage at 0x%llx:\n", raw ? "raw " : "", off); ++ dump_buf(buf, mtd->writesize, off); ++ ++ printf("\n"); ++ printf("OOB:\n"); ++ dump_buf(buf + mtd->writesize, mtd->oobsize, 0); ++ ++ free(buf); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static int do_nand_bad(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(); ++ u64 off = 0; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ while (off < mtd->size) { ++ if (mtd_block_isbad(mtd, off)) ++ printf("\t%08llx\n", off); ++ ++ off += mtd->erasesize; ++ } ++ ++ return 0; ++} ++ ++static int do_nand_markbad(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(); ++ u64 off; ++ int ret; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (argc < 2) { ++ printf("Missing address within a block to be marked bad\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ off = simple_strtoull(argv[1], NULL, 0); ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ off &= ~(u64)mtd->erasesize_mask; ++ ++ ret = mtd_block_markbad(mtd, off); ++ ++ if (!ret) ++ printf("Block at 0x%08llx has been marked bad\n", off); ++ else ++ printf("Failed to mark bad block at 0x%08llx\n", off); ++ ++ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; ++} ++ ++static int do_nand_bitflip(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(); ++ struct mtd_oob_ops io_op = {}; ++ u32 col, bit; ++ bool res; ++ u64 off; ++ u8 *buf; ++ int ret; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (argc < 2) { ++ printf("Missing address to generate bitflip\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ off = simple_strtoull(argv[1], NULL, 0); ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 3) { ++ printf("Missing column address\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ col = simple_strtoul(argv[2], NULL, 0); ++ if (col >= mtd->writesize + mtd->oobsize) { ++ printf("Column address must be less than %u\n", ++ mtd->writesize + mtd->oobsize); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 4) { ++ printf("Missing bit position\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ bit = simple_strtoul(argv[3], NULL, 0); ++ if (bit > 7) { ++ printf("Bit position must be less than 8\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ off &= ~(u64)mtd->writesize_mask; ++ ++ buf = malloc(mtd->writesize + mtd->oobsize); ++ if (!buf) { ++ printf("Failed to allocate buffer\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ io_op.mode = MTD_OPS_RAW; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = buf; ++ io_op.ooblen = mtd->oobsize; ++ io_op.oobbuf = buf + mtd->writesize; ++ ++ ret = mtd_read_oob(mtd, off, &io_op); ++ if (ret < 0) { ++ printf("Failed to read page at 0x%llx, err %d\n", off, ret); ++ free(buf); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (!(buf[col] & (1 << bit))) { ++ printf("Bit %u at byte %u is already zero\n", bit, col); ++ free(buf); ++ return CMD_RET_FAILURE; ++ } ++ ++ buf[col] &= ~(1 << bit); ++ ++ memset(&io_op, 0, sizeof(io_op)); ++ io_op.mode = MTD_OPS_RAW; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = buf; ++ io_op.ooblen = mtd->oobsize; ++ io_op.oobbuf = buf + mtd->writesize; ++ ++ ret = mtd_write_oob(mtd, off, &io_op); ++ ++ if (ret < 0) { ++ printf("Failed to write page at 0x%llx, err %d\n", off, ret); ++ return CMD_RET_FAILURE; ++ } ++ ++ memset(&io_op, 0, sizeof(io_op)); ++ io_op.mode = MTD_OPS_RAW; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = buf; ++ io_op.ooblen = mtd->oobsize; ++ io_op.oobbuf = buf + mtd->writesize; ++ ++ ret = mtd_read_oob(mtd, off, &io_op); ++ if (ret < 0) { ++ printf("Failed to read page at 0x%llx, err %d\n", off, ret); ++ free(buf); ++ return CMD_RET_FAILURE; ++ } ++ ++ res = (buf[col] & (1 << bit)) == 0; ++ free(buf); ++ ++ if (res) { ++ printf("Bit %u at byte %u has been changed to 0\n", bit, col); ++ return CMD_RET_SUCCESS; ++ } ++ ++ printf("Failed to change bit %u at byte %u to 0\n", bit, col); ++ return CMD_RET_FAILURE; ++} ++ ++static int do_nand_erase(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(), *part; ++ bool spread = false, force = false; ++ u64 off, size, end, limit; ++ struct erase_info ei; ++ char *ends; ++ int ret; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (strstr(argv[0], ".spread")) ++ spread = true; ++ ++ if (strstr(argv[0], ".force")) ++ force = true; ++ ++ if (spread && force) { ++ printf("spread and force must not be set at the same time\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 2) { ++ printf("Erase start offset/partition must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ part = nand_get_part(mtd, argv[1]); ++ if (part) { ++ off = part->offset; ++ ++ if (argc < 3) ++ size = part->size; ++ else ++ size = simple_strtoull(argv[2], NULL, 0); ++ ++ if (size > part->size) { ++ printf("Erase end offset is larger than partition size\n"); ++ printf("Erase size reduced to 0x%llx\n", part->size); ++ ++ size = part->size; ++ } ++ ++ limit = off + part->size; ++ } else { ++ off = simple_strtoull(argv[1], &ends, 0); ++ ++ if (ends == argv[1] || *ends) { ++ printf("Partition '%s' not found\n", argv[1]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 3) { ++ printf("Erase size offset must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ size = simple_strtoull(argv[2], NULL, 0); ++ ++ if (off + size > mtd->size) { ++ printf("Erase end offset is larger than flash size\n"); ++ ++ size = mtd->size - off; ++ printf("Erase size reduced to 0x%llx\n", size); ++ } ++ ++ limit = mtd->size; ++ } ++ ++ end = off + size; ++ off &= ~(u64)mtd->erasesize_mask; ++ end = (end + mtd->erasesize_mask) & (~(u64)mtd->erasesize_mask); ++ size = end - off; ++ ++ printf("Erasing from 0x%llx to 0x%llx, size 0x%llx ...\n", ++ off, end - 1, end - off); ++ ++ while (size && off < limit) { ++ if (mtd_block_isbad(mtd, off)) { ++ printf("Bad block at 0x%llx", off); ++ ++ if (spread) { ++ printf(" ... skipped\n"); ++ off += mtd->erasesize; ++ continue; ++ } ++ ++ if (!force) { ++ printf(" ... aborted\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ printf(" ... will be force erased\n"); ++ } ++ ++ memset(&ei, 0, sizeof(ei)); ++ ++ ei.mtd = mtd; ++ ei.addr = off; ++ ei.len = mtd->erasesize; ++ ei.scrub = force; ++ ++ ret = mtd_erase(mtd, &ei); ++ if (ret) { ++ printf("Erase failed at 0x%llx\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ off += mtd->erasesize; ++ size -= mtd->erasesize; ++ } ++ ++ printf("Succeeded\n"); ++ ++ return CMD_RET_SUCCESS; ++} ++ ++static bool is_empty_page(const u8 *buf, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; i++) { ++ if (buf[i] != 0xff) ++ return false; ++ } ++ ++ return true; ++} ++ ++static int do_nand_io_normal(int argc, char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(), *part; ++ bool spread = false, force = false, raw = false, writeff = false; ++ bool read = false, checkbad = true; ++ struct mtd_oob_ops io_op = {}; ++ size_t size, padding, chksz; ++ uintptr_t addr; ++ u64 off, offp; ++ char *ends; ++ u8 *buf; ++ int ret; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (!strncmp(argv[0], "read", 4)) ++ read = true; ++ ++ if (strstr(argv[0], ".spread")) ++ spread = true; ++ ++ if (strstr(argv[0], ".force")) ++ force = true; ++ ++ if (strstr(argv[0], ".raw")) ++ raw = true; ++ ++ if (strstr(argv[0], ".ff")) ++ writeff = true; ++ ++ if (spread && force) { ++ printf("spread and force must not be set at the same time\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 2) { ++ printf("Data address must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ addr = simple_strtoul(argv[1], NULL, 0); ++ ++ if (argc < 3) { ++ printf("Flash address/partition must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ part = nand_get_part(mtd, argv[2]); ++ if (part) { ++ if (argc < 4) { ++ off = 0; ++ } else { ++ off = simple_strtoull(argv[3], NULL, 0); ++ if (off + part->offset >= part->size) { ++ printf("Offset is larger than partition size\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ if (argc < 5) { ++ size = part->size - off; ++ } else { ++ size = simple_strtoul(argv[4], NULL, 0); ++ if (off + size > part->size) { ++ printf("Data size is too large\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ off += part->offset; ++ } else { ++ off = simple_strtoull(argv[2], &ends, 0); ++ ++ if (ends == argv[1] || *ends) { ++ printf("Partition '%s' not found\n", argv[2]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 4) { ++ printf("Data size must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ size = simple_strtoul(argv[3], NULL, 0); ++ if (off + size > mtd->size) { ++ printf("Data size is too large\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ buf = malloc(mtd->writesize); ++ if (!buf) { ++ printf("Failed to allocate buffer\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ printf("%s from 0x%llx to 0x%llx, size 0x%zx ...\n", ++ read ? "Reading" : "Writing", off, off + size - 1, size); ++ ++ while (size && off < mtd->size) { ++ if (checkbad || !(off & mtd->erasesize_mask)) { ++ offp = off & ~(u64)mtd->erasesize_mask; ++ ++ if (mtd_block_isbad(mtd, offp)) { ++ printf("Bad block at 0x%llx", offp); ++ ++ if (spread) { ++ printf(" ... skipped\n"); ++ off += mtd->erasesize; ++ checkbad = true; ++ continue; ++ } ++ ++ if (!force) { ++ printf(" ... aborted\n"); ++ goto err_out; ++ } ++ ++ printf(" ... continue\n"); ++ } ++ ++ checkbad = false; ++ } ++ ++ padding = off & mtd->writesize_mask; ++ chksz = mtd->writesize - padding; ++ chksz = min_t(size_t, chksz, size); ++ ++ offp = off & ~(u64)mtd->writesize_mask; ++ ++ memset(&io_op, 0, sizeof(io_op)); ++ io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB; ++ io_op.len = mtd->writesize; ++ ++ if (chksz < mtd->writesize) ++ io_op.datbuf = buf; ++ else ++ io_op.datbuf = (void *)addr; ++ ++ if (read) { ++ ret = mtd_read_oob(mtd, offp, &io_op); ++ if (ret && ret != -EUCLEAN && ret != -EBADMSG) ++ goto io_err; ++ ++ if (chksz < mtd->writesize) ++ memcpy((void *)addr, buf + padding, chksz); ++ } else { ++ if (chksz < mtd->writesize) { ++ memset(buf, 0xff, mtd->writesize); ++ memcpy(buf + padding, (void *)addr, chksz); ++ } ++ ++ if (is_empty_page(io_op.datbuf, io_op.len) && !writeff) ++ ret = 0; ++ else ++ ret = mtd_write_oob(mtd, offp, &io_op); ++ ++ if (ret) ++ goto io_err; ++ } ++ ++ size -= chksz; ++ addr += chksz; ++ off += chksz; ++ } ++ ++ if (!size) { ++ printf("Succeeded\n"); ++ ret = CMD_RET_SUCCESS; ++ goto out; ++ } ++ ++ printf("0x%zx byte%s remained for %s\n", size, size > 1 ? "s" : "", ++ read ? "read" : "write"); ++ goto err_out; ++ ++io_err: ++ printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, offp); ++ ++err_out: ++ ret = CMD_RET_FAILURE; ++ ++out: ++ free(buf); ++ return ret; ++} ++ ++static int do_nand_io_page(int argc, char *const argv[]) ++{ ++ struct mtd_info *mtd = nand_get_curr_dev(), *part; ++ bool spread = false, force = false, raw = false, autooob = false; ++ bool read = false, checkbad = true, writeff = false; ++ struct mtd_oob_ops io_op = {}; ++ uintptr_t addr; ++ u64 off, offp; ++ char *ends; ++ u32 count; ++ int ret; ++ ++ if (!mtd) ++ return CMD_RET_FAILURE; ++ ++ if (!strncmp(argv[0], "read", 4)) ++ read = true; ++ ++ if (strstr(argv[0], ".spread")) ++ spread = true; ++ ++ if (strstr(argv[0], ".force")) ++ force = true; ++ ++ if (strstr(argv[0], ".raw")) ++ raw = true; ++ ++ if (strstr(argv[0], ".auto")) ++ autooob = true; ++ ++ if (spread && force) { ++ printf("spread and force must not be set at the same time\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (raw && autooob) { ++ printf("raw and auto must not be set at the same time\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (argc < 2) { ++ printf("Data address must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ addr = simple_strtoul(argv[1], NULL, 0); ++ ++ if (argc < 3) { ++ printf("Flash address/partition must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ part = nand_get_part(mtd, argv[2]); ++ if (part) { ++ if (argc < 4) { ++ printf("Partition offset / page count must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ if (argc < 5) { ++ off = 0; ++ ++ count = simple_strtoul(argv[3], NULL, 0); ++ if (part->offset + count * mtd->writesize > part->size) { ++ printf("Page count exceeds partition size\n"); ++ return CMD_RET_FAILURE; ++ } ++ } else { ++ off = simple_strtoull(argv[3], NULL, 0); ++ if (off >= part->size) { ++ printf("Offset 0x%llx is larger than partition size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ off &= ~(u64)mtd->writesize_mask; ++ ++ count = simple_strtoul(argv[4], NULL, 0); ++ if (part->offset + off + count * mtd->writesize > part->size) { ++ printf("Page count exceeds partition size\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ off += part->offset; ++ } else { ++ off = simple_strtoull(argv[2], &ends, 0); ++ ++ if (ends == argv[1] || *ends) { ++ printf("Partition '%s' not found\n", argv[2]); ++ return CMD_RET_FAILURE; ++ } ++ ++ if (off >= mtd->size) { ++ printf("Offset 0x%llx is larger than flash size\n", off); ++ return CMD_RET_FAILURE; ++ } ++ ++ off &= ~(u64)mtd->writesize_mask; ++ ++ if (argc < 4) { ++ printf("Page count must be specified\n"); ++ return CMD_RET_USAGE; ++ } ++ ++ count = simple_strtoul(argv[3], NULL, 0); ++ if (off + count * mtd->writesize > mtd->size) { ++ printf("Page count exceeds flash size\n"); ++ return CMD_RET_FAILURE; ++ } ++ } ++ ++ printf("%s from 0x%llx to 0x%llx (+%u), count %u ...\n", ++ read ? "Reading" : "Writing", off, ++ off + count * mtd->writesize - 1, mtd->oobsize, count); ++ ++ while (count && off < mtd->size) { ++ if (checkbad || !(off & mtd->erasesize_mask)) { ++ offp = off & ~(u64)mtd->erasesize_mask; ++ ++ if (mtd_block_isbad(mtd, offp)) { ++ printf("Bad block at 0x%llx", offp); ++ ++ if (spread) { ++ printf(" ... skipped\n"); ++ off += mtd->erasesize; ++ checkbad = true; ++ continue; ++ } ++ ++ if (!force) { ++ printf(" ... aborted\n"); ++ return CMD_RET_FAILURE; ++ } ++ ++ printf(" ... continue\n"); ++ } ++ ++ checkbad = false; ++ } ++ ++ memset(&io_op, 0, sizeof(io_op)); ++ ++ if (raw) ++ io_op.mode = MTD_OPS_RAW; ++ else if (autooob) ++ io_op.mode = MTD_OPS_AUTO_OOB; ++ else ++ io_op.mode = MTD_OPS_PLACE_OOB; ++ ++ io_op.len = mtd->writesize; ++ io_op.ooblen = mtd->oobsize; ++ io_op.datbuf = (void *)addr; ++ io_op.oobbuf = io_op.datbuf + mtd->writesize; ++ ++ if (read) { ++ ret = mtd_read_oob(mtd, off, &io_op); ++ if (ret && ret != -EUCLEAN && ret != -EBADMSG) ++ goto io_err; ++ } else { ++ if (is_empty_page((void *)addr, mtd->writesize + mtd->oobsize) && !writeff) ++ ret = 0; ++ else ++ ret = mtd_write_oob(mtd, off, &io_op); ++ ++ if (ret) ++ goto io_err; ++ } ++ ++ count--; ++ addr += mtd->writesize + mtd->oobsize; ++ off += mtd->writesize; ++ } ++ ++ if (!count) { ++ printf("Succeeded\n"); ++ return CMD_RET_SUCCESS; ++ } ++ ++ printf("%u page%s remained for %s\n", count, count > 1 ? "s" : "", ++ read ? "read" : "write"); ++ return CMD_RET_FAILURE; ++ ++io_err: ++ printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, off); ++ return CMD_RET_FAILURE; ++} ++ ++static int do_nand_io(struct cmd_tbl *cmdtp, int flag, int argc, ++ char *const argv[]) ++{ ++ if (strstr(argv[0], ".oob")) ++ return do_nand_io_page(argc, argv); ++ ++ return do_nand_io_normal(argc, argv); ++} ++ ++#ifdef CONFIG_SYS_LONGHELP ++static char nand_help_text[] = ++ "- NAND flash R/W and debugging utility\n" ++ "nand list\n" ++ "nand info - Show active NAND devices\n" ++ "nand select - Select active NAND devices\n" ++ "nand dump[.raw] \n" ++ "nand bad\n" ++ "nand markbad \n" ++ "nand bitflip \n" ++ "nand erase[.spread|.force] [ | []]\n" ++ "nand read[.spread|.force][.raw] \n" ++ " [ []]\n" ++ "nand write[.spread|.force][.raw][.ff] \n" ++ " [ []]\n" ++ "nand read.oob[.spread|.force][.raw|.auto] \n" ++ " [] \n" ++ "nand write.oob[.spread|.force][.raw|.auto][.ff] \n" ++ " [] \n"; ++#endif ++ ++U_BOOT_CMD_WITH_SUBCMDS(nand, "NAND utility", ++ nand_help_text, ++ U_BOOT_SUBCMD_MKENT(list, 1, 0, do_nand_list), ++ U_BOOT_SUBCMD_MKENT(info, 1, 0, do_nand_info), ++ U_BOOT_SUBCMD_MKENT(select, 2, 0, do_nand_select), ++ U_BOOT_SUBCMD_MKENT(dump, 2, 0, do_nand_dump), ++ U_BOOT_SUBCMD_MKENT(bad, 1, 0, do_nand_bad), ++ U_BOOT_SUBCMD_MKENT(markbad, 2, 0, do_nand_markbad), ++ U_BOOT_SUBCMD_MKENT(bitflip, 4, 0, do_nand_bitflip), ++ U_BOOT_SUBCMD_MKENT(erase, 3, 0, do_nand_erase), ++ U_BOOT_SUBCMD_MKENT(read, 5, 0, do_nand_io), ++ U_BOOT_SUBCMD_MKENT(write, 5, 0, do_nand_io) ++); diff --git a/package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch b/package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch new file mode 100644 index 00000000000..6a61045955e --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch @@ -0,0 +1,142 @@ +From c4172a95df8a57a66c70a8b9948b9600a01c4cb7 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 11:32:08 +0800 +Subject: [PATCH 49/71] mtd: spi-nor: add support to read flash unique ID + +This patch adds support to read unique ID from spi-nor flashes. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/spi/spi-nor-core.c | 95 ++++++++++++++++++++++++++++++++++ + include/linux/mtd/spi-nor.h | 2 + + 2 files changed, 97 insertions(+) + +--- a/drivers/mtd/spi/spi-nor-core.c ++++ b/drivers/mtd/spi/spi-nor-core.c +@@ -2742,6 +2742,100 @@ static int spi_nor_init_params(struct sp + return 0; + } + ++static int spi_nor_read_uuid(struct spi_nor *nor) ++{ ++ u8 read_opcode, addr_width, read_dummy; ++ loff_t addr; ++ u8 *uuid; ++ u8 uuid_len; ++ int shift = 0; ++ int ret; ++ int i; ++ struct spi_mem_op op; ++ ++ read_opcode = nor->read_opcode; ++ addr_width = nor->addr_width; ++ read_dummy = nor->read_dummy; ++ ++ switch (JEDEC_MFR(nor->info)) { ++ case SNOR_MFR_WINBOND: ++ uuid_len = 8; ++ nor->read_opcode = 0x4b; ++ nor->addr_width = 0; ++ addr = 0x0; ++ nor->read_dummy = 4; ++ break; ++ case SNOR_MFR_GIGADEVICE: ++ uuid_len = 16; ++ nor->read_opcode = 0x4b; ++ nor->addr_width = 3; ++ addr = 0x0; ++ nor->read_dummy = 1; ++ break; ++ case CFI_MFR_ST: ++ case SNOR_MFR_MICRON: ++ uuid_len = 17; ++ shift = 3; ++ nor->read_opcode = 0x9f; ++ nor->addr_width = 0; ++ addr = 0x0; ++ nor->read_dummy = 0; ++ break; ++ case SNOR_MFR_EON: ++ uuid_len = 12; ++ nor->read_opcode = 0x5a; ++ nor->addr_width = 3; ++ addr = 0x80; ++ nor->read_dummy = 1; ++ break; ++ /* Automotive only in SPANSION's NOR devices */ ++ case SNOR_MFR_SPANSION: ++ uuid_len = 11; ++ shift = 386; ++ nor->read_opcode = 0x9f; ++ nor->addr_width = 0; ++ addr = 0x0; ++ nor->read_dummy = 0; ++ break; ++ default: ++ printf("UUID not supported on this device.\n"); ++ return -ENOTSUPP; ++ } ++ ++ uuid = kmalloc((uuid_len + shift) * sizeof(*uuid), GFP_KERNEL); ++ if (!uuid) { ++ ret = -ENOMEM; ++ goto read_err; ++ } ++ memset(uuid, 0x0, (uuid_len + shift) * sizeof(*uuid)); ++ ++ op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0), ++ SPI_MEM_OP_ADDR(nor->addr_width, addr, 0), ++ SPI_MEM_OP_DUMMY(nor->read_dummy, 0), ++ SPI_MEM_OP_DATA_IN(uuid_len+shift, NULL, 0)); ++ ++ spi_nor_setup_op(nor, &op, nor->reg_proto); ++ ++ ret = spi_nor_read_write_reg(nor, &op, uuid); ++ if (ret < 0) { ++ dev_dbg(nor->dev, "error %d reading %x\n", ret, nor->read_opcode); ++ goto read_err; ++ } ++ ++ printf("UUID: 0x"); ++ for(i = 0; iread_opcode = read_opcode; ++ nor->addr_width = addr_width; ++ nor->read_dummy = read_dummy; ++ kfree(uuid); ++ ++ return ret; ++} ++ + static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size) + { + size_t i; +@@ -3719,6 +3813,7 @@ int spi_nor_scan(struct spi_nor *nor) + nor->write = spi_nor_write_data; + nor->read_reg = spi_nor_read_reg; + nor->write_reg = spi_nor_write_reg; ++ nor->read_uuid = spi_nor_read_uuid; + + nor->setup = spi_nor_default_setup; + +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -28,6 +28,7 @@ + #define SNOR_MFR_SPANSION CFI_MFR_AMD + #define SNOR_MFR_SST CFI_MFR_SST + #define SNOR_MFR_WINBOND 0xef /* Also used by some Spansion */ ++#define SNOR_MFR_EON CFI_MFR_EON + #define SNOR_MFR_CYPRESS 0x34 + + /* +@@ -547,6 +548,7 @@ struct spi_nor { + void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops); + int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); + int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len); ++ int (*read_uuid)(struct spi_nor *nor); + + ssize_t (*read)(struct spi_nor *nor, loff_t from, + size_t len, u_char *read_buf); diff --git a/package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch b/package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch new file mode 100644 index 00000000000..972a022d4ba --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch @@ -0,0 +1,48 @@ +From e60939acbebd07161f3978d1c6f13123fdd2ebf2 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 11:27:02 +0800 +Subject: [PATCH 50/71] cmd: sf: add support to read flash unique ID + +This patch adds support to display unique ID from spi-nor flashes + +Signed-off-by: Weijie Gao +--- + cmd/sf.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +--- a/cmd/sf.c ++++ b/cmd/sf.c +@@ -391,6 +391,14 @@ static int do_spi_protect(int argc, char + return ret == 0 ? 0 : 1; + } + ++static int do_spi_flash_read_uuid(void) ++{ ++ int ret = 0; ++ ret = flash->read_uuid(flash); ++ ++ return ret == 0 ? 0 : 1; ++} ++ + enum { + STAGE_ERASE, + STAGE_CHECK, +@@ -587,6 +595,8 @@ static int do_spi_flash(struct cmd_tbl * + ret = do_spi_flash_erase(argc, argv); + else if (strcmp(cmd, "protect") == 0) + ret = do_spi_protect(argc, argv); ++ else if (strcmp(cmd, "uuid") == 0) ++ ret = do_spi_flash_read_uuid(); + else if (IS_ENABLED(CONFIG_CMD_SF_TEST) && !strcmp(cmd, "test")) + ret = do_spi_flash_test(argc, argv); + else +@@ -617,7 +627,8 @@ static const char long_help[] = + " at `addr' to flash at `offset'\n" + " or to start of mtd `partition'\n" + "sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n" +- " at address 'sector'" ++ " at address 'sector'\n" ++ "sf uuid - read uuid from flash" + #ifdef CONFIG_CMD_SF_TEST + "\nsf test offset len - run a very basic destructive test" + #endif diff --git a/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch b/package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch similarity index 62% rename from package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch rename to package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch index dc9c9ae5e40..ee45ff0de63 100644 --- a/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch +++ b/package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch @@ -1,26 +1,20 @@ -From afea25576fc92d562b248b783cf03564eb4521da Mon Sep 17 00:00:00 2001 +From 5a15437610e8e8c68dc347845a83d0cbad80ca08 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Tue, 19 Jan 2021 10:58:48 +0800 -Subject: [PATCH 12/12] cmd: bootmenu: add ability to select item by shortkey +Subject: [PATCH 51/71] cmd: bootmenu: add ability to select item by shortkey Add ability to use shortkey to select item for bootmenu command Signed-off-by: Weijie Gao --- - cmd/bootmenu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++----- - 1 file changed, 70 insertions(+), 7 deletions(-) + cmd/bootmenu.c | 34 ++++++++++++++++++++++++----- + common/menu.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-- + include/menu.h | 12 +++++++---- + 3 files changed, 93 insertions(+), 11 deletions(-) --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -87,16 +88,17 @@ static char *bootmenu_choice_entry(void +@@ -87,16 +87,17 @@ static char *bootmenu_choice_entry(void struct bootmenu_data *menu = data; struct bootmenu_entry *iter; enum bootmenu_key key = KEY_NONE; @@ -40,7 +34,7 @@ Signed-off-by: Weijie Gao } switch (key) { -@@ -110,6 +112,12 @@ static char *bootmenu_choice_entry(void +@@ -110,6 +111,12 @@ static char *bootmenu_choice_entry(void ++menu->active; /* no menu key selected, regenerate menu */ return NULL; @@ -53,28 +47,43 @@ Signed-off-by: Weijie Gao case KEY_SELECT: iter = menu->first; for (i = 0; i < menu->active; ++i) -@@ -181,12 +189,19 @@ static int prepare_bootmenu_entry(struct +@@ -167,6 +174,9 @@ static int prepare_bootmenu_entry(struct + unsigned short int i = *index; + struct bootmenu_entry *entry = NULL; + struct bootmenu_entry *iter = *current; ++ char *choice_option; ++ char choice_char; ++ int len; + + while ((option = bootmenu_getoption(i))) { + +@@ -181,11 +191,24 @@ static int prepare_bootmenu_entry(struct if (!entry) return -ENOMEM; - entry->title = strndup(option, sep - option); -+ entry->title = malloc((sep - option) + 4); ++ /* Add KEY_CHOICE support: '%d. %s\0' : len --> len + 4 */ ++ len = sep - option + 4; ++ choice_option = malloc(len); ++ if (!choice_option) { ++ free(entry->title); ++ free(entry); ++ return -ENOMEM; ++ } ++ if (!get_choice_char(i, &choice_char)) ++ len = snprintf(choice_option, len, "%c. %s", choice_char, option); ++ else ++ len = snprintf(choice_option, len, " %s", option); ++ entry->title = strndup(choice_option, len); if (!entry->title) { free(entry); return -ENOMEM; } ++ free(choice_option); -+ if (i < ARRAY_SIZE(choice_chars)) { -+ sprintf(entry->title, "%c. %.*s", choice_chars[i], -+ (int)(sep - option), option); -+ } else { -+ sprintf(entry->title, " %.*s", (int)(sep - option), option); -+ } -+ entry->command = strdup(sep + 1); if (!entry->command) { - free(entry->title); -@@ -331,6 +346,7 @@ static struct bootmenu_data *bootmenu_cr +@@ -331,6 +354,7 @@ static struct bootmenu_data *bootmenu_cr menu->delay = delay; menu->active = 0; menu->first = NULL; @@ -82,7 +91,7 @@ Signed-off-by: Weijie Gao default_str = env_get("bootmenu_default"); if (default_str) -@@ -356,9 +372,9 @@ static struct bootmenu_data *bootmenu_cr +@@ -356,9 +380,9 @@ static struct bootmenu_data *bootmenu_cr /* Add Quit entry if entering U-Boot console is disabled */ if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE)) @@ -96,18 +105,17 @@ Signed-off-by: Weijie Gao free(entry); --- a/common/menu.c +++ b/common/menu.c -@@ -9,6 +9,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -47,6 +48,17 @@ struct menu { +@@ -47,6 +47,33 @@ struct menu { int item_cnt; }; ++const char choice_chars[] = { ++ '1', '2', '3', '4', '5', '6', '7', '8', '9', ++ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', ++ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', ++ 'u', 'v', 'w', 'x', 'y', 'z' ++}; ++ +static int find_choice(char choice) +{ + int i; @@ -118,11 +126,20 @@ Signed-off-by: Weijie Gao + + return -1; +} ++ ++int get_choice_char(int index, char *result) ++{ ++ if (index < ARRAY_SIZE(choice_chars)) ++ *result = choice_chars[index]; ++ else ++ return -1; ++ return 0; ++} + /* * An iterator function for menu items. callback will be called for each item * in m, with m, a pointer to the item, and extra being passed to callback. If -@@ -426,7 +438,7 @@ int menu_destroy(struct menu *m) +@@ -426,7 +453,7 @@ int menu_destroy(struct menu *m) } void bootmenu_autoboot_loop(struct bootmenu_data *menu, @@ -131,7 +148,7 @@ Signed-off-by: Weijie Gao { int i, c; -@@ -456,6 +468,19 @@ void bootmenu_autoboot_loop(struct bootm +@@ -456,6 +483,19 @@ void bootmenu_autoboot_loop(struct bootm break; default: *key = KEY_NONE; @@ -151,7 +168,7 @@ Signed-off-by: Weijie Gao break; } -@@ -475,10 +500,16 @@ void bootmenu_autoboot_loop(struct bootm +@@ -475,10 +515,16 @@ void bootmenu_autoboot_loop(struct bootm } void bootmenu_loop(struct bootmenu_data *menu, @@ -169,7 +186,7 @@ Signed-off-by: Weijie Gao if (*esc == 1) { if (tstc()) { c = getchar(); -@@ -504,6 +535,14 @@ void bootmenu_loop(struct bootmenu_data +@@ -504,6 +550,14 @@ void bootmenu_loop(struct bootmenu_data if (c == '\e') { *esc = 1; *key = KEY_NONE; @@ -186,7 +203,29 @@ Signed-off-by: Weijie Gao case 1: --- a/include/menu.h +++ b/include/menu.h -@@ -40,6 +40,7 @@ struct bootmenu_data { +@@ -2,10 +2,11 @@ + /* + * Copyright 2010-2011 Calxeda, Inc. + */ +- + #ifndef __MENU_H__ + #define __MENU_H__ + ++#include ++ + struct menu; + + struct menu *menu_create(char *title, int timeout, int prompt, +@@ -18,6 +19,8 @@ int menu_get_choice(struct menu *m, void + int menu_item_add(struct menu *m, char *item_key, void *item_data); + int menu_destroy(struct menu *m); + int menu_default_choice(struct menu *m, void **choice); ++/* Add KEY_CHOICE support */ ++int get_choice_char(int index, char *result); + + /** + * menu_show() Show a boot menu +@@ -40,6 +43,7 @@ struct bootmenu_data { int active; /* active menu entry */ int count; /* total count of menu entries */ struct bootmenu_entry *first; /* first menu entry */ @@ -194,7 +233,7 @@ Signed-off-by: Weijie Gao }; enum bootmenu_key { -@@ -48,11 +49,19 @@ enum bootmenu_key { +@@ -48,11 +52,11 @@ enum bootmenu_key { KEY_DOWN, KEY_SELECT, KEY_QUIT, @@ -206,13 +245,6 @@ Signed-off-by: Weijie Gao + enum bootmenu_key *key, int *esc, int *choice); void bootmenu_loop(struct bootmenu_data *menu, - enum bootmenu_key *key, int *esc); +- + enum bootmenu_key *key, int *esc, int *choice); -+ -+static const char choice_chars[] = { -+ '1', '2', '3', '4', '5', '6', '7', '8', '9', -+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', -+ 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', -+ 'u', 'v', 'w', 'x', 'y', 'z' -+}; - #endif /* __MENU_H__ */ diff --git a/package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch b/package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch new file mode 100644 index 00000000000..149a156ba26 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch @@ -0,0 +1,28 @@ +From 7ab891faaaf2b6126694352d4503dc40605a6aec Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 15:10:02 +0800 +Subject: [PATCH 52/71] common: spl: spl_nand: enable + CONFIG_SYS_NAND_U_BOOT_OFFS undefined + +Enable using spl_nand with CONFIG_SYS_NAND_U_BOOT_OFFS undefined since +mtk-snand does not require raw nand framework. + +Signed-off-by: Weijie Gao +--- + common/spl/spl_nand.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/common/spl/spl_nand.c ++++ b/common/spl/spl_nand.c +@@ -16,7 +16,11 @@ + + uint32_t __weak spl_nand_get_uboot_raw_page(void) + { ++#ifdef CONFIG_SYS_NAND_U_BOOT_OFFS + return CONFIG_SYS_NAND_U_BOOT_OFFS; ++#else ++ return 0; ++#endif + } + + #if defined(CONFIG_SPL_NAND_RAW_ONLY) diff --git a/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch b/package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch similarity index 61% rename from package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch rename to package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch index 4ee3d6f3202..b6243db9c2d 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch +++ b/package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch @@ -1,7 +1,7 @@ -From 3757223c3354b9feeffcbe916eb18eb8873bd133 Mon Sep 17 00:00:00 2001 +From 452dc98572f8353f77551bcce5a2ca8cd050f498 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Wed, 3 Mar 2021 10:48:53 +0800 -Subject: [PATCH 07/12] board: mt7629: add support for booting from SPI-NAND +Subject: [PATCH 53/71] board: mt7629: add support for booting from SPI-NAND Add support for mt7629 to boot from SPI-NAND. Add a new defconfig for mt7629+spi-nand configuration. @@ -11,10 +11,12 @@ Signed-off-by: Weijie Gao arch/arm/dts/mt7629-rfb-u-boot.dtsi | 8 ++ arch/arm/dts/mt7629-rfb.dts | 10 +++ arch/arm/dts/mt7629.dtsi | 16 ++++ - board/mediatek/mt7629/Kconfig | 35 ++++++++- - configs/mt7629_nand_rfb_defconfig | 111 ++++++++++++++++++++++++++++ - include/configs/mt7629.h | 7 ++ - 6 files changed, 186 insertions(+), 1 deletion(-) + arch/arm/mach-mediatek/Kconfig | 4 +- + board/mediatek/mt7629/Kconfig | 40 ++++++++++ + board/mediatek/mt7629/mt7629_rfb.c | 5 ++ + configs/mt7629_nand_rfb_defconfig | 113 ++++++++++++++++++++++++++++ + 7 files changed, 195 insertions(+), 1 deletion(-) + create mode 100644 board/mediatek/mt7629/Kconfig create mode 100644 configs/mt7629_nand_rfb_defconfig --- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi @@ -85,9 +87,78 @@ Signed-off-by: Weijie Gao snor: snor@11014000 { compatible = "mediatek,mtk-snor"; reg = <0x11014000 0x1000>; +--- a/arch/arm/mach-mediatek/Kconfig ++++ b/arch/arm/mach-mediatek/Kconfig +@@ -131,9 +131,11 @@ config SYS_CONFIG_NAME + + config MTK_BROM_HEADER_INFO + string +- default "media=nor" if TARGET_MT8518 || TARGET_MT8512 || TARGET_MT7629 || TARGET_MT7622 ++ default "media=nor" if TARGET_MT8518 || TARGET_MT8512 || TARGET_MT7622 + default "media=emmc" if TARGET_MT8516 || TARGET_MT8365 || TARGET_MT8183 + default "media=snand;nandinfo=2k+64" if TARGET_MT7981 || TARGET_MT7986 + default "lk=1" if TARGET_MT7623 + ++source "board/mediatek/mt7629/Kconfig" ++ + endif +--- /dev/null ++++ b/board/mediatek/mt7629/Kconfig +@@ -0,0 +1,40 @@ ++if TARGET_MT7629 ++ ++config MTK_BROM_HEADER_INFO ++ string ++ default "media=nor" if BOOT_FROM_SNOR ++ default "media=snand;nandinfo=2k+64" if BOOT_FROM_SNAND_2K_64 ++ default "media=snand;nandinfo=2k+128" if BOOT_FROM_SNAND_2K_128 ++ default "media=snand;nandinfo=4k+128" if BOOT_FROM_SNAND_4K_128 ++ default "media=snand;nandinfo=4k+256" if BOOT_FROM_SNAND_4K_256 ++ ++choice ++ prompt "Boot device" ++ default BOOT_FROM_SNOR ++ ++config BOOT_FROM_SNOR ++ bool "SPI-NOR" ++ ++config BOOT_FROM_SNAND_2K_64 ++ bool "SPI-NAND (2K+64)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_2K_128 ++ bool "SPI-NAND (2K+128)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_4K_128 ++ bool "SPI-NAND (4K+128)" ++ select MT7629_BOOT_FROM_SNAND ++ ++config BOOT_FROM_SNAND_4K_256 ++ bool "SPI-NAND (4K+256)" ++ select MT7629_BOOT_FROM_SNAND ++ ++endchoice ++ ++config MT7629_BOOT_FROM_SNAND ++ bool ++ default n ++ ++endif +--- a/board/mediatek/mt7629/mt7629_rfb.c ++++ b/board/mediatek/mt7629/mt7629_rfb.c +@@ -15,3 +15,8 @@ int board_init(void) + + return 0; + } ++ ++uint32_t spl_nand_get_uboot_raw_page(void) ++{ ++ return CONFIG_SPL_PAD_TO; ++} --- /dev/null +++ b/configs/mt7629_nand_rfb_defconfig -@@ -0,0 +1,111 @@ +@@ -0,0 +1,113 @@ +CONFIG_ARM=y +CONFIG_SYS_ARCH_TIMER=y +CONFIG_SYS_THUMB_BUILD=y @@ -96,36 +167,42 @@ Signed-off-by: Weijie Gao +CONFIG_SYS_MALLOC_F_LEN=0x4000 +CONFIG_NR_DRAM_BANKS=1 +CONFIG_ENV_SIZE=0x20000 -+CONFIG_ENV_OFFSET=0x100000 ++CONFIG_ENV_OFFSET=0x0 ++CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb" +CONFIG_SPL_TEXT_BASE=0x201000 +CONFIG_TARGET_MT7629=y +CONFIG_BOOT_FROM_SNAND_2K_64=y -+CONFIG_SPL_SERIAL_SUPPORT=y -+CONFIG_SPL_DRIVERS_MISC_SUPPORT=y ++CONFIG_SPL_SERIAL=y +CONFIG_SPL_STACK_R_ADDR=0x40800000 ++CONFIG_SYS_LOAD_ADDR=0x42007f1c +CONFIG_SPL_PAYLOAD="u-boot.img" +CONFIG_BUILD_TARGET="u-boot-mtk.bin" -+CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb" ++CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y ++CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x41fffef0 +CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin" +CONFIG_FIT=y -+CONFIG_FIT_VERBOSE=y -+CONFIG_BOOTDELAY=3 ++# CONFIG_AUTOBOOT is not set +CONFIG_DEFAULT_FDT_FILE="mt7629-rfb" +CONFIG_SYS_CONSOLE_IS_IN_ENV=y -+CONFIG_SYS_STDIO_DEREGISTER=y +# CONFIG_DISPLAY_BOARDINFO is not set ++CONFIG_SPL_MAX_SIZE=0x20000 ++CONFIG_SPL_FOOTPRINT_LIMIT=y ++CONFIG_SPL_MAX_FOOTPRINT=0x20000 +CONFIG_SPL_SYS_MALLOC_SIMPLE=y ++# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set ++CONFIG_SPL_STACK=0x106000 +CONFIG_SPL_STACK_R=y +CONFIG_SPL_MTD_SUPPORT=y +CONFIG_SPL_NAND_SUPPORT=y -+CONFIG_SPL_WATCHDOG_SUPPORT=y ++CONFIG_SPL_WATCHDOG=y +CONFIG_HUSH_PARSER=y +CONFIG_SYS_PROMPT="U-Boot> " -+CONFIG_CMD_BOOTMENU=y +# CONFIG_BOOTM_NETBSD is not set +# CONFIG_BOOTM_PLAN9 is not set +# CONFIG_BOOTM_RTEMS is not set +# CONFIG_BOOTM_VXWORKS is not set ++CONFIG_SYS_BOOTM_LEN=0x4000000 ++CONFIG_CMD_BOOTMENU=y +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_XIMG is not set +CONFIG_CMD_BIND=y @@ -140,13 +217,10 @@ Signed-off-by: Weijie Gao +CONFIG_CMD_FAT=y +CONFIG_CMD_FS_GENERIC=y +CONFIG_CMD_LOG=y -+CONFIG_EFI_PARTITION=y -+# CONFIG_SPL_PARTITION_UUIDS is not set -+CONFIG_PARTITION_TYPE_GUID=y +CONFIG_OF_SPL_REMOVE_PROPS="interrupt-parent assigned-clocks assigned-clock-parents" +CONFIG_ENV_OVERWRITE=y +CONFIG_ENV_IS_IN_MTD=y -+CONFIG_ENV_MTD_NAME="spi-nand0" ++CONFIG_ENV_MTD_NAME="u-boot-env" +CONFIG_ENV_SIZE_REDUND=0x40000 +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_NET_RANDOM_ETHADDR=y @@ -155,7 +229,6 @@ Signed-off-by: Weijie Gao +CONFIG_SPL_REGMAP=y +CONFIG_SYSCON=y +CONFIG_SPL_SYSCON=y -+CONFIG_BLK=y +CONFIG_CLK=y +CONFIG_SPL_CLK=y +# CONFIG_MMC is not set @@ -189,35 +262,13 @@ Signed-off-by: Weijie Gao +CONFIG_SPL_SYSRESET=y +CONFIG_SYSRESET_WATCHDOG=y +CONFIG_USB=y -+CONFIG_DM_USB=y +# CONFIG_SPL_DM_USB is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_MTK=y +CONFIG_USB_STORAGE=y +CONFIG_WDT_MTK=y -+CONFIG_FAT_WRITE=y ++# CONFIG_SHA256 is not set ++# CONFIG_SPL_SHA1 is not set +CONFIG_LZMA=y +CONFIG_SPL_LZMA=y +# CONFIG_EFI_LOADER is not set ---- a/include/configs/mt7629.h -+++ b/include/configs/mt7629.h -@@ -25,12 +25,19 @@ - - /* Defines for SPL */ - #define CONFIG_SPL_STACK 0x106000 -+#ifdef CONFIG_MT7629_BOOT_FROM_SNAND -+#define CONFIG_SPL_MAX_SIZE SZ_128K -+#define CONFIG_SPL_MAX_FOOTPRINT SZ_128K -+#define CONFIG_SPL_PAD_TO 0x20000 -+#define CONFIG_SYS_NAND_U_BOOT_OFFS CONFIG_SPL_PAD_TO -+#else - #define CONFIG_SPL_MAX_SIZE SZ_64K - #define CONFIG_SPL_MAX_FOOTPRINT SZ_64K - #define CONFIG_SPL_PAD_TO 0x10000 - - #define CONFIG_SPI_ADDR 0x30000000 - #define CONFIG_SYS_UBOOT_BASE (CONFIG_SPI_ADDR + CONFIG_SPL_PAD_TO) -+#endif - - /* SPL -> Uboot */ - #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_TEXT_BASE + SZ_2M - \ diff --git a/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch similarity index 85% rename from package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch rename to package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch index 84101d0ef93..6e90f47f35c 100644 --- a/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch +++ b/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch @@ -1,7 +1,7 @@ -From 6bcd65ed47844e747ff6db066b092632f1760256 Mon Sep 17 00:00:00 2001 +From 4c1803cc08b1618d935c1386f43f43a4e9c97697 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Wed, 3 Mar 2021 10:51:43 +0800 -Subject: [PATCH 08/12] board: mt7622: use new spi-nand driver +Subject: [PATCH 54/71] board: mt7622: use new spi-nand driver Enable new spi-nand driver support for mt7622_rfb_defconfig @@ -14,7 +14,7 @@ Signed-off-by: Weijie Gao --- a/arch/arm/dts/mt7622-rfb.dts +++ b/arch/arm/dts/mt7622-rfb.dts -@@ -188,6 +188,13 @@ +@@ -196,6 +196,13 @@ }; }; @@ -26,11 +26,11 @@ Signed-off-by: Weijie Gao +}; + &uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; + status = "okay"; + }; --- a/arch/arm/dts/mt7622.dtsi +++ b/arch/arm/dts/mt7622.dtsi -@@ -53,6 +53,22 @@ +@@ -77,6 +77,22 @@ #size-cells = <0>; }; @@ -55,7 +55,7 @@ Signed-off-by: Weijie Gao reg = <0x11014000 0x1000>; --- a/configs/mt7622_rfb_defconfig +++ b/configs/mt7622_rfb_defconfig -@@ -16,6 +16,7 @@ CONFIG_LOG=y +@@ -18,6 +18,7 @@ CONFIG_LOG=y CONFIG_SYS_PROMPT="MT7622> " CONFIG_CMD_BOOTMENU=y CONFIG_CMD_MMC=y @@ -63,7 +63,7 @@ Signed-off-by: Weijie Gao CONFIG_CMD_PCI=y CONFIG_CMD_SF_TEST=y CONFIG_CMD_PING=y -@@ -28,6 +29,10 @@ CONFIG_SYSCON=y +@@ -33,6 +34,10 @@ CONFIG_SYSCON=y CONFIG_CLK=y CONFIG_MMC_HS200_SUPPORT=y CONFIG_MMC_MTK=y diff --git a/package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch b/package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch new file mode 100644 index 00000000000..2cd62a2c19a --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch @@ -0,0 +1,223 @@ +From d5841f8707dcb7a1f73607de67ab45dba93a56a4 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Fri, 29 Jul 2022 17:04:12 +0800 +Subject: [PATCH 55/71] board: mt7981: add reference board using new spi-nand + driver + +Add a new reference board using new spi-nand driver for SPI-NAND flash on +SNFI interface + +Signed-off-by: Weijie Gao +--- + arch/arm/dts/Makefile | 1 + + arch/arm/dts/mt7981-snfi-nand-rfb.dts | 132 +++++++++++++++++++++++++ + configs/mt7981_snfi_nand_rfb_defconfig | 57 +++++++++++ + 3 files changed, 190 insertions(+) + create mode 100644 arch/arm/dts/mt7981-snfi-nand-rfb.dts + create mode 100644 configs/mt7981_snfi_nand_rfb_defconfig + +--- a/arch/arm/dts/Makefile ++++ b/arch/arm/dts/Makefile +@@ -1206,6 +1206,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ + mt7623n-bananapi-bpi-r2.dtb \ + mt7629-rfb.dtb \ + mt7981-rfb.dtb \ ++ mt7981-snfi-nand-rfb.dtb \ + mt7981-emmc-rfb.dtb \ + mt7981-sd-rfb.dtb \ + mt7986a-rfb.dtb \ +--- /dev/null ++++ b/arch/arm/dts/mt7981-snfi-nand-rfb.dts +@@ -0,0 +1,132 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (c) 2021 MediaTek Inc. ++ * Author: Sam Shih ++ */ ++ ++/dts-v1/; ++#include "mt7981.dtsi" ++#include ++ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ model = "mt7981-rfb"; ++ compatible = "mediatek,mt7981", "mediatek,mt7981-rfb"; ++ chosen { ++ stdout-path = &uart0; ++ tick-timer = &timer0; ++ }; ++}; ++ ++&uart0 { ++ status = "okay"; ++}; ++ ++&uart1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&uart1_pins>; ++ status = "disabled"; ++}; ++ ++ð { ++ status = "okay"; ++ mediatek,gmac-id = <0>; ++ phy-mode = "sgmii"; ++ mediatek,switch = "mt7531"; ++ reset-gpios = <&gpio 39 GPIO_ACTIVE_HIGH>; ++ ++ fixed-link { ++ speed = <1000>; ++ full-duplex; ++ }; ++}; ++ ++&pinctrl { ++ snfi_pins: snfi-pins-func-1 { ++ mux { ++ function = "flash"; ++ groups = "snfi"; ++ }; ++ ++ clk { ++ pins = "SPI0_CLK"; ++ drive-strength = ; ++ bias-pull-down = ; ++ }; ++ ++ conf-pu { ++ pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP"; ++ drive-strength = ; ++ bias-pull-up = ; ++ }; ++ ++ conf-pd { ++ pins = "SPI0_MOSI", "SPI0_MISO"; ++ drive-strength = ; ++ bias-pull-down = ; ++ }; ++ }; ++ ++ spic_pins: spi1-pins-func-1 { ++ mux { ++ function = "spi"; ++ groups = "spi1_1"; ++ }; ++ }; ++ ++ uart1_pins: spi1-pins-func-3 { ++ mux { ++ function = "uart"; ++ groups = "uart1_2"; ++ }; ++ }; ++ ++ /* pin15 as pwm0 */ ++ one_pwm_pins: one-pwm-pins { ++ mux { ++ function = "pwm"; ++ groups = "pwm0_1"; ++ }; ++ }; ++ ++ /* pin15 as pwm0 and pin14 as pwm1 */ ++ two_pwm_pins: two-pwm-pins { ++ mux { ++ function = "pwm"; ++ groups = "pwm0_1", "pwm1_0"; ++ }; ++ }; ++ ++ /* pin15 as pwm0, pin14 as pwm1, pin7 as pwm2 */ ++ three_pwm_pins: three-pwm-pins { ++ mux { ++ function = "pwm"; ++ groups = "pwm0_1", "pwm1_0", "pwm2"; ++ }; ++ }; ++ ++ mmc0_pins_default: mmc0default { ++ mux { ++ function = "flash"; ++ groups = "emmc_45"; ++ }; ++ }; ++}; ++ ++&snand { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&snfi_pins>; ++ status = "okay"; ++ quad-spi; ++}; ++ ++&pwm { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&two_pwm_pins>; ++ status = "okay"; ++}; ++ ++&watchdog { ++ status = "disabled"; ++}; +--- /dev/null ++++ b/configs/mt7981_snfi_nand_rfb_defconfig +@@ -0,0 +1,57 @@ ++CONFIG_ARM=y ++CONFIG_POSITION_INDEPENDENT=y ++CONFIG_ARCH_MEDIATEK=y ++CONFIG_SYS_TEXT_BASE=0x41e00000 ++CONFIG_SYS_MALLOC_F_LEN=0x4000 ++CONFIG_NR_DRAM_BANKS=1 ++CONFIG_ENV_SIZE=0x20000 ++CONFIG_DEFAULT_DEVICE_TREE="mt7981-snfi-nand-rfb" ++CONFIG_TARGET_MT7981=y ++CONFIG_DEBUG_UART_BASE=0x11002000 ++CONFIG_DEBUG_UART_CLOCK=40000000 ++CONFIG_SYS_LOAD_ADDR=0x46000000 ++CONFIG_DEBUG_UART=y ++# CONFIG_AUTOBOOT is not set ++CONFIG_DEFAULT_FDT_FILE="mt7981-snfi-nand-rfb" ++CONFIG_LOGLEVEL=7 ++CONFIG_LOG=y ++CONFIG_SYS_PROMPT="MT7981> " ++CONFIG_SYS_CBSIZE=512 ++CONFIG_SYS_PBSIZE=1049 ++# CONFIG_BOOTM_NETBSD is not set ++# CONFIG_BOOTM_PLAN9 is not set ++# CONFIG_BOOTM_RTEMS is not set ++# CONFIG_BOOTM_VXWORKS is not set ++# CONFIG_CMD_ELF is not set ++# CONFIG_CMD_UNLZ4 is not set ++# CONFIG_CMD_UNZIP is not set ++CONFIG_CMD_GPIO=y ++CONFIG_CMD_MTD=y ++CONFIG_CMD_PING=y ++CONFIG_CMD_SMC=y ++CONFIG_MTDIDS_DEFAULT="spi-nand0=spi-nand0" ++CONFIG_MTDPARTS_DEFAULT="spi-nand0:1024k(bl2),512k(u-boot-env),2048k(factory),2048k(fip),65536k(ubi)" ++CONFIG_CMD_UBI=y ++CONFIG_CMD_UBI_RENAME=y ++CONFIG_ENV_OVERWRITE=y ++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_CLK=y ++# CONFIG_MMC is not set ++CONFIG_MTD=y ++CONFIG_DM_MTD=y ++CONFIG_MTK_SPI_NAND=y ++CONFIG_MTK_SPI_NAND_MTD=y ++CONFIG_PHY_FIXED=y ++CONFIG_DM_ETH=y ++CONFIG_MEDIATEK_ETH=y ++CONFIG_PINCTRL=y ++CONFIG_PINCONF=y ++CONFIG_PINCTRL_MT7981=y ++CONFIG_POWER_DOMAIN=y ++CONFIG_MTK_POWER_DOMAIN=y ++CONFIG_DM_SERIAL=y ++CONFIG_MTK_SERIAL=y ++CONFIG_HEXDUMP=y diff --git a/package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch b/package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch new file mode 100644 index 00000000000..228bde1567a --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch @@ -0,0 +1,76 @@ +From a2df2df6fd1aec32572c7b30ccf5a184ec1763fd Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 27 Jul 2022 16:32:17 +0800 +Subject: [PATCH 56/71] mtd: spi-nor: add more flash ids + +Add more spi-nor flash ids + +Signed-off-by: Weijie Gao +--- + drivers/mtd/spi/spi-nor-core.c | 1 + + drivers/mtd/spi/spi-nor-ids.c | 23 ++++++++++++++++++++++- + 2 files changed, 23 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/spi/spi-nor-core.c ++++ b/drivers/mtd/spi/spi-nor-core.c +@@ -641,6 +641,7 @@ static int set_4byte(struct spi_nor *nor + case SNOR_MFR_ISSI: + case SNOR_MFR_MACRONIX: + case SNOR_MFR_WINBOND: ++ case SNOR_MFR_EON: + if (need_wren) + write_enable(nor); + +--- a/drivers/mtd/spi/spi-nor-ids.c ++++ b/drivers/mtd/spi/spi-nor-ids.c +@@ -83,7 +83,8 @@ const struct flash_info spi_nor_ids[] = + { INFO("en25q32b", 0x1c3016, 0, 64 * 1024, 64, 0) }, + { INFO("en25q64", 0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { INFO("en25q128b", 0x1c3018, 0, 64 * 1024, 256, 0) }, +- { INFO("en25qh128", 0x1c7018, 0, 64 * 1024, 256, 0) }, ++ { INFO("en25qh128", 0x1c7018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { INFO("en25qh256", 0x1c7019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("en25s64", 0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, + #endif + #ifdef CONFIG_SPI_FLASH_GIGADEVICE /* GIGADEVICE */ +@@ -119,6 +120,11 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { ++ INFO("gd25q256", 0xc84019, 0, 64 * 1024, 512, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { + INFO("gd25lq128", 0xc86018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +@@ -395,6 +401,16 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { ++ INFO("w25q256jv", 0xef7019, 0, 64 * 1024, 512, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { ++ INFO("w25q512jv", 0xef7020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, ++ { + INFO("w25q128jw", 0xef8018, 0, 64 * 1024, 256, + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) +@@ -439,6 +455,11 @@ const struct flash_info spi_nor_ids[] = + SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) + }, + { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, ++ { ++ INFO("w25q512", 0xef4020, 0, 64 * 1024, 1024, ++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | ++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) ++ }, + { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, + { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, diff --git a/package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch b/package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch new file mode 100644 index 00000000000..1c83479d0db --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch @@ -0,0 +1,1550 @@ +From a3c5878599d530330027412eb8be12f816ac215c Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 27 Jul 2022 16:36:13 +0800 +Subject: [PATCH 57/71] mtd: spi-nand: backport from upstream kernel + +Backport new features from upstream kernel + +Signed-off-by: Weijie Gao +--- + drivers/mtd/nand/spi/Kconfig | 8 + + drivers/mtd/nand/spi/core.c | 101 ++++++---- + drivers/mtd/nand/spi/gigadevice.c | 322 ++++++++++++++++++++++++++---- + drivers/mtd/nand/spi/macronix.c | 173 +++++++++++++--- + drivers/mtd/nand/spi/micron.c | 50 ++--- + drivers/mtd/nand/spi/toshiba.c | 66 +++--- + drivers/mtd/nand/spi/winbond.c | 171 +++++++++++++--- + include/linux/mtd/spinand.h | 86 +++++--- + 8 files changed, 753 insertions(+), 224 deletions(-) + +--- a/drivers/mtd/nand/spi/Kconfig ++++ b/drivers/mtd/nand/spi/Kconfig +@@ -5,3 +5,11 @@ menuconfig MTD_SPI_NAND + select SPI_MEM + help + This is the framework for the SPI NAND device drivers. ++ ++config MTD_SPI_NAND_W25N01KV ++ tristate "Winbond W25N01KV Support" ++ select MTD_SPI_NAND ++ default n ++ help ++ Winbond W25N01KV share the same ID with W25N01GV. However, they have ++ different attributes. +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #else +@@ -451,10 +452,11 @@ out: + return status & STATUS_BUSY ? -ETIMEDOUT : 0; + } + +-static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) ++static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, ++ u8 ndummy, u8 *buf) + { +- struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, +- SPINAND_MAX_ID_LEN); ++ struct spi_mem_op op = SPINAND_READID_OP( ++ naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + int ret; + + ret = spi_mem_exec_op(spinand->slave, &op); +@@ -464,18 +466,6 @@ static int spinand_read_id_op(struct spi + return ret; + } + +-static int spinand_reset_op(struct spinand_device *spinand) +-{ +- struct spi_mem_op op = SPINAND_RESET_OP; +- int ret; +- +- ret = spi_mem_exec_op(spinand->slave, &op); +- if (ret) +- return ret; +- +- return spinand_wait(spinand, NULL); +-} +- + static int spinand_lock_block(struct spinand_device *spinand, u8 lock) + { + return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock); +@@ -836,24 +826,63 @@ static const struct spinand_manufacturer + &winbond_spinand_manufacturer, + }; + +-static int spinand_manufacturer_detect(struct spinand_device *spinand) ++static int spinand_manufacturer_match(struct spinand_device *spinand, ++ enum spinand_readid_method rdid_method) + { ++ u8 *id = spinand->id.data; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { +- ret = spinand_manufacturers[i]->ops->detect(spinand); +- if (ret > 0) { +- spinand->manufacturer = spinand_manufacturers[i]; +- return 0; +- } else if (ret < 0) { +- return ret; +- } ++ const struct spinand_manufacturer *manufacturer = ++ spinand_manufacturers[i]; ++ ++ if (id[0] != manufacturer->id) ++ continue; ++ ++ ret = spinand_match_and_init(spinand, ++ manufacturer->chips, ++ manufacturer->nchips, ++ rdid_method); ++ if (ret < 0) ++ continue; ++ ++ spinand->manufacturer = manufacturer; ++ return 0; + } + + return -ENOTSUPP; + } + ++static int spinand_id_detect(struct spinand_device *spinand) ++{ ++ u8 *id = spinand->id.data; ++ int ret; ++ ++ ret = spinand_read_id_op(spinand, 0, 0, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); ++ if (!ret) ++ return 0; ++ ++ ret = spinand_read_id_op(spinand, 1, 0, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, ++ SPINAND_READID_METHOD_OPCODE_ADDR); ++ if (!ret) ++ return 0; ++ ++ ret = spinand_read_id_op(spinand, 0, 1, id); ++ if (ret) ++ return ret; ++ ret = spinand_manufacturer_match(spinand, ++ SPINAND_READID_METHOD_OPCODE_DUMMY); ++ ++ return ret; ++} ++ + static int spinand_manufacturer_init(struct spinand_device *spinand) + { + if (spinand->manufacturer->ops->init) +@@ -909,9 +938,9 @@ spinand_select_op_variant(struct spinand + * @spinand: SPI NAND object + * @table: SPI NAND device description table + * @table_size: size of the device description table ++ * @rdid_method: read id method to match + * +- * Should be used by SPI NAND manufacturer drivers when they want to find a +- * match between a device ID retrieved through the READ_ID command and an ++ * Match between a device ID retrieved through the READ_ID command and an + * entry in the SPI NAND description table. If a match is found, the spinand + * object will be initialized with information provided by the matching + * spinand_info entry. +@@ -920,8 +949,10 @@ spinand_select_op_variant(struct spinand + */ + int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, +- unsigned int table_size, u8 devid) ++ unsigned int table_size, ++ enum spinand_readid_method rdid_method) + { ++ u8 *id = spinand->id.data; + struct nand_device *nand = spinand_to_nand(spinand); + unsigned int i; + +@@ -929,13 +960,17 @@ int spinand_match_and_init(struct spinan + const struct spinand_info *info = &table[i]; + const struct spi_mem_op *op; + +- if (devid != info->devid) ++ if (rdid_method != info->devid.method) ++ continue; ++ ++ if (memcmp(id + 1, info->devid.id, info->devid.len)) + continue; + + nand->memorg = table[i].memorg; + nand->eccreq = table[i].eccreq; + spinand->eccinfo = table[i].eccinfo; + spinand->flags = table[i].flags; ++ spinand->id.len = 1 + table[i].devid.len; + spinand->select_target = table[i].select_target; + + op = spinand_select_op_variant(spinand, +@@ -967,17 +1002,7 @@ static int spinand_detect(struct spinand + struct nand_device *nand = spinand_to_nand(spinand); + int ret; + +- ret = spinand_reset_op(spinand); +- if (ret) +- return ret; +- +- ret = spinand_read_id_op(spinand, spinand->id.data); +- if (ret) +- return ret; +- +- spinand->id.len = SPINAND_MAX_ID_LEN; +- +- ret = spinand_manufacturer_detect(spinand); ++ ret = spinand_id_detect(spinand); + if (ret) { + dev_err(spinand->slave->dev, "unknown raw ID %*phN\n", + SPINAND_MAX_ID_LEN, spinand->id.data); +--- a/drivers/mtd/nand/spi/gigadevice.c ++++ b/drivers/mtd/nand/spi/gigadevice.c +@@ -22,8 +22,13 @@ + + #define GD5FXGQXXEXXG_REG_STATUS2 0xf0 + ++#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK (7 << 4) ++#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS (1 << 4) ++#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4) ++ + /* Q4 devices, QUADIO: Dummy bytes valid for 1 and 2 GBit variants */ +-static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants, ++static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), +@@ -31,8 +36,17 @@ static SPINAND_OP_VARIANTS(gd5fxgq4_read + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + +-/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */ +-static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants, ++static SPINAND_OP_VARIANTS(read_cache_variants_f, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0)); ++ ++/* For Q5 devices, QUADIO use different dummy byte settings */ ++/* Q5 1Gb */ ++static SPINAND_OP_VARIANTS(dummy2_read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), +@@ -40,6 +54,15 @@ static SPINAND_OP_VARIANTS(gd5f1gq5_read + SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); + ++/* Q5 2Gb & 4Gb */ ++static SPINAND_OP_VARIANTS(dummy4_read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ + static SPINAND_OP_VARIANTS(write_cache_variants, + SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), + SPINAND_PROG_LOAD(true, 0, NULL, 0)); +@@ -48,7 +71,65 @@ static SPINAND_OP_VARIANTS(update_cache_ + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + +-static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section, ++static int gd5fxgq4xa_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 gd5fxgq4xa_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 { ++ /* section 0 has one byte reserved for bad block mark */ ++ region->offset = 1; ++ region->length = 7; ++ } ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = { ++ .ecc = gd5fxgq4xa_ooblayout_ecc, ++ .rfree = gd5fxgq4xa_ooblayout_free, ++}; ++ ++static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS: ++ /* 1-7 bits are flipped. return the maximum. */ ++ return 7; ++ ++ case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS: ++ return 8; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -60,7 +141,7 @@ static int gd5fxgqxxexxg_ooblayout_ecc(s + return 0; + } + +-static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section, ++static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { + if (section) +@@ -73,7 +154,13 @@ static int gd5fxgqxxexxg_ooblayout_free( + return 0; + } + +-static int gd5fxgq4xexxg_ecc_get_status(struct spinand_device *spinand, ++/* Valid for Q4/Q5 and Q6 (untested) devices */ ++static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = { ++ .ecc = gd5fxgqx_variant2_ooblayout_ecc, ++ .rfree = gd5fxgqx_variant2_ooblayout_free, ++}; ++ ++static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, + u8 status) + { + u8 status2; +@@ -152,59 +239,214 @@ static int gd5fxgq5xexxg_ecc_get_status( + return -EINVAL; + } + +-static const struct mtd_ooblayout_ops gd5fxgqxxexxg_ooblayout = { +- .ecc = gd5fxgqxxexxg_ooblayout_ecc, +- .rfree = gd5fxgqxxexxg_ooblayout_free, ++static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) { ++ case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS: ++ return 3; ++ ++ case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */ ++ return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2; ++ } ++ ++ return -EINVAL; ++} ++ ++static int esmt_1_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 esmt_1_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 2; ++ region->length = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops esmt_1_ooblayout = { ++ .ecc = esmt_1_ooblayout_ecc, ++ .rfree = esmt_1_ooblayout_free, + }; + + static const struct spinand_info gigadevice_spinand_table[] = { +- SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, +- NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ SPINAND_INFO("F50L1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), +- SPINAND_INFO_OP_VARIANTS(&gd5fxgq4_read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, +- SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout, +- gd5fxgq4xexxg_ecc_get_status)), +- SPINAND_INFO("GD5F1GQ5UExxG", 0x51, ++ SPINAND_ECCINFO(&esmt_1_ooblayout, NULL)), ++ SPINAND_INFO("GD5F1GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, ++ gd5fxgq4xa_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, ++ gd5fxgq4xa_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ4xA", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), ++ NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, ++ gd5fxgq4xa_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ4UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ4UFxxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4ufxxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), +- SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants, ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, + &write_cache_variants, + &update_cache_variants), +- 0, +- SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout, ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x55), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x91), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GM7UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x92), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GM8UExxG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x95), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq4uexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F1GQ5UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F2GQ5UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, ++ gd5fxgq5xexxg_ecc_get_status)), ++ SPINAND_INFO("GD5F4GQ6UExxH", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), ++ NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq5xexxg_ecc_get_status)), + }; + +-static int gigadevice_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * For GD NANDs, There is an address byte needed to shift in before IDs +- * are read out, so the first byte in raw_id is dummy. +- */ +- if (id[1] != SPINAND_MFR_GIGADEVICE) +- return 0; +- +- ret = spinand_match_and_init(spinand, gigadevice_spinand_table, +- ARRAY_SIZE(gigadevice_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { +- .detect = gigadevice_spinand_detect, + }; + + const struct spinand_manufacturer gigadevice_spinand_manufacturer = { + .id = SPINAND_MFR_GIGADEVICE, + .name = "GigaDevice", ++ .chips = gigadevice_spinand_table, ++ .nchips = ARRAY_SIZE(gigadevice_spinand_table), + .ops = &gigadevice_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/macronix.c ++++ b/drivers/mtd/nand/spi/macronix.c +@@ -105,7 +105,8 @@ static int mx35lf1ge4ab_ecc_get_status(s + } + + static const struct spinand_info macronix_spinand_table[] = { +- SPINAND_INFO("MX35LF1GE4AB", 0x12, ++ SPINAND_INFO("MX35LF1GE4AB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -114,7 +115,8 @@ static const struct spinand_info macroni + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35LF2GE4AB", 0x22, ++ SPINAND_INFO("MX35LF2GE4AB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), + NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -122,7 +124,96 @@ static const struct spinand_info macroni + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), +- SPINAND_INFO("MX35UF4GE4AD", 0xb7, ++ SPINAND_INFO("MX35LF2GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35LF4GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37), ++ NAND_MEMORG(1, 4096, 128, 64, 2048, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35LF1G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX35LF2G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX35LF4G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), ++ SPINAND_INFO("MX31LF1GE4BC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX31UF1GE4BC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ ++ SPINAND_INFO("MX35LF2G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF4G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5), ++ NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF4GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -131,7 +222,28 @@ static const struct spinand_info macroni + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35UF2GE4AD", 0xa6, ++ SPINAND_INFO("MX35UF2G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0), ++ NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF2GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6), + NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -140,16 +252,28 @@ static const struct spinand_info macroni + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35UF2GE4AC", 0xa2, ++ SPINAND_INFO("MX35UF2GE4AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2), + NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1), + NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1G14AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35UF1GE4AD", 0x96, ++ SPINAND_INFO("MX35UF1G24AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -158,7 +282,18 @@ static const struct spinand_info macroni + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, + mx35lf1ge4ab_ecc_get_status)), +- SPINAND_INFO("MX35UF1GE4AC", 0x92, ++ SPINAND_INFO("MX35UF1GE4AD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96), ++ NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ SPINAND_HAS_QE_BIT, ++ SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, ++ mx35lf1ge4ab_ecc_get_status)), ++ SPINAND_INFO("MX35UF1GE4AC", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92), + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -170,33 +305,13 @@ static const struct spinand_info macroni + + }; + +-static int macronix_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Macronix SPI NAND read ID needs a dummy byte, so the first byte in +- * raw_id is garbage. +- */ +- if (id[1] != SPINAND_MFR_MACRONIX) +- return 0; +- +- ret = spinand_match_and_init(spinand, macronix_spinand_table, +- ARRAY_SIZE(macronix_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { +- .detect = macronix_spinand_detect, + }; + + const struct spinand_manufacturer macronix_spinand_manufacturer = { + .id = SPINAND_MFR_MACRONIX, + .name = "Macronix", ++ .chips = macronix_spinand_table, ++ .nchips = ARRAY_SIZE(macronix_spinand_table), + .ops = ¯onix_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/micron.c ++++ b/drivers/mtd/nand/spi/micron.c +@@ -120,7 +120,8 @@ static int micron_8_ecc_get_status(struc + + static const struct spinand_info micron_spinand_table[] = { + /* M79A 2Gb 3.3V */ +- SPINAND_INFO("MT29F2G01ABAGD", 0x24, ++ SPINAND_INFO("MT29F2G01ABAGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -130,7 +131,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M79A 2Gb 1.8V */ +- SPINAND_INFO("MT29F2G01ABBGD", 0x25, ++ SPINAND_INFO("MT29F2G01ABBGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25), + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -140,7 +142,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 3.3V */ +- SPINAND_INFO("MT29F1G01ABAFD", 0x14, ++ SPINAND_INFO("MT29F1G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -150,7 +153,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M78A 1Gb 1.8V */ +- SPINAND_INFO("MT29F1G01ABAFD", 0x15, ++ SPINAND_INFO("MT29F1G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -160,7 +164,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M79A 4Gb 3.3V */ +- SPINAND_INFO("MT29F4G01ADAGD", 0x36, ++ SPINAND_INFO("MT29F4G01ADAGD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36), + NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -171,7 +176,8 @@ static const struct spinand_info micron_ + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), + /* M70A 4Gb 3.3V */ +- SPINAND_INFO("MT29F4G01ABAFD", 0x34, ++ SPINAND_INFO("MT29F4G01ABAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -181,7 +187,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M70A 4Gb 1.8V */ +- SPINAND_INFO("MT29F4G01ABBFD", 0x35, ++ SPINAND_INFO("MT29F4G01ABBFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -191,7 +198,8 @@ static const struct spinand_info micron_ + SPINAND_ECCINFO(µn_8_ooblayout, + micron_8_ecc_get_status)), + /* M70A 8Gb 3.3V */ +- SPINAND_INFO("MT29F8G01ADAFD", 0x46, ++ SPINAND_INFO("MT29F8G01ADAFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -202,7 +210,8 @@ static const struct spinand_info micron_ + micron_8_ecc_get_status), + SPINAND_SELECT_TARGET(micron_select_target)), + /* M70A 8Gb 1.8V */ +- SPINAND_INFO("MT29F8G01ADBFD", 0x47, ++ SPINAND_INFO("MT29F8G01ADBFD", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -214,26 +223,6 @@ static const struct spinand_info micron_ + SPINAND_SELECT_TARGET(micron_select_target)), + }; + +-static int micron_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Micron SPI NAND read ID need a dummy byte, +- * so the first byte in raw_id is dummy. +- */ +- if (id[1] != SPINAND_MFR_MICRON) +- return 0; +- +- ret = spinand_match_and_init(spinand, micron_spinand_table, +- ARRAY_SIZE(micron_spinand_table), id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static int micron_spinand_init(struct spinand_device *spinand) + { + /* +@@ -248,12 +237,13 @@ static int micron_spinand_init(struct sp + } + + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { +- .detect = micron_spinand_detect, + .init = micron_spinand_init, + }; + + const struct spinand_manufacturer micron_spinand_manufacturer = { + .id = SPINAND_MFR_MICRON, + .name = "Micron", ++ .chips = micron_spinand_table, ++ .nchips = ARRAY_SIZE(micron_spinand_table), + .ops = µn_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/toshiba.c ++++ b/drivers/mtd/nand/spi/toshiba.c +@@ -111,7 +111,8 @@ static int tx58cxgxsxraix_ecc_get_status + + static const struct spinand_info toshiba_spinand_table[] = { + /* 3.3V 1Gb (1st generation) */ +- SPINAND_INFO("TC58CVG0S3HRAIG", 0xC2, ++ SPINAND_INFO("TC58CVG0S3HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -121,7 +122,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 2Gb (1st generation) */ +- SPINAND_INFO("TC58CVG1S3HRAIG", 0xCB, ++ SPINAND_INFO("TC58CVG1S3HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -131,7 +133,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 4Gb (1st generation) */ +- SPINAND_INFO("TC58CVG2S0HRAIG", 0xCD, ++ SPINAND_INFO("TC58CVG2S0HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -141,7 +144,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb (1st generation) */ +- SPINAND_INFO("TC58CYG0S3HRAIG", 0xB2, ++ SPINAND_INFO("TC58CYG0S3HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -151,7 +155,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 2Gb (1st generation) */ +- SPINAND_INFO("TC58CYG1S3HRAIG", 0xBB, ++ SPINAND_INFO("TC58CYG1S3HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -161,7 +166,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb (1st generation) */ +- SPINAND_INFO("TC58CYG2S0HRAIG", 0xBD, ++ SPINAND_INFO("TC58CYG2S0HRAIG", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -176,7 +182,8 @@ static const struct spinand_info toshiba + * QE_BIT. + */ + /* 3.3V 1Gb (2nd generation) */ +- SPINAND_INFO("TC58CVG0S3HRAIJ", 0xE2, ++ SPINAND_INFO("TC58CVG0S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -186,7 +193,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 2Gb (2nd generation) */ +- SPINAND_INFO("TC58CVG1S3HRAIJ", 0xEB, ++ SPINAND_INFO("TC58CVG1S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -196,7 +204,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 4Gb (2nd generation) */ +- SPINAND_INFO("TC58CVG2S0HRAIJ", 0xED, ++ SPINAND_INFO("TC58CVG2S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -206,7 +215,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 3.3V 8Gb (2nd generation) */ +- SPINAND_INFO("TH58CVG3S0HRAIJ", 0xE4, ++ SPINAND_INFO("TH58CVG3S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -216,7 +226,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb (2nd generation) */ +- SPINAND_INFO("TC58CYG0S3HRAIJ", 0xD2, ++ SPINAND_INFO("TC58CYG0S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2), + NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -226,7 +237,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 2Gb (2nd generation) */ +- SPINAND_INFO("TC58CYG1S3HRAIJ", 0xDB, ++ SPINAND_INFO("TC58CYG1S3HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB), + NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -236,7 +248,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb (2nd generation) */ +- SPINAND_INFO("TC58CYG2S0HRAIJ", 0xDD, ++ SPINAND_INFO("TC58CYG2S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD), + NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -246,7 +259,8 @@ static const struct spinand_info toshiba + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 8Gb (2nd generation) */ +- SPINAND_INFO("TH58CYG3S0HRAIJ", 0xD4, ++ SPINAND_INFO("TH58CYG3S0HRAIJ", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4), + NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -257,33 +271,13 @@ static const struct spinand_info toshiba + tx58cxgxsxraix_ecc_get_status)), + }; + +-static int toshiba_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Toshiba SPI NAND read ID needs a dummy byte, +- * so the first byte in id is garbage. +- */ +- if (id[1] != SPINAND_MFR_TOSHIBA) +- return 0; +- +- ret = spinand_match_and_init(spinand, toshiba_spinand_table, +- ARRAY_SIZE(toshiba_spinand_table), +- id[2]); +- if (ret) +- return ret; +- +- return 1; +-} +- + static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { +- .detect = toshiba_spinand_detect, + }; + + const struct spinand_manufacturer toshiba_spinand_manufacturer = { + .id = SPINAND_MFR_TOSHIBA, + .name = "Toshiba", ++ .chips = toshiba_spinand_table, ++ .nchips = ARRAY_SIZE(toshiba_spinand_table), + .ops = &toshiba_spinand_manuf_ops, + }; +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -19,6 +19,25 @@ + + #define WINBOND_CFG_BUF_READ BIT(3) + ++#define W25N02_N04KV_STATUS_ECC_MASK (3 << 4) ++#define W25N02_N04KV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS (1 << 4) ++#define W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4) ++#define W25N02_N04KV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++#define W25N01_M02GV_STATUS_ECC_MASK (3 << 4) ++#define W25N01_M02GV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N01_M02GV_STATUS_ECC_1_BITFLIPS (1 << 4) ++#define W25N01_M02GV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++ ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++#define W25N01KV_STATUS_ECC_MASK (3 << 4) ++#define W25N01KV_STATUS_ECC_NO_BITFLIPS (0 << 4) ++#define W25N01KV_STATUS_ECC_1_3_BITFLIPS (1 << 4) ++#define W25N01KV_STATUS_ECC_4_BITFLIPS (3 << 4) ++#define W25N01KV_STATUS_ECC_UNCOR_ERROR (2 << 4) ++#endif ++ + static SPINAND_OP_VARIANTS(read_cache_variants, + SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), + SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), +@@ -35,6 +54,35 @@ static SPINAND_OP_VARIANTS(update_cache_ + SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), + SPINAND_PROG_LOAD(false, 0, NULL, 0)); + ++static int w25n02kv_n04kv_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 64; ++ region->length = 16; ++ ++ return 0; ++} ++ ++static int w25n02kv_n04kv_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 2; ++ region->length = 14; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops w25n02kv_n04kv_ooblayout = { ++ .ecc = w25n02kv_n04kv_ooblayout_ecc, ++ .rfree = w25n02kv_n04kv_ooblayout_free, ++}; ++ + static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section, + struct mtd_oob_region *region) + { +@@ -78,8 +126,63 @@ static int w25m02gv_select_target(struct + return spi_mem_exec_op(spinand->slave, &op); + } + ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++static int w25n01kv_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & W25N01KV_STATUS_ECC_MASK) { ++ case W25N01KV_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case W25N01KV_STATUS_ECC_1_3_BITFLIPS: ++ return 3; ++ ++ case W25N01KV_STATUS_ECC_4_BITFLIPS: ++ return 4; ++ ++ case W25N01KV_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++#endif ++ ++static int w25n02kv_n04kv_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ switch (status & W25N02_N04KV_STATUS_ECC_MASK) { ++ case W25N02_N04KV_STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS: ++ return 3; ++ ++ case W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS: ++ return 4; ++ ++ /* W25N02_N04KV_use internal 8bit ECC algorithm. ++ * But the ECC strength is 4 bit requried. ++ * Return 3 if the bit bit flip count less than 5. ++ * Return 4 if the bit bit flip count more than 5 to 8. ++ */ ++ ++ case W25N02_N04KV_STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct spinand_info winbond_spinand_table[] = { +- SPINAND_INFO("W25M02GV", 0xAB, ++ SPINAND_INFO("W25M02GV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -88,7 +191,19 @@ static const struct spinand_info winbond + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), +- SPINAND_INFO("W25N01GV", 0xAA, ++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV) ++ SPINAND_INFO("W25N01KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), ++ NAND_MEMORG(1, 2048, 96, 64, 1024, 1, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, w25n01kv_ecc_get_status)), ++#else ++ SPINAND_INFO("W25N01GV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -96,32 +211,31 @@ static const struct spinand_info winbond + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), +-}; +- +-/** +- * winbond_spinand_detect - initialize device related part in spinand_device +- * struct if it is a Winbond device. +- * @spinand: SPI NAND device structure +- */ +-static int winbond_spinand_detect(struct spinand_device *spinand) +-{ +- u8 *id = spinand->id.data; +- int ret; +- +- /* +- * Winbond SPI NAND read ID need a dummy byte, +- * so the first byte in raw_id is dummy. ++#endif ++ SPINAND_INFO("W25N02KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, ++ w25n02kv_n04kv_ecc_get_status)), ++ /* W25N04KV has 2-die(lun), however, it can select die automatically. ++ * Treat it as single die here and double block size. + */ +- if (id[1] != SPINAND_MFR_WINBOND) +- return 0; +- +- ret = spinand_match_and_init(spinand, winbond_spinand_table, +- ARRAY_SIZE(winbond_spinand_table), id[2]); +- if (ret) +- return ret; +- +- return 1; +-} ++ SPINAND_INFO("W25N04KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23), ++ NAND_MEMORG(1, 2048, 128, 64, 4096, 2, 1, 1), ++ NAND_ECCREQ(4, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, ++ w25n02kv_n04kv_ecc_get_status)), ++}; + + static int winbond_spinand_init(struct spinand_device *spinand) + { +@@ -142,12 +256,13 @@ static int winbond_spinand_init(struct s + } + + static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { +- .detect = winbond_spinand_detect, + .init = winbond_spinand_init, + }; + + const struct spinand_manufacturer winbond_spinand_manufacturer = { + .id = SPINAND_MFR_WINBOND, + .name = "Winbond", ++ .chips = winbond_spinand_table, ++ .nchips = ARRAY_SIZE(winbond_spinand_table), + .ops = &winbond_spinand_manuf_ops, + }; +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -39,15 +39,15 @@ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +-#define SPINAND_READID_OP(ndummy, buf, len) \ ++#define SPINAND_READID_OP(naddr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ +- SPI_MEM_OP_NO_ADDR, \ ++ SPI_MEM_OP_ADDR(naddr, 0, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + + #define SPINAND_SET_FEATURE_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \ +- SPI_MEM_OP_ADDR(1, reg, 1), \ ++ SPI_MEM_OP_ADDR(1, reg, 1), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(1, valptr, 1)) + +@@ -75,18 +75,36 @@ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + ++#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len)\ ++ SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \ ++ SPI_MEM_OP_ADDR(3, addr, 1), \ ++ SPI_MEM_OP_DUMMY(ndummy, 1), \ ++ SPI_MEM_OP_DATA_IN(len, buf, 1)) ++ + #define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 2)) + ++#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len) \ ++ SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \ ++ SPI_MEM_OP_ADDR(3, addr, 1), \ ++ SPI_MEM_OP_DUMMY(ndummy, 1), \ ++ SPI_MEM_OP_DATA_IN(len, buf, 2)) ++ + #define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 4)) + ++#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len) \ ++ SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \ ++ SPI_MEM_OP_ADDR(3, addr, 1), \ ++ SPI_MEM_OP_DUMMY(ndummy, 1), \ ++ SPI_MEM_OP_DATA_IN(len, buf, 4)) ++ + #define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \ + SPI_MEM_OP_ADDR(2, addr, 2), \ +@@ -153,37 +171,46 @@ struct spinand_device; + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length +- * +- * struct_spinand_id->data contains all bytes returned after a READ_ID command, +- * including dummy bytes if the chip does not emit ID bytes right after the +- * READ_ID command. The responsibility to extract real ID bytes is left to +- * struct_manufacurer_ops->detect(). + */ + struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; + }; + ++enum spinand_readid_method { ++ SPINAND_READID_METHOD_OPCODE, ++ SPINAND_READID_METHOD_OPCODE_ADDR, ++ SPINAND_READID_METHOD_OPCODE_DUMMY, ++}; ++ ++/** ++ * struct spinand_devid - SPI NAND device id structure ++ * @id: device id of current chip ++ * @len: number of bytes in device id ++ * @method: method to read chip id ++ * There are 3 possible variants: ++ * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately ++ * after read_id opcode. ++ * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after ++ * read_id opcode + 1-byte address. ++ * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after ++ * read_id opcode + 1 dummy byte. ++ */ ++struct spinand_devid { ++ const u8 *id; ++ const u8 len; ++ const enum spinand_readid_method method; ++}; ++ + /** + * struct manufacurer_ops - SPI NAND manufacturer specific operations +- * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed +- * the core calls the struct_manufacurer_ops->detect() hook of each +- * registered manufacturer until one of them return 1. Note that +- * the first thing to check in this hook is that the manufacturer ID +- * in struct_spinand_device->id matches the manufacturer whose +- * ->detect() hook has been called. Should return 1 if there's a +- * match, 0 if the manufacturer ID does not match and a negative +- * error code otherwise. When true is returned, the core assumes +- * that properties of the NAND chip (spinand->base.memorg and +- * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that +- * NAND chips coming from this vendor can be detected and initialized properly. ++ * NAND chips coming from this vendor can be initialized properly. + */ + struct spinand_manufacturer_ops { +- int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); + }; +@@ -192,11 +219,16 @@ struct spinand_manufacturer_ops { + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name ++ * @devid_len: number of bytes in device ID ++ * @chips: supported SPI NANDs under current manufacturer ++ * @nchips: number of SPI NANDs available in chips array + * @ops: manufacturer operations + */ + struct spinand_manufacturer { + u8 id; + char *name; ++ const struct spinand_info *chips; ++ const size_t nchips; + const struct spinand_manufacturer_ops *ops; + }; + +@@ -268,7 +300,7 @@ struct spinand_ecc_info { + */ + struct spinand_info { + const char *model; +- u8 devid; ++ struct spinand_devid devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; +@@ -282,6 +314,13 @@ struct spinand_info { + unsigned int target); + }; + ++#define SPINAND_ID(__method, ...) \ ++ { \ ++ .id = (const u8[]){ __VA_ARGS__ }, \ ++ .len = sizeof((u8[]){ __VA_ARGS__ }), \ ++ .method = __method, \ ++ } ++ + #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ +@@ -440,9 +479,10 @@ static inline void spinand_set_ofnode(st + } + #endif /* __UBOOT__ */ + +-int spinand_match_and_init(struct spinand_device *dev, ++int spinand_match_and_init(struct spinand_device *spinand, + const struct spinand_info *table, +- unsigned int table_size, u8 devid); ++ unsigned int table_size, ++ enum spinand_readid_method rdid_method); + + int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); + int spinand_select_target(struct spinand_device *spinand, unsigned int target); diff --git a/package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch b/package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch new file mode 100644 index 00000000000..b6a22291707 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch @@ -0,0 +1,78 @@ +From 793bed29e78cc54d989333d756fef51efaca4e56 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 26 Jul 2022 09:29:18 +0800 +Subject: [PATCH 58/71] mmc: mtk-sd: add support to display verbose error log + +Add an option to enable debug log, and also display verbose error log for +both command and data. + +Signed-off-by: Weijie Gao +--- + drivers/mmc/Kconfig | 8 ++++++++ + drivers/mmc/Makefile | 4 ++++ + drivers/mmc/mtk-sd.c | 24 +++++++++++++++--------- + 3 files changed, 27 insertions(+), 9 deletions(-) + +--- a/drivers/mmc/Kconfig ++++ b/drivers/mmc/Kconfig +@@ -789,6 +789,14 @@ config MMC_MTK + This is needed if support for any SD/SDIO/MMC devices is required. + If unsure, say N. + ++config MMC_MTK_DEBUG ++ bool "Display verbose error log" ++ default n ++ depends on MMC_MTK ++ help ++ Enable this option to allow verbose error log being displayed for ++ debugging. ++ + endif + + config FSL_ESDHC +--- a/drivers/mmc/Makefile ++++ b/drivers/mmc/Makefile +@@ -84,3 +84,7 @@ obj-$(CONFIG_RENESAS_SDHI) += tmio-comm + obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o + obj-$(CONFIG_MMC_MTK) += mtk-sd.o + obj-$(CONFIG_MMC_SDHCI_F_SDH30) += f_sdh30.o ++ ++ifdef CONFIG_MMC_MTK_DEBUG ++CFLAGS_mtk-sd.o += -DDEBUG ++endif +--- a/drivers/mmc/mtk-sd.c ++++ b/drivers/mmc/mtk-sd.c +@@ -778,18 +778,24 @@ static int msdc_ops_send_cmd(struct udev + if (cmd_ret && + !(cmd_ret == -EIO && + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK || +- cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200))) ++ cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200))) { ++ dev_dbg(dev, "MSDC start command failure with %d, cmd=%d, arg=0x%x\n", ++ cmd_ret, cmd->cmdidx, cmd->cmdarg); + return cmd_ret; +- +- if (data) { +- data_ret = msdc_start_data(host, data); +- if (cmd_ret) +- return cmd_ret; +- else +- return data_ret; + } + +- return 0; ++ if (!data) ++ return cmd_ret; ++ ++ data_ret = msdc_start_data(host, data); ++ if (cmd_ret) ++ return cmd_ret; ++ ++ if (data_ret) ++ dev_dbg(dev, "MSDC start data failure with %d, cmd=%d, arg=0x%x\n", ++ data_ret, cmd->cmdidx, cmd->cmdarg); ++ ++ return data_ret; + } + + static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) diff --git a/package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch b/package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch new file mode 100644 index 00000000000..ba32fa869df --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch @@ -0,0 +1,58 @@ +From dd66fc817f7ab7a4fcab9836a9251a8f64f329df Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 16:58:36 +0800 +Subject: [PATCH 59/71] cmd: ubi: make volume find/create/remove APIs public + +Export ubi_create_vol/ubi_find_volume/ubi_remove_vol to public so that they +can be used by other programs. + +Signed-off-by: Weijie Gao +--- + cmd/ubi.c | 8 ++++---- + include/ubi_uboot.h | 4 ++++ + 2 files changed, 8 insertions(+), 4 deletions(-) + +--- a/cmd/ubi.c ++++ b/cmd/ubi.c +@@ -148,8 +148,8 @@ bad: + return err; + } + +-static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id, +- bool skipcheck) ++int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id, ++ bool skipcheck) + { + struct ubi_mkvol_req req; + int err; +@@ -182,7 +182,7 @@ static int ubi_create_vol(char *volume, + return ubi_create_volume(ubi, &req); + } + +-static struct ubi_volume *ubi_find_volume(char *volume) ++struct ubi_volume *ubi_find_volume(char *volume) + { + struct ubi_volume *vol = NULL; + int i; +@@ -197,7 +197,7 @@ static struct ubi_volume *ubi_find_volum + return NULL; + } + +-static int ubi_remove_vol(char *volume) ++int ubi_remove_vol(char *volume) + { + int err, reserved_pebs, i; + struct ubi_volume *vol; +--- a/include/ubi_uboot.h ++++ b/include/ubi_uboot.h +@@ -73,6 +73,10 @@ extern void ubi_exit(void); + extern int ubi_part(char *part_name, const char *vid_header_offset); + extern int ubi_volume_write(char *volume, void *buf, size_t size); + extern int ubi_volume_read(char *volume, char *buf, size_t size); ++extern int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id, ++ bool skipcheck); ++extern struct ubi_volume *ubi_find_volume(char *volume); ++extern int ubi_remove_vol(char *volume); + + extern struct ubi_device *ubi_devices[]; + int cmd_ubifs_mount(char *vol_name); diff --git a/package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch b/package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch new file mode 100644 index 00000000000..1d62c05e40a --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch @@ -0,0 +1,27 @@ +From f6a4130959af1e6d13d616203e42ed3c894666ad Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 17:00:00 +0800 +Subject: [PATCH 60/71] cmd: ubi: allow creating volume with all free spaces + +Allow creating volume with all free spaces by giving a negative size value. + +Signed-off-by: Weijie Gao +--- + cmd/ubi.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/cmd/ubi.c ++++ b/cmd/ubi.c +@@ -161,7 +161,11 @@ int ubi_create_vol(char *volume, int64_t + + req.vol_id = vol_id; + req.alignment = 1; +- req.bytes = size; ++ ++ if (size < 0) ++ req.bytes = ubi->avail_pebs * ubi->leb_size; ++ else ++ req.bytes = size; + + strcpy(req.name, volume); + req.name_len = strlen(volume); diff --git a/package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch b/package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch new file mode 100644 index 00000000000..08b14a6b291 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch @@ -0,0 +1,72 @@ +From fc0c70a7c6a088072d0c77e5a59d5e9b7754c6db Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 17:01:20 +0800 +Subject: [PATCH 61/71] env: ubi: add support to create environment volume if + it does not exist + +Add an option to allow environment volume being auto created if not exist. + +Signed-off-by: Weijie Gao +--- + env/Kconfig | 6 ++++++ + env/ubi.c | 20 ++++++++++++++++++++ + 2 files changed, 26 insertions(+) + +--- a/env/Kconfig ++++ b/env/Kconfig +@@ -647,6 +647,12 @@ config ENV_UBI_VOLUME_REDUND + help + Name of the redundant volume that you want to store the environment in. + ++config ENV_UBI_VOLUME_CREATE ++ bool "Create UBI volume if not exist" ++ depends on ENV_IS_IN_UBI ++ help ++ Create the UBI volume if it does not exist. ++ + config ENV_UBI_VID_OFFSET + int "ubi environment VID offset" + depends on ENV_IS_IN_UBI +--- a/env/ubi.c ++++ b/env/ubi.c +@@ -100,6 +100,18 @@ static int env_ubi_save(void) + #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */ + #endif /* CONFIG_CMD_SAVEENV */ + ++int __weak env_ubi_volume_create(const char *volume) ++{ ++ struct ubi_volume *vol; ++ ++ vol = ubi_find_volume((char *)volume); ++ if (vol) ++ return 0; ++ ++ return ubi_create_vol((char *)volume, CONFIG_ENV_SIZE, true, ++ UBI_VOL_NUM_AUTO, false); ++} ++ + #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT + static int env_ubi_load(void) + { +@@ -129,6 +141,11 @@ static int env_ubi_load(void) + return -EIO; + } + ++ if (IS_ENABLED(CONFIG_ENV_UBI_VOLUME_CREATE)) { ++ env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME); ++ env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME_REDUND); ++ } ++ + read1_fail = ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1, + CONFIG_ENV_SIZE); + if (read1_fail) +@@ -166,6 +183,9 @@ static int env_ubi_load(void) + return -EIO; + } + ++ if (IS_ENABLED(CONFIG_ENV_UBI_VOLUME_CREATE)) ++ env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME); ++ + if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, buf, CONFIG_ENV_SIZE)) { + printf("\n** Unable to read env from %s:%s **\n", + CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME); diff --git a/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch b/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch similarity index 65% rename from package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch rename to package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch index 5d312eca8fe..cd1794f0d11 100644 --- a/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch +++ b/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch @@ -1,3 +1,18 @@ +From 189a2fe96931ef3ea0e187c8e9bfa589c2a0ae10 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Mon, 25 Jul 2022 17:24:56 +0800 +Subject: [PATCH 62/71] mtd: ubi: add support for UBI end-of-filesystem marker + used by OpenWrt + +Add support for UBI end-of-filesystem marker used by OpenWrt to allow +attaching a new UBI mtd partition just upgraded. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++--- + drivers/mtd/ubi/ubi.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -802,6 +802,13 @@ out_unlock: diff --git a/package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch b/package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch new file mode 100644 index 00000000000..110b1bb2377 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch @@ -0,0 +1,26 @@ +--- a/include/configs/mt7986.h ++++ b/include/configs/mt7986.h +@@ -11,6 +11,11 @@ + + #include + ++#define CONFIG_SYS_MAXARGS 32 ++#define CONFIG_SYS_BOOTM_LEN SZ_128M ++#define CONFIG_SYS_CBSIZE SZ_1K ++#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + \ ++ sizeof(CONFIG_SYS_PROMPT) + 16) + #define CONFIG_SYS_NONCACHED_MEMORY SZ_1M + #define CONFIG_SYS_MMC_ENV_DEV 0 + +@@ -19,6 +24,11 @@ + + /* SPL -> Uboot */ + #define CONFIG_SYS_UBOOT_START CONFIG_SYS_TEXT_BASE ++#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_TEXT_BASE + SZ_2M - \ ++ GENERATED_GBL_DATA_SIZE) ++ ++/* Flash */ ++#define CONFIG_SYS_NAND_MAX_CHIPS 1 + + /* DRAM */ + #define CONFIG_SYS_SDRAM_BASE 0x40000000 diff --git a/package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch b/package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch new file mode 100644 index 00000000000..9808e2c860e --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch @@ -0,0 +1,240 @@ +From 6792b57b3ba61ca6d69ea4a13a58bed65fc5da87 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 7 Aug 2022 04:04:46 +0200 +Subject: [PATCH] board: mediatek: wire-up NMBM support + +--- + board/mediatek/mt7622/mt7622_rfb.c | 38 +++++++++++++++++++++ + board/mediatek/mt7629/mt7629_rfb.c | 38 +++++++++++++++++++++ + board/mediatek/mt7981/mt7981_rfb.c | 52 ++++++++++++++++++++++++++++ + board/mediatek/mt7986/mt7986_rfb.c | 54 ++++++++++++++++++++++++++++++ + 4 files changed, 182 insertions(+) + +--- a/board/mediatek/mt7622/mt7622_rfb.c ++++ b/board/mediatek/mt7622/mt7622_rfb.c +@@ -10,6 +10,11 @@ + #include + #include + ++#include ++#include ++#include ++#include ++ + DECLARE_GLOBAL_DATA_PTR; + + int board_init(void) +@@ -24,3 +29,36 @@ int board_late_init(void) + env_relocate(); + return 0; + } ++ ++int board_nmbm_init(void) ++{ ++#ifdef CONFIG_ENABLE_NAND_NMBM ++ struct mtd_info *lower, *upper; ++ int ret; ++ ++ printf("\n"); ++ printf("Initializing NMBM ...\n"); ++ ++ mtd_probe_devices(); ++ ++ lower = get_mtd_device_nm("spi-nand0"); ++ if (IS_ERR(lower) || !lower) { ++ printf("Lower MTD device 'spi-nand0' not found\n"); ++ return 0; ++ } ++ ++ ret = nmbm_attach_mtd(lower, ++ NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK, ++ CONFIG_NMBM_MAX_RATIO, ++ CONFIG_NMBM_MAX_BLOCKS, &upper); ++ ++ printf("\n"); ++ ++ if (ret) ++ return 0; ++ ++ add_mtd_device(upper); ++#endif ++ ++ return 0; ++} +--- a/board/mediatek/mt7629/mt7629_rfb.c ++++ b/board/mediatek/mt7629/mt7629_rfb.c +@@ -6,6 +6,11 @@ + #include + #include + ++#include ++#include ++#include ++#include ++ + DECLARE_GLOBAL_DATA_PTR; + + int board_init(void) +@@ -20,3 +25,36 @@ uint32_t spl_nand_get_uboot_raw_page(voi + { + return CONFIG_SPL_PAD_TO; + } ++ ++int board_nmbm_init(void) ++{ ++#ifdef CONFIG_ENABLE_NAND_NMBM ++ struct mtd_info *lower, *upper; ++ int ret; ++ ++ printf("\n"); ++ printf("Initializing NMBM ...\n"); ++ ++ mtd_probe_devices(); ++ ++ lower = get_mtd_device_nm("spi-nand0"); ++ if (IS_ERR(lower) || !lower) { ++ printf("Lower MTD device 'spi-nand0' not found\n"); ++ return 0; ++ } ++ ++ ret = nmbm_attach_mtd(lower, ++ NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK, ++ CONFIG_NMBM_MAX_RATIO, ++ CONFIG_NMBM_MAX_BLOCKS, &upper); ++ ++ printf("\n"); ++ ++ if (ret) ++ return 0; ++ ++ add_mtd_device(upper); ++#endif ++ ++ return 0; ++} +--- a/board/mediatek/mt7981/mt7981_rfb.c ++++ b/board/mediatek/mt7981/mt7981_rfb.c +@@ -4,7 +4,59 @@ + * Author: Sam Shih + */ + ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ + int board_init(void) + { ++ gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; ++ return 0; ++} ++ ++int board_late_init(void) ++{ ++ gd->env_valid = 1; //to load environment variable from persistent store ++ env_relocate(); ++ return 0; ++} ++ ++int board_nmbm_init(void) ++{ ++#ifdef CONFIG_ENABLE_NAND_NMBM ++ struct mtd_info *lower, *upper; ++ int ret; ++ ++ printf("\n"); ++ printf("Initializing NMBM ...\n"); ++ ++ mtd_probe_devices(); ++ ++ lower = get_mtd_device_nm("spi-nand0"); ++ if (IS_ERR(lower) || !lower) { ++ printf("Lower MTD device 'spi-nand0' not found\n"); ++ return 0; ++ } ++ ++ ret = nmbm_attach_mtd(lower, NMBM_F_CREATE, CONFIG_NMBM_MAX_RATIO, ++ CONFIG_NMBM_MAX_BLOCKS, &upper); ++ ++ printf("\n"); ++ ++ if (ret) ++ return 0; ++ ++ add_mtd_device(upper); ++#endif ++ + return 0; + } +--- a/board/mediatek/mt7986/mt7986_rfb.c ++++ b/board/mediatek/mt7986/mt7986_rfb.c +@@ -4,7 +4,61 @@ + * Author: Sam Shih + */ + ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ + int board_init(void) + { ++ gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100; ++ return 0; ++} ++ ++int board_late_init(void) ++{ ++ gd->env_valid = 1; //to load environment variable from persistent store ++ env_relocate(); ++ return 0; ++} ++ ++int board_nmbm_init(void) ++{ ++#ifdef CONFIG_ENABLE_NAND_NMBM ++ struct mtd_info *lower, *upper; ++ int ret; ++ ++ printf("\n"); ++ printf("Initializing NMBM ...\n"); ++ ++ mtd_probe_devices(); ++ ++ lower = get_mtd_device_nm("spi-nand0"); ++ if (IS_ERR(lower) || !lower) { ++ printf("Lower MTD device 'spi-nand0' not found\n"); ++ return 0; ++ } ++ ++ ret = nmbm_attach_mtd(lower, ++ NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK, ++ CONFIG_NMBM_MAX_RATIO, ++ CONFIG_NMBM_MAX_BLOCKS, &upper); ++ ++ printf("\n"); ++ ++ if (ret) ++ return 0; ++ ++ add_mtd_device(upper); ++#endif ++ + return 0; + } diff --git a/package/boot/uboot-mediatek/patches/110-no-kwbimage.patch b/package/boot/uboot-mediatek/patches/110-no-kwbimage.patch index 6d4af1f174f..5fb896d92d2 100644 --- a/package/boot/uboot-mediatek/patches/110-no-kwbimage.patch +++ b/package/boot/uboot-mediatek/patches/110-no-kwbimage.patch @@ -1,6 +1,6 @@ --- a/tools/Makefile +++ b/tools/Makefile -@@ -120,7 +120,6 @@ dumpimage-mkimage-objs := aisimage.o \ +@@ -121,7 +121,6 @@ dumpimage-mkimage-objs := aisimage.o \ imximage.o \ imx8image.o \ imx8mimage.o \ diff --git a/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch b/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch index f1e60c64075..72926ea2e7f 100644 --- a/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch +++ b/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch @@ -1,6 +1,6 @@ --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c -@@ -431,7 +431,11 @@ static void menu_display_statusline(stru +@@ -439,7 +439,11 @@ static void menu_display_statusline(stru printf(ANSI_CURSOR_POSITION, 1, 1); puts(ANSI_CLEAR_LINE); printf(ANSI_CURSOR_POSITION, 2, 3); @@ -13,7 +13,7 @@ puts(ANSI_CLEAR_LINE_TO_END); printf(ANSI_CURSOR_POSITION, 3, 1); puts(ANSI_CLEAR_LINE); -@@ -516,6 +520,7 @@ static enum bootmenu_ret bootmenu_show(i +@@ -524,6 +528,7 @@ static enum bootmenu_ret bootmenu_show(i return BOOTMENU_RET_FAIL; } @@ -23,7 +23,7 @@ goto cleanup; --- a/include/menu.h +++ b/include/menu.h -@@ -40,6 +40,7 @@ struct bootmenu_data { +@@ -43,6 +43,7 @@ struct bootmenu_data { int active; /* active menu entry */ int count; /* total count of menu entries */ struct bootmenu_entry *first; /* first menu entry */ diff --git a/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch b/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch index 544767e2fa2..4ffbc17208f 100644 --- a/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch +++ b/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch @@ -15,7 +15,7 @@ help --- a/cmd/nvedit.c +++ b/cmd/nvedit.c -@@ -408,6 +408,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in +@@ -409,6 +409,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in } #endif @@ -76,7 +76,7 @@ #if defined(CONFIG_CMD_ENV_CALLBACK) static int print_static_binding(const char *var_name, const char *callback_name, void *priv) -@@ -1231,6 +1285,9 @@ static struct cmd_tbl cmd_env_sub[] = { +@@ -1232,6 +1286,9 @@ static struct cmd_tbl cmd_env_sub[] = { U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""), #endif U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""), @@ -86,7 +86,7 @@ #if defined(CONFIG_CMD_RUN) U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""), #endif -@@ -1322,6 +1379,9 @@ static char env_help_text[] = +@@ -1323,6 +1380,9 @@ static char env_help_text[] = #if defined(CONFIG_CMD_NVEDIT_EFI) "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n" #endif @@ -96,7 +96,7 @@ #if defined(CONFIG_CMD_RUN) "env run var [...] - run commands in an environment variable\n" #endif -@@ -1431,6 +1491,17 @@ U_BOOT_CMD( +@@ -1432,6 +1492,17 @@ U_BOOT_CMD( ); #endif diff --git a/package/boot/uboot-mediatek/patches/301-mt7622-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/301-mt7622-generic-reset-button-ignore-env.patch index 8630fc16cef..9fae6d056f6 100644 --- a/package/boot/uboot-mediatek/patches/301-mt7622-generic-reset-button-ignore-env.patch +++ b/package/boot/uboot-mediatek/patches/301-mt7622-generic-reset-button-ignore-env.patch @@ -15,9 +15,9 @@ +#define CONFIG_RESET_BUTTON_LABEL "reset" +#endif - DECLARE_GLOBAL_DATA_PTR; - -@@ -20,7 +27,19 @@ int board_init(void) + #include + #include +@@ -25,7 +32,19 @@ int board_init(void) int board_late_init(void) { @@ -40,9 +40,9 @@ } --- a/arch/arm/mach-mediatek/Kconfig +++ b/arch/arm/mach-mediatek/Kconfig -@@ -136,4 +136,8 @@ config MTK_BROM_HEADER_INFO - default "media=snand;nandinfo=2k+64" if TARGET_MT7981 || TARGET_MT7986 - default "lk=1" if TARGET_MT7623 +@@ -138,4 +138,8 @@ config MTK_BROM_HEADER_INFO + + source "board/mediatek/mt7629/Kconfig" +config RESET_BUTTON_LABEL + string "Button to trigger factory reset" diff --git a/package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch b/package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch deleted file mode 100644 index da746bb124c..00000000000 --- a/package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/drivers/mtd/spi/spi-nor-ids.c -+++ b/drivers/mtd/spi/spi-nor-ids.c -@@ -376,6 +376,8 @@ const struct flash_info spi_nor_ids[] = - SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) - }, - { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, -+ { INFO("w25q512jv", 0xef4020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ | -+ SPI_NOR_HAS_TB | SPI_NOR_HAS_LOCK) }, - { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, - #endif diff --git a/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch b/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch index a30baf00747..8279d250f3e 100644 --- a/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch +++ b/package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch @@ -406,7 +406,7 @@ DECLARE_GLOBAL_DATA_PTR; -@@ -392,6 +393,20 @@ static int initr_onenand(void) +@@ -406,6 +407,20 @@ static int initr_onenand(void) } #endif @@ -427,9 +427,9 @@ #ifdef CONFIG_MMC static int initr_mmc(void) { -@@ -703,6 +718,9 @@ static init_fnc_t init_sequence_r[] = { - #ifdef CONFIG_CMD_ONENAND - initr_onenand, +@@ -720,6 +735,9 @@ static init_fnc_t init_sequence_r[] = { + #ifdef CONFIG_NMBM_MTD + initr_nmbm, #endif +#ifdef CONFIG_SPI_FLASH + initr_spiflash, -- 2.30.2