From d3a337a592bc26be374a70d1aca4aa20080527d4 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 7 Aug 2022 12:06:56 +0200 Subject: 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 --- ...evert-clk-Add-debugging-for-return-values.patch | 69 - ...-mediatek-add-more-network-configurations.patch | 44 - ...-add-support-for-MediaTek-SPI-NAND-flash-.patch | 3696 ------------------ ...-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch | 174 - ...16-env-add-support-for-generic-MTD-device.patch | 409 -- ...629-add-support-for-booting-from-SPI-NAND.patch | 223 -- ...k-18-board-mt7622-use-new-spi-nand-driver.patch | 76 - ...-mt7622-enable-environment-for-mt7622_rfb.patch | 2 +- ...ts-mt7622-remove-default-pinctrl-of-uart0.patch | 2 +- ...dts-mt7622-force-high-speed-mode-for-uart.patch | 2 +- .../002-0018-arm-dts-mt7622-add-i2c-support.patch | 4 +- ...mage-split-the-code-of-generating-NAND-he.patch | 2 +- ...-ids-Add-support-for-flashes-tested-by-xi.patch | 182 + ...-core-Add-support-for-Macronix-Octal-flas.patch | 614 +++ ...-ids-add-winbond-w25q512nw-family-support.patch | 39 + ...0-00-clk-remove-log_ret-from-clk_get_rate.patch | 43 + ...-mediatek-add-more-network-configurations.patch | 70 + ...-add-support-for-MediaTek-SPI-NAND-flash-.patch | 4000 ++++++++++++++++++++ .../100-03-mtd-mtk-snand-add-support-for-SPL.patch | 175 + ...04-env-add-support-for-generic-MTD-device.patch | 409 ++ ...05-mtd-add-a-new-mtd-device-type-for-NMBM.patch | 44 + ...100-06-mtd-add-core-facility-code-of-NMBM.patch | 3422 +++++++++++++++++ .../100-07-mtd-nmbm-add-support-for-mtd.patch | 958 +++++ ...d_r-add-support-to-initialize-NMBM-after-.patch | 46 + .../patches/100-09-cmd-add-nmbm-command.patch | 370 ++ ...d-add-markbad-subcommand-for-NMBM-testing.patch | 80 + ...-env-add-support-for-NMBM-upper-MTD-layer.patch | 280 ++ ...12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch | 173 + ...dd-a-new-command-for-NAND-flash-debugging.patch | 1118 ++++++ ...i-nor-add-support-to-read-flash-unique-ID.patch | 142 + ...md-sf-add-support-to-read-flash-unique-ID.patch | 48 + ...nu-add-ability-to-select-item-by-shortkey.patch | 250 ++ ...spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch | 28 + ...629-add-support-for-booting-from-SPI-NAND.patch | 274 ++ ...0-19-board-mt7622-use-new-spi-nand-driver.patch | 76 + ...1-add-reference-board-using-new-spi-nand-.patch | 223 ++ .../100-21-mtd-spi-nor-add-more-flash-ids.patch | 76 + ...td-spi-nand-backport-from-upstream-kernel.patch | 1550 ++++++++ ...-add-support-to-display-verbose-error-log.patch | 78 + ...ake-volume-find-create-remove-APIs-public.patch | 58 + ...llow-creating-volume-with-all-free-spaces.patch | 27 + ...-support-to-create-environment-volume-if-.patch | 72 + ...-support-for-UBI-end-of-filesystem-marker.patch | 66 + .../100-28-include-configs-mt7986-h-from-SDK.patch | 26 + ...00-29-board-mediatek-wire-up-NMBM-support.patch | 240 ++ .../uboot-mediatek/patches/110-no-kwbimage.patch | 2 +- ...nu-add-ability-to-select-item-by-shortkey.patch | 218 -- .../patches/211-cmd-bootmenu-custom-title.patch | 6 +- .../patches/220-cmd-env-readmem.patch | 8 +- ...01-mt7622-generic-reset-button-ignore-env.patch | 12 +- .../350-add-support-for-Winbond-W25Q512JV.patch | 11 - .../patches/412-add-ubnt-unifi-6-lr.patch | 8 +- .../patches/600-ubi-detect-eof-marker.patch | 51 - 53 files changed, 15281 insertions(+), 4995 deletions(-) delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch delete mode 100644 package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.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 create mode 100644 package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch create mode 100644 package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch create mode 100644 package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch create mode 100644 package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch 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 create mode 100644 package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch create mode 100644 package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch create mode 100644 package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch create mode 100644 package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch 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 create mode 100644 package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch 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/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch delete mode 100644 package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch delete mode 100644 package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.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 3b68c52713..0000000000 --- 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-09-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch deleted file mode 100644 index 50c2ac2e42..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 938ba7ed996a86c9cc7af08b69df57b8b4c09510 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 - -Make the network configurations uniform for mediatek boards - -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(-) - ---- a/include/configs/mt7622.h -+++ b/include/configs/mt7622.h -@@ -30,6 +30,7 @@ - - /* Ethernet */ - #define CONFIG_IPADDR 192.168.1.1 --#define CONFIG_SERVERIP 192.168.1.3 -+#define CONFIG_SERVERIP 192.168.1.2 -+#define CONFIG_NETMASK 255.255.255.0 - - #endif ---- a/include/configs/mt7623.h -+++ b/include/configs/mt7623.h -@@ -45,6 +45,7 @@ - /* Ethernet */ - #define CONFIG_IPADDR 192.168.1.1 - #define CONFIG_SERVERIP 192.168.1.2 -+#define CONFIG_NETMASK 255.255.255.0 - - #ifdef CONFIG_DISTRO_DEFAULTS - ---- a/include/configs/mt7629.h -+++ b/include/configs/mt7629.h -@@ -45,5 +45,6 @@ - /* 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/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch deleted file mode 100644 index 1caa7ffd24..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch +++ /dev/null @@ -1,3696 +0,0 @@ -From d6c5309185aae3d9ecf80eae8b248522d11a6136 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 - controller - -Add mtd driver for MediaTek SPI-NAND flash controller - -This driver is written from scratch, and uses standard mtd framework, not -the nand framework which only applies for raw parallel nand flashes so that -this driver can have a smaller size in binary. - -Signed-off-by: Weijie Gao ---- - drivers/mtd/Kconfig | 2 + - 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-ids.c | 511 +++++++ - drivers/mtd/mtk-snand/mtk-snand-mtd.c | 526 ++++++++ - 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(+) - 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 - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ecc.c - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ids.c - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-mtd.c - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.c - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.h - create mode 100644 drivers/mtd/mtk-snand/mtk-snand.c - create mode 100644 drivers/mtd/mtk-snand/mtk-snand.h - ---- a/drivers/mtd/Kconfig -+++ b/drivers/mtd/Kconfig -@@ -158,6 +158,8 @@ config SYS_MAX_FLASH_BANKS_DETECT - to reduce the effective number of flash bank, between 0 and - CONFIG_SYS_MAX_FLASH_BANKS - -+source "drivers/mtd/mtk-snand/Kconfig" -+ - source "drivers/mtd/nand/Kconfig" - - config SYS_NAND_MAX_CHIPS ---- a/drivers/mtd/Makefile -+++ b/drivers/mtd/Makefile -@@ -39,3 +39,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR - obj-$(CONFIG_SPL_UBI) += ubispl/ - - endif -+ -+obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/ ---- /dev/null -+++ b/drivers/mtd/mtk-snand/Kconfig -@@ -0,0 +1,21 @@ -+# -+# Copyright (C) 2020 MediaTek Inc. All rights reserved. -+# Author: Weijie Gao -+# -+# SPDX-License-Identifier: GPL-2.0 -+# -+ -+config MTK_SPI_NAND -+ tristate "MediaTek SPI NAND flash controller driver" -+ depends on !MTD_SPI_NAND -+ help -+ This option enables access to SPI-NAND flashes through the -+ MediaTek SPI NAND Flash Controller -+ -+config MTK_SPI_NAND_MTD -+ tristate "MTD support for MediaTek SPI NAND flash controller" -+ depends on DM_MTD -+ depends on MTK_SPI_NAND -+ help -+ This option enables access to SPI-NAND flashes through the -+ MTD interface of MediaTek SPI NAND Flash Controller ---- /dev/null -+++ b/drivers/mtd/mtk-snand/Makefile -@@ -0,0 +1,11 @@ -+# -+# Copyright (C) 2020 MediaTek Inc. All rights reserved. -+# Author: Weijie Gao -+# -+# SPDX-License-Identifier: GPL-2.0 -+# -+ -+obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o -+obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o -+ -+ccflags-y += -DPRIVATE_MTK_SNAND_HEADER ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-def.h -@@ -0,0 +1,266 @@ -+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#ifndef _MTK_SNAND_DEF_H_ -+#define _MTK_SNAND_DEF_H_ -+ -+#include "mtk-snand-os.h" -+ -+#ifdef PRIVATE_MTK_SNAND_HEADER -+#include "mtk-snand.h" -+#else -+#include -+#endif -+ -+struct mtk_snand_plat_dev; -+ -+enum snand_flash_io { -+ SNAND_IO_1_1_1, -+ SNAND_IO_1_1_2, -+ SNAND_IO_1_2_2, -+ SNAND_IO_1_1_4, -+ SNAND_IO_1_4_4, -+ -+ __SNAND_IO_MAX -+}; -+ -+#define SPI_IO_1_1_1 BIT(SNAND_IO_1_1_1) -+#define SPI_IO_1_1_2 BIT(SNAND_IO_1_1_2) -+#define SPI_IO_1_2_2 BIT(SNAND_IO_1_2_2) -+#define SPI_IO_1_1_4 BIT(SNAND_IO_1_1_4) -+#define SPI_IO_1_4_4 BIT(SNAND_IO_1_4_4) -+ -+struct snand_opcode { -+ uint8_t opcode; -+ uint8_t dummy; -+}; -+ -+struct snand_io_cap { -+ uint8_t caps; -+ struct snand_opcode opcodes[__SNAND_IO_MAX]; -+}; -+ -+#define SNAND_OP(_io, _opcode, _dummy) [_io] = { .opcode = (_opcode), \ -+ .dummy = (_dummy) } -+ -+#define SNAND_IO_CAP(_name, _caps, ...) \ -+ struct snand_io_cap _name = { .caps = (_caps), \ -+ .opcodes = { __VA_ARGS__ } } -+ -+#define SNAND_MAX_ID_LEN 4 -+ -+enum snand_id_type { -+ SNAND_ID_DYMMY, -+ SNAND_ID_ADDR = SNAND_ID_DYMMY, -+ SNAND_ID_DIRECT, -+ -+ __SNAND_ID_TYPE_MAX -+}; -+ -+struct snand_id { -+ uint8_t type; /* enum snand_id_type */ -+ uint8_t len; -+ uint8_t id[SNAND_MAX_ID_LEN]; -+}; -+ -+#define SNAND_ID(_type, ...) \ -+ { .type = (_type), .id = { __VA_ARGS__ }, \ -+ .len = sizeof((uint8_t[]) { __VA_ARGS__ }) } -+ -+struct snand_mem_org { -+ uint16_t pagesize; -+ uint16_t sparesize; -+ uint16_t pages_per_block; -+ uint16_t blocks_per_die; -+ uint16_t planes_per_die; -+ uint16_t ndies; -+}; -+ -+#define SNAND_MEMORG(_ps, _ss, _ppb, _bpd, _ppd, _nd) \ -+ { .pagesize = (_ps), .sparesize = (_ss), .pages_per_block = (_ppb), \ -+ .blocks_per_die = (_bpd), .planes_per_die = (_ppd), .ndies = (_nd) } -+ -+typedef int (*snand_select_die_t)(struct mtk_snand *snf, uint32_t dieidx); -+ -+struct snand_flash_info { -+ const char *model; -+ struct snand_id id; -+ const struct snand_mem_org memorg; -+ const struct snand_io_cap *cap_rd; -+ const struct snand_io_cap *cap_pl; -+ snand_select_die_t select_die; -+}; -+ -+#define SNAND_INFO(_model, _id, _memorg, _cap_rd, _cap_pl, ...) \ -+ { .model = (_model), .id = _id, .memorg = _memorg, \ -+ .cap_rd = (_cap_rd), .cap_pl = (_cap_pl), __VA_ARGS__ } -+ -+const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, -+ const uint8_t *id); -+ -+struct mtk_snand_soc_data { -+ uint16_t sector_size; -+ uint16_t max_sectors; -+ uint16_t fdm_size; -+ uint16_t fdm_ecc_size; -+ uint16_t fifo_size; -+ -+ bool bbm_swap; -+ bool empty_page_check; -+ uint32_t mastersta_mask; -+ -+ const uint8_t *spare_sizes; -+ uint32_t num_spare_size; -+}; -+ -+enum mtk_ecc_regs { -+ ECC_DECDONE, -+}; -+ -+struct mtk_ecc_soc_data { -+ const uint8_t *ecc_caps; -+ uint32_t num_ecc_cap; -+ const uint32_t *regs; -+ uint16_t mode_shift; -+ uint8_t errnum_bits; -+ uint8_t errnum_shift; -+}; -+ -+struct mtk_snand { -+ struct mtk_snand_plat_dev *pdev; -+ -+ void __iomem *nfi_base; -+ void __iomem *ecc_base; -+ -+ enum mtk_snand_soc soc; -+ const struct mtk_snand_soc_data *nfi_soc; -+ const struct mtk_ecc_soc_data *ecc_soc; -+ bool snfi_quad_spi; -+ bool quad_spi_op; -+ -+ const char *model; -+ uint64_t size; -+ uint64_t die_size; -+ uint32_t erasesize; -+ uint32_t writesize; -+ uint32_t oobsize; -+ -+ uint32_t num_dies; -+ snand_select_die_t select_die; -+ -+ uint8_t opcode_rfc; -+ uint8_t opcode_pl; -+ uint8_t dummy_rfc; -+ uint8_t mode_rfc; -+ uint8_t mode_pl; -+ -+ uint32_t writesize_mask; -+ uint32_t writesize_shift; -+ uint32_t erasesize_mask; -+ uint32_t erasesize_shift; -+ uint64_t die_mask; -+ uint32_t die_shift; -+ -+ uint32_t spare_per_sector; -+ uint32_t raw_sector_size; -+ uint32_t ecc_strength; -+ uint32_t ecc_steps; -+ uint32_t ecc_bytes; -+ uint32_t ecc_parity_bits; -+ -+ uint8_t *page_cache; /* Used by read/write page */ -+ uint8_t *buf_cache; /* Used by block bad/markbad & auto_oob */ -+}; -+ -+enum mtk_snand_log_category { -+ SNAND_LOG_NFI, -+ SNAND_LOG_SNFI, -+ SNAND_LOG_ECC, -+ SNAND_LOG_CHIP, -+ -+ __SNAND_LOG_CAT_MAX -+}; -+ -+int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, -+ uint32_t msg_size); -+int mtk_snand_ecc_encoder_start(struct mtk_snand *snf); -+void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf); -+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_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, -+ uint8_t *in, uint32_t inlen); -+int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val); -+ -+int mtk_snand_log(struct mtk_snand_plat_dev *pdev, -+ enum mtk_snand_log_category cat, const char *fmt, ...); -+ -+#define snand_log_nfi(pdev, fmt, ...) \ -+ mtk_snand_log(pdev, SNAND_LOG_NFI, fmt, ##__VA_ARGS__) -+ -+#define snand_log_snfi(pdev, fmt, ...) \ -+ mtk_snand_log(pdev, SNAND_LOG_SNFI, fmt, ##__VA_ARGS__) -+ -+#define snand_log_ecc(pdev, fmt, ...) \ -+ mtk_snand_log(pdev, SNAND_LOG_ECC, fmt, ##__VA_ARGS__) -+ -+#define snand_log_chip(pdev, fmt, ...) \ -+ mtk_snand_log(pdev, SNAND_LOG_CHIP, fmt, ##__VA_ARGS__) -+ -+/* ffs64 */ -+static inline int mtk_snand_ffs64(uint64_t x) -+{ -+ if (!x) -+ return 0; -+ -+ if (!(x & 0xffffffff)) -+ return ffs((uint32_t)(x >> 32)) + 32; -+ -+ return ffs((uint32_t)(x & 0xffffffff)); -+} -+ -+/* NFI dummy commands */ -+#define NFI_CMD_DUMMY_READ 0x00 -+#define NFI_CMD_DUMMY_WRITE 0x80 -+ -+/* SPI-NAND opcodes */ -+#define SNAND_CMD_RESET 0xff -+#define SNAND_CMD_BLOCK_ERASE 0xd8 -+#define SNAND_CMD_READ_FROM_CACHE_QUAD 0xeb -+#define SNAND_CMD_WINBOND_SELECT_DIE 0xc2 -+#define SNAND_CMD_READ_FROM_CACHE_DUAL 0xbb -+#define SNAND_CMD_READID 0x9f -+#define SNAND_CMD_READ_FROM_CACHE_X4 0x6b -+#define SNAND_CMD_READ_FROM_CACHE_X2 0x3b -+#define SNAND_CMD_PROGRAM_LOAD_X4 0x32 -+#define SNAND_CMD_SET_FEATURE 0x1f -+#define SNAND_CMD_READ_TO_CACHE 0x13 -+#define SNAND_CMD_PROGRAM_EXECUTE 0x10 -+#define SNAND_CMD_GET_FEATURE 0x0f -+#define SNAND_CMD_READ_FROM_CACHE 0x0b -+#define SNAND_CMD_WRITE_ENABLE 0x06 -+#define SNAND_CMD_PROGRAM_LOAD 0x02 -+ -+/* SPI-NAND feature addresses */ -+#define SNAND_FEATURE_MICRON_DIE_ADDR 0xd0 -+#define SNAND_MICRON_DIE_SEL_1 BIT(6) -+ -+#define SNAND_FEATURE_STATUS_ADDR 0xc0 -+#define SNAND_STATUS_OIP BIT(0) -+#define SNAND_STATUS_WEL BIT(1) -+#define SNAND_STATUS_ERASE_FAIL BIT(2) -+#define SNAND_STATUS_PROGRAM_FAIL BIT(3) -+ -+#define SNAND_FEATURE_CONFIG_ADDR 0xb0 -+#define SNAND_FEATURE_QUAD_ENABLE BIT(0) -+#define SNAND_FEATURE_ECC_EN BIT(4) -+ -+#define SNAND_FEATURE_PROTECT_ADDR 0xa0 -+ -+#endif /* _MTK_SNAND_DEF_H_ */ ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-ecc.c -@@ -0,0 +1,264 @@ -+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include "mtk-snand-def.h" -+ -+/* ECC registers */ -+#define ECC_ENCCON 0x000 -+#define ENC_EN BIT(0) -+ -+#define ECC_ENCCNFG 0x004 -+#define ENC_MS_S 16 -+#define ENC_BURST_EN BIT(8) -+#define ENC_TNUM_S 0 -+ -+#define ECC_ENCIDLE 0x00c -+#define ENC_IDLE BIT(0) -+ -+#define ECC_DECCON 0x100 -+#define DEC_EN BIT(0) -+ -+#define ECC_DECCNFG 0x104 -+#define DEC_EMPTY_EN BIT(31) -+#define DEC_CS_S 16 -+#define DEC_CON_S 12 -+#define DEC_CON_CORRECT 3 -+#define DEC_BURST_EN BIT(8) -+#define DEC_TNUM_S 0 -+ -+#define ECC_DECIDLE 0x10c -+#define DEC_IDLE BIT(0) -+ -+#define ECC_DECENUM0 0x114 -+#define ECC_DECENUM(n) (ECC_DECENUM0 + (n) * 4) -+ -+/* ECC_ENCIDLE & ECC_DECIDLE */ -+#define ECC_IDLE BIT(0) -+ -+/* ENC_MODE & DEC_MODE */ -+#define ECC_MODE_NFI 1 -+ -+#define ECC_TIMEOUT 500000 -+ -+static const uint8_t mt7622_ecc_caps[] = { 4, 6, 8, 10, 12 }; -+ -+static const uint8_t mt7986_ecc_caps[] = { -+ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 -+}; -+ -+static const uint32_t mt7622_ecc_regs[] = { -+ [ECC_DECDONE] = 0x11c, -+}; -+ -+static const uint32_t mt7986_ecc_regs[] = { -+ [ECC_DECDONE] = 0x124, -+}; -+ -+static const struct mtk_ecc_soc_data mtk_ecc_socs[__SNAND_SOC_MAX] = { -+ [SNAND_SOC_MT7622] = { -+ .ecc_caps = mt7622_ecc_caps, -+ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), -+ .regs = mt7622_ecc_regs, -+ .mode_shift = 4, -+ .errnum_bits = 5, -+ .errnum_shift = 5, -+ }, -+ [SNAND_SOC_MT7629] = { -+ .ecc_caps = mt7622_ecc_caps, -+ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), -+ .regs = mt7622_ecc_regs, -+ .mode_shift = 4, -+ .errnum_bits = 5, -+ .errnum_shift = 5, -+ }, -+ [SNAND_SOC_MT7986] = { -+ .ecc_caps = mt7986_ecc_caps, -+ .num_ecc_cap = ARRAY_SIZE(mt7986_ecc_caps), -+ .regs = mt7986_ecc_regs, -+ .mode_shift = 5, -+ .errnum_bits = 5, -+ .errnum_shift = 8, -+ }, -+}; -+ -+static inline uint32_t ecc_read32(struct mtk_snand *snf, uint32_t reg) -+{ -+ return readl(snf->ecc_base + reg); -+} -+ -+static inline void ecc_write32(struct mtk_snand *snf, uint32_t reg, -+ uint32_t val) -+{ -+ writel(val, snf->ecc_base + reg); -+} -+ -+static inline void ecc_write16(struct mtk_snand *snf, uint32_t reg, -+ uint16_t val) -+{ -+ writew(val, snf->ecc_base + reg); -+} -+ -+static int mtk_ecc_poll(struct mtk_snand *snf, uint32_t reg, uint32_t bits) -+{ -+ uint32_t val; -+ -+ return read16_poll_timeout(snf->ecc_base + reg, val, (val & bits), 0, -+ ECC_TIMEOUT); -+} -+ -+static int mtk_ecc_wait_idle(struct mtk_snand *snf, uint32_t reg) -+{ -+ int ret; -+ -+ ret = mtk_ecc_poll(snf, reg, ECC_IDLE); -+ if (ret) { -+ snand_log_ecc(snf->pdev, "ECC engine is busy\n"); -+ return -EBUSY; -+ } -+ -+ return 0; -+} -+ -+int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, -+ uint32_t msg_size) -+{ -+ uint32_t i, val, ecc_msg_bits, ecc_strength; -+ int ret; -+ -+ snf->ecc_soc = &mtk_ecc_socs[snf->soc]; -+ -+ snf->ecc_parity_bits = fls(1 + 8 * msg_size); -+ ecc_strength = max_ecc_bytes * 8 / snf->ecc_parity_bits; -+ -+ for (i = snf->ecc_soc->num_ecc_cap - 1; i >= 0; i--) { -+ if (snf->ecc_soc->ecc_caps[i] <= ecc_strength) -+ break; -+ } -+ -+ if (unlikely(i < 0)) { -+ snand_log_ecc(snf->pdev, "Page size %u+%u is not supported\n", -+ snf->writesize, snf->oobsize); -+ return -ENOTSUPP; -+ } -+ -+ snf->ecc_strength = snf->ecc_soc->ecc_caps[i]; -+ snf->ecc_bytes = DIV_ROUND_UP(snf->ecc_strength * snf->ecc_parity_bits, -+ 8); -+ -+ /* Encoder config */ -+ ecc_write16(snf, ECC_ENCCON, 0); -+ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); -+ if (ret) -+ return ret; -+ -+ ecc_msg_bits = msg_size * 8; -+ val = (ecc_msg_bits << ENC_MS_S) | -+ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; -+ ecc_write32(snf, ECC_ENCCNFG, val); -+ -+ /* Decoder config */ -+ ecc_write16(snf, ECC_DECCON, 0); -+ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); -+ if (ret) -+ return ret; -+ -+ ecc_msg_bits += snf->ecc_strength * snf->ecc_parity_bits; -+ val = DEC_EMPTY_EN | (ecc_msg_bits << DEC_CS_S) | -+ (DEC_CON_CORRECT << DEC_CON_S) | -+ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; -+ ecc_write32(snf, ECC_DECCNFG, val); -+ -+ return 0; -+} -+ -+int mtk_snand_ecc_encoder_start(struct mtk_snand *snf) -+{ -+ int ret; -+ -+ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); -+ if (ret) { -+ ecc_write16(snf, ECC_ENCCON, 0); -+ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); -+ } -+ -+ ecc_write16(snf, ECC_ENCCON, ENC_EN); -+ -+ return 0; -+} -+ -+void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf) -+{ -+ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); -+ ecc_write16(snf, ECC_ENCCON, 0); -+} -+ -+int mtk_snand_ecc_decoder_start(struct mtk_snand *snf) -+{ -+ int ret; -+ -+ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); -+ if (ret) { -+ ecc_write16(snf, ECC_DECCON, 0); -+ mtk_ecc_wait_idle(snf, ECC_DECIDLE); -+ } -+ -+ ecc_write16(snf, ECC_DECCON, DEC_EN); -+ -+ return 0; -+} -+ -+void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf) -+{ -+ mtk_ecc_wait_idle(snf, ECC_DECIDLE); -+ ecc_write16(snf, ECC_DECCON, 0); -+} -+ -+int mtk_ecc_wait_decoder_done(struct mtk_snand *snf) -+{ -+ uint16_t val, step_mask = (1 << snf->ecc_steps) - 1; -+ uint32_t reg = snf->ecc_soc->regs[ECC_DECDONE]; -+ int ret; -+ -+ ret = read16_poll_timeout(snf->ecc_base + reg, val, -+ (val & step_mask) == step_mask, 0, -+ ECC_TIMEOUT); -+ if (ret) -+ snand_log_ecc(snf->pdev, "ECC decoder is busy\n"); -+ -+ return ret; -+} -+ -+int mtk_ecc_check_decode_error(struct mtk_snand *snf, uint32_t page) -+{ -+ uint32_t i, regi, fi, errnum; -+ uint32_t errnum_shift = snf->ecc_soc->errnum_shift; -+ uint32_t errnum_mask = (1 << snf->ecc_soc->errnum_bits) - 1; -+ int ret = 0; -+ -+ for (i = 0; i < snf->ecc_steps; i++) { -+ regi = i / 4; -+ fi = i % 4; -+ -+ 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; -+ } -+ -+ snand_log_ecc(snf->pdev, -+ "Uncorrectable bitflips in page %u sect %u\n", -+ page, i); -+ ret = -EBADMSG; -+ } -+ -+ return ret; -+} ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-ids.c -@@ -0,0 +1,511 @@ -+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include "mtk-snand-def.h" -+ -+static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx); -+static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx); -+ -+#define SNAND_MEMORG_512M_2K_64 SNAND_MEMORG(2048, 64, 64, 512, 1, 1) -+#define SNAND_MEMORG_1G_2K_64 SNAND_MEMORG(2048, 64, 64, 1024, 1, 1) -+#define SNAND_MEMORG_2G_2K_64 SNAND_MEMORG(2048, 64, 64, 2048, 1, 1) -+#define SNAND_MEMORG_2G_2K_120 SNAND_MEMORG(2048, 120, 64, 2048, 1, 1) -+#define SNAND_MEMORG_4G_2K_64 SNAND_MEMORG(2048, 64, 64, 4096, 1, 1) -+#define SNAND_MEMORG_1G_2K_120 SNAND_MEMORG(2048, 120, 64, 1024, 1, 1) -+#define SNAND_MEMORG_1G_2K_128 SNAND_MEMORG(2048, 128, 64, 1024, 1, 1) -+#define SNAND_MEMORG_2G_2K_128 SNAND_MEMORG(2048, 128, 64, 2048, 1, 1) -+#define SNAND_MEMORG_4G_2K_128 SNAND_MEMORG(2048, 128, 64, 4096, 1, 1) -+#define SNAND_MEMORG_4G_4K_240 SNAND_MEMORG(4096, 240, 64, 2048, 1, 1) -+#define SNAND_MEMORG_4G_4K_256 SNAND_MEMORG(4096, 256, 64, 2048, 1, 1) -+#define SNAND_MEMORG_8G_4K_256 SNAND_MEMORG(4096, 256, 64, 4096, 1, 1) -+#define SNAND_MEMORG_2G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 2048, 2, 1) -+#define SNAND_MEMORG_2G_2K_64_2D SNAND_MEMORG(2048, 64, 64, 1024, 1, 2) -+#define SNAND_MEMORG_2G_2K_128_2P SNAND_MEMORG(2048, 128, 64, 2048, 2, 1) -+#define SNAND_MEMORG_4G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 4096, 2, 1) -+#define SNAND_MEMORG_4G_2K_128_2P_2D SNAND_MEMORG(2048, 128, 64, 2048, 2, 2) -+#define SNAND_MEMORG_8G_4K_256_2D SNAND_MEMORG(4096, 256, 64, 2048, 1, 2) -+ -+static const SNAND_IO_CAP(snand_cap_read_from_cache_quad, -+ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | -+ SPI_IO_1_4_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), -+ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), -+ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), -+ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 4)); -+ -+static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_q2d, -+ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | -+ SPI_IO_1_4_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), -+ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), -+ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), -+ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 2)); -+ -+static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_a8d, -+ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | -+ SPI_IO_1_4_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), -+ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), -+ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 8), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), -+ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 8)); -+ -+static const SNAND_IO_CAP(snand_cap_read_from_cache_x4, -+ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_1_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), -+ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); -+ -+static const SNAND_IO_CAP(snand_cap_read_from_cache_x4_only, -+ SPI_IO_1_1_1 | SPI_IO_1_1_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); -+ -+static const SNAND_IO_CAP(snand_cap_program_load_x1, -+ SPI_IO_1_1_1, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0)); -+ -+static const SNAND_IO_CAP(snand_cap_program_load_x4, -+ SPI_IO_1_1_1 | SPI_IO_1_1_4, -+ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0), -+ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_PROGRAM_LOAD_X4, 0)); -+ -+static const struct snand_flash_info snand_flash_ids[] = { -+ SNAND_INFO("W25N512GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x20), -+ SNAND_MEMORG_512M_2K_64, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("W25N01GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x21), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("W25M02GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xab, 0x21), -+ SNAND_MEMORG_2G_2K_64_2D, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4, -+ mtk_snand_winbond_select_die), -+ SNAND_INFO("W25N02KV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x22), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("GD5F1GQ4UAWxx", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x10), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F1GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd1), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F1GQ4UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd9), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F1GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf1), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F2GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd2), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F2GQ5UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x32), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_a8d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F2GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf2), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F4GQ4UBxIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd4), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F4GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf4), -+ SNAND_MEMORG_4G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F2GQ5UExxG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x52), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("GD5F4GQ4UCxIG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0xb4), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("MX35LF1GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x12), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF1G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x14), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX31LF1GE4BC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x1e), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF2GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x22), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF2G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x24), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF2GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x26), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF2G14AC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x20), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF4G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x35), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MX35LF4GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x37), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("MT29F1G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x12), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("MT29F1G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x14), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MT29F2G01AAAED", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x9f), -+ SNAND_MEMORG_2G_2K_64_2P, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("MT29F2G01ABAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x24), -+ SNAND_MEMORG_2G_2K_128_2P, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MT29F4G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x32), -+ SNAND_MEMORG_4G_2K_64_2P, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("MT29F4G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x34), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("MT29F4G01ADAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x36), -+ SNAND_MEMORG_4G_2K_128_2P_2D, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4, -+ mtk_snand_micron_select_die), -+ SNAND_INFO("MT29F8G01ADAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x46), -+ SNAND_MEMORG_8G_4K_256_2D, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4, -+ mtk_snand_micron_select_die), -+ -+ SNAND_INFO("TC58CVG0S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xc2), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("TC58CVG1S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcb), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("TC58CVG2S0HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcd), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x1), -+ SNAND_INFO("TC58CVG0S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe2), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("TC58CVG1S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xeb), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("TC58CVG2S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xed), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("TH58CVG3S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe4), -+ SNAND_MEMORG_8G_4K_256, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("F50L512M41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x20), -+ SNAND_MEMORG_512M_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("F50L1G41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("F50L1G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x01), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("F50L2G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x0a), -+ SNAND_MEMORG_2G_2K_64_2D, -+ &snand_cap_read_from_cache_quad, -+ &snand_cap_program_load_x4, -+ mtk_snand_winbond_select_die), -+ -+ SNAND_INFO("CS11G0T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x00), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G0G0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x10), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G0S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x20), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G1T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x01), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G1S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x21), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G2T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x02), -+ SNAND_MEMORG_4G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("CS11G2S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x22), -+ SNAND_MEMORG_4G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("EM73B044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x01), -+ SNAND_MEMORG_512M_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x11), -+ SNAND_MEMORG_1G_2K_120, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x09), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x18), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x19), -+ SNAND_MEMORG(2048, 64, 128, 512, 1, 1), -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1c), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1e), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044VCC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x22), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044VCF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x25), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x31), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0a), -+ SNAND_MEMORG_2G_2K_120, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x12), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x10), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x13), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x14), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x17), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCH", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1b), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1f), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x20), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCL", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2e), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x32), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x03), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0b), -+ SNAND_MEMORG_4G_4K_240, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x23), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2c), -+ SNAND_MEMORG_4G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2f), -+ SNAND_MEMORG_4G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73F044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x24), -+ SNAND_MEMORG_8G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73F044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2d), -+ SNAND_MEMORG_8G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73E044SNE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0e), -+ SNAND_MEMORG_8G_4K_256, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73C044SNG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0c), -+ SNAND_MEMORG_1G_2K_120, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("EM73D044VCN", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0f), -+ SNAND_MEMORG_2G_2K_64, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("FM35Q1GA", SNAND_ID(SNAND_ID_DYMMY, 0xe5, 0x71), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("PN26G01A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe1), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("PN26G02A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe2), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("IS37SML01G1", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("ATO25D1GA", SNAND_ID(SNAND_ID_DYMMY, 0x9b, 0x12), -+ SNAND_MEMORG_1G_2K_64, -+ &snand_cap_read_from_cache_x4_only, -+ &snand_cap_program_load_x4), -+ -+ SNAND_INFO("HYF1GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x51), -+ SNAND_MEMORG_1G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+ SNAND_INFO("HYF2GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x52), -+ SNAND_MEMORG_2G_2K_128, -+ &snand_cap_read_from_cache_quad_q2d, -+ &snand_cap_program_load_x4), -+}; -+ -+static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx) -+{ -+ uint8_t op[2]; -+ -+ if (dieidx > 1) { -+ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); -+ return -EINVAL; -+ } -+ -+ op[0] = SNAND_CMD_WINBOND_SELECT_DIE; -+ op[1] = (uint8_t)dieidx; -+ -+ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); -+} -+ -+static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx) -+{ -+ int ret; -+ -+ if (dieidx > 1) { -+ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); -+ return -EINVAL; -+ } -+ -+ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_MICRON_DIE_ADDR, -+ SNAND_MICRON_DIE_SEL_1); -+ if (ret) { -+ snand_log_chip(snf->pdev, -+ "Failed to set die selection feature\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, -+ const uint8_t *id) -+{ -+ const struct snand_id *fid; -+ uint32_t i; -+ -+ for (i = 0; i < ARRAY_SIZE(snand_flash_ids); i++) { -+ if (snand_flash_ids[i].id.type != type) -+ continue; -+ -+ fid = &snand_flash_ids[i].id; -+ if (memcmp(fid->id, id, fid->len)) -+ continue; -+ -+ return &snand_flash_ids[i]; -+ } -+ -+ return NULL; -+} ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c -@@ -0,0 +1,524 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "mtk-snand.h" -+ -+struct mtk_snand_mtd { -+ struct udevice *dev; -+ struct mtk_snand *snf; -+ struct mtk_snand_chip_info cinfo; -+ uint8_t *page_cache; -+}; -+ -+static const char snand_mtd_name_prefix[] = "spi-nand"; -+ -+static u32 snandidx; -+ -+static inline struct mtk_snand_mtd *mtd_to_msm(struct mtd_info *mtd) -+{ -+ return mtd->priv; -+} -+ -+static int mtk_snand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ u64 start_addr, end_addr; -+ int ret; -+ -+ /* Do not allow write past end of device */ -+ if ((instr->addr + instr->len) > mtd->size) { -+ pr_debug("%s: attempt to erase beyond end of device\n", -+ __func__); -+ return -EINVAL; -+ } -+ -+ start_addr = instr->addr & (~mtd->erasesize_mask); -+ end_addr = instr->addr + instr->len; -+ if (end_addr & mtd->erasesize_mask) { -+ end_addr = (end_addr + mtd->erasesize_mask) & -+ (~mtd->erasesize_mask); -+ } -+ -+ instr->state = MTD_ERASING; -+ -+ while (start_addr < end_addr) { -+ WATCHDOG_RESET(); -+ -+ if (mtk_snand_block_isbad(msm->snf, start_addr)) { -+ if (!instr->scrub) { -+ instr->fail_addr = start_addr; -+ ret = -EIO; -+ break; -+ } -+ } -+ -+ ret = mtk_snand_erase_block(msm->snf, start_addr); -+ if (ret) { -+ instr->fail_addr = start_addr; -+ break; -+ } -+ -+ start_addr += mtd->erasesize; -+ } -+ -+ if (ret) -+ instr->state = MTD_ERASE_FAILED; -+ else -+ instr->state = MTD_ERASE_DONE; -+ -+ if (ret) -+ ret = -EIO; -+ -+ return ret; -+} -+ -+static int mtk_snand_mtd_read_data(struct mtk_snand_mtd *msm, uint64_t addr, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); -+ 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; -+ -+ col = addr & mtd->writesize_mask; -+ addr &= ~mtd->writesize_mask; -+ maxooblen = mtd_oobavail(mtd, ops); -+ ooboffs = ops->ooboffs; -+ ooblen = ops->ooblen; -+ len = ops->len; -+ -+ datcache = len ? msm->page_cache : NULL; -+ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; -+ -+ ops->oobretlen = 0; -+ ops->retlen = 0; -+ -+ while (len || ooblen) { -+ WATCHDOG_RESET(); -+ -+ if (ops->mode == MTD_OPS_AUTO_OOB) -+ ret = mtk_snand_read_page_auto_oob(msm->snf, addr, -+ datcache, oobcache, maxooblen, NULL, raw); -+ else -+ ret = mtk_snand_read_page(msm->snf, addr, datcache, -+ oobcache, raw); -+ -+ if (ret < 0) -+ return ret; -+ -+ if (len) { -+ /* Move data */ -+ chklen = mtd->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 += mtd->writesize; -+ } -+ -+ return 0; -+} -+ -+static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ 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: -+ case MTD_OPS_AUTO_OOB: -+ case MTD_OPS_RAW: -+ break; -+ default: -+ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); -+ return -EINVAL; -+ } -+ -+ 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 (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 mtk_snand_mtd_read_data(msm, from, ops); -+} -+ -+static int mtk_snand_mtd_write_data(struct mtk_snand_mtd *msm, uint64_t addr, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); -+ size_t len, ooblen, maxooblen, chklen, oobwrlen; -+ uint32_t col, ooboffs; -+ uint8_t *datcache, *oobcache; -+ bool raw = ops->mode == MTD_OPS_RAW ? true : false; -+ int ret; -+ -+ col = addr & mtd->writesize_mask; -+ addr &= ~mtd->writesize_mask; -+ maxooblen = mtd_oobavail(mtd, ops); -+ ooboffs = ops->ooboffs; -+ ooblen = ops->ooblen; -+ len = ops->len; -+ -+ datcache = len ? msm->page_cache : NULL; -+ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; -+ -+ ops->oobretlen = 0; -+ ops->retlen = 0; -+ -+ while (len || ooblen) { -+ WATCHDOG_RESET(); -+ -+ if (len) { -+ /* Move data */ -+ chklen = mtd->writesize - col; -+ if (chklen > len) -+ chklen = len; -+ -+ memset(datcache, 0xff, col); -+ memcpy(datcache + col, ops->datbuf + ops->retlen, -+ chklen); -+ memset(datcache + col + chklen, 0xff, -+ mtd->writesize - col - chklen); -+ len -= chklen; -+ col = 0; /* (col + chklen) % */ -+ ops->retlen += chklen; -+ } -+ -+ oobwrlen = 0; -+ 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, -+ mtd->oobsize - ooboffs - chklen); -+ oobwrlen = chklen + ooboffs; -+ ooblen -= chklen; -+ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ -+ ops->oobretlen += chklen; -+ } -+ -+ if (ops->mode == MTD_OPS_AUTO_OOB) -+ ret = mtk_snand_write_page_auto_oob(msm->snf, addr, -+ datcache, oobcache, oobwrlen, NULL, raw); -+ else -+ ret = mtk_snand_write_page(msm->snf, addr, datcache, -+ oobcache, raw); -+ -+ if (ret) -+ return ret; -+ -+ addr += mtd->writesize; -+ } -+ -+ return 0; -+} -+ -+static int mtk_snand_mtd_write_oob(struct mtd_info *mtd, loff_t to, -+ struct mtd_oob_ops *ops) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ 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: -+ case MTD_OPS_AUTO_OOB: -+ case MTD_OPS_RAW: -+ break; -+ default: -+ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); -+ return -EINVAL; -+ } -+ -+ 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 (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 mtk_snand_mtd_write_data(msm, to, ops); -+} -+ -+static int mtk_snand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, -+ size_t *retlen, u_char *buf) -+{ -+ struct mtd_oob_ops ops = { -+ .mode = MTD_OPS_PLACE_OOB, -+ .datbuf = buf, -+ .len = len, -+ }; -+ int ret; -+ -+ ret = mtk_snand_mtd_read_oob(mtd, from, &ops); -+ -+ if (retlen) -+ *retlen = ops.retlen; -+ -+ return ret; -+} -+ -+static int mtk_snand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, -+ size_t *retlen, const u_char *buf) -+{ -+ struct mtd_oob_ops ops = { -+ .mode = MTD_OPS_PLACE_OOB, -+ .datbuf = (void *)buf, -+ .len = len, -+ }; -+ int ret; -+ -+ ret = mtk_snand_mtd_write_oob(mtd, to, &ops); -+ -+ if (retlen) -+ *retlen = ops.retlen; -+ -+ return ret; -+} -+ -+static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ -+ return mtk_snand_block_isbad(msm->snf, offs); -+} -+ -+static int mtk_snand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ -+ return mtk_snand_block_markbad(msm->snf, offs); -+} -+ -+static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobecc) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ -+ if (section) -+ return -ERANGE; -+ -+ oobecc->offset = msm->cinfo.fdm_size * msm->cinfo.num_sectors; -+ oobecc->length = mtd->oobsize - oobecc->offset; -+ -+ return 0; -+} -+ -+static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section, -+ struct mtd_oob_region *oobfree) -+{ -+ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); -+ -+ if (section >= msm->cinfo.num_sectors) -+ return -ERANGE; -+ -+ oobfree->length = msm->cinfo.fdm_size - 1; -+ oobfree->offset = section * msm->cinfo.fdm_size + 1; -+ -+ return 0; -+} -+ -+static const struct mtd_ooblayout_ops mtk_snand_ooblayout = { -+ .ecc = mtk_snand_ooblayout_ecc, -+ .rfree = mtk_snand_ooblayout_free, -+}; -+ -+static int mtk_snand_mtd_probe(struct udevice *dev) -+{ -+ struct mtk_snand_mtd *msm = dev_get_priv(dev); -+ struct mtd_info *mtd = dev_get_uclass_priv(dev); -+ struct mtk_snand_platdata mtk_snand_pdata = {}; -+ size_t namelen; -+ fdt_addr_t base; -+ int ret; -+ -+ base = dev_read_addr_name(dev, "nfi"); -+ if (base == FDT_ADDR_T_NONE) -+ return -EINVAL; -+ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); -+ -+ base = dev_read_addr_name(dev, "ecc"); -+ if (base == FDT_ADDR_T_NONE) -+ return -EINVAL; -+ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); -+ -+ mtk_snand_pdata.soc = dev_get_driver_data(dev); -+ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); -+ -+ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &msm->snf); -+ if (ret) -+ return ret; -+ -+ mtk_snand_get_chip_info(msm->snf, &msm->cinfo); -+ -+ msm->page_cache = malloc(msm->cinfo.pagesize + msm->cinfo.sparesize); -+ if (!msm->page_cache) { -+ printf("%s: failed to allocate memory for page cache\n", -+ __func__); -+ ret = -ENOMEM; -+ goto errout1; -+ } -+ -+ namelen = sizeof(snand_mtd_name_prefix) + 12; -+ -+ mtd->name = malloc(namelen); -+ if (!mtd->name) { -+ printf("%s: failed to allocate memory for MTD name\n", -+ __func__); -+ ret = -ENOMEM; -+ goto errout2; -+ } -+ -+ msm->dev = dev; -+ -+ snprintf(mtd->name, namelen, "%s%u", snand_mtd_name_prefix, snandidx++); -+ -+ mtd->priv = msm; -+ mtd->dev = dev; -+ mtd->type = MTD_NANDFLASH; -+ mtd->flags = MTD_CAP_NANDFLASH; -+ -+ mtd->size = msm->cinfo.chipsize; -+ mtd->erasesize = msm->cinfo.blocksize; -+ mtd->writesize = msm->cinfo.pagesize; -+ mtd->writebufsize = mtd->writesize; -+ mtd->oobsize = msm->cinfo.sparesize; -+ mtd->oobavail = msm->cinfo.num_sectors * (msm->cinfo.fdm_size - 1); -+ -+ mtd->ooblayout = &mtk_snand_ooblayout; -+ -+ mtd->ecc_strength = msm->cinfo.ecc_strength * msm->cinfo.num_sectors; -+ mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4; -+ mtd->ecc_step_size = msm->cinfo.sector_size; -+ -+ mtd->_read = mtk_snand_mtd_read; -+ mtd->_write = mtk_snand_mtd_write; -+ mtd->_erase = mtk_snand_mtd_erase; -+ mtd->_read_oob = mtk_snand_mtd_read_oob; -+ mtd->_write_oob = mtk_snand_mtd_write_oob; -+ mtd->_block_isbad = mtk_snand_mtd_block_isbad; -+ mtd->_block_markbad = mtk_snand_mtd_block_markbad; -+ -+ ret = add_mtd_device(mtd); -+ if (ret) { -+ printf("%s: failed to add SPI-NAND MTD device\n", __func__); -+ ret = -ENODEV; -+ goto errout3; -+ } -+ -+ printf("SPI-NAND: %s (%lluMB)\n", msm->cinfo.model, -+ msm->cinfo.chipsize >> 20); -+ -+ return 0; -+ -+errout3: -+ free(mtd->name); -+ -+errout2: -+ free(msm->page_cache); -+ -+errout1: -+ mtk_snand_cleanup(msm->snf); -+ -+ return ret; -+} -+ -+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,mt7986-snand", .data = SNAND_SOC_MT7986 }, -+ { /* sentinel */ }, -+}; -+ -+U_BOOT_DRIVER(spinand) = { -+ .name = "mtk-snand", -+ .id = UCLASS_MTD, -+ .of_match = mtk_snand_ids, -+ .priv_auto = sizeof(struct mtk_snand_mtd), -+ .probe = mtk_snand_mtd_probe, -+}; ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-os.c -@@ -0,0 +1,39 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include "mtk-snand-def.h" -+ -+int mtk_snand_log(struct mtk_snand_plat_dev *pdev, -+ enum mtk_snand_log_category cat, const char *fmt, ...) -+{ -+ const char *catname = ""; -+ va_list ap; -+ int ret; -+ -+ switch (cat) { -+ case SNAND_LOG_NFI: -+ catname = "NFI: "; -+ break; -+ case SNAND_LOG_SNFI: -+ catname = "SNFI: "; -+ break; -+ case SNAND_LOG_ECC: -+ catname = "ECC: "; -+ break; -+ default: -+ break; -+ } -+ -+ puts("SPI-NAND: "); -+ puts(catname); -+ -+ va_start(ap, fmt); -+ ret = vprintf(fmt, ap); -+ va_end(ap); -+ -+ return ret; -+} ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-os.h -@@ -0,0 +1,120 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#ifndef _MTK_SNAND_OS_H_ -+#define _MTK_SNAND_OS_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifndef ARCH_DMA_MINALIGN -+#define ARCH_DMA_MINALIGN 64 -+#endif -+ -+struct mtk_snand_plat_dev { -+ ulong unused; -+}; -+ -+/* Polling helpers */ -+#define read16_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ -+ readw_poll_timeout((addr), (val), (cond), (timeout_us)) -+ -+#define read32_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ -+ readl_poll_timeout((addr), (val), (cond), (timeout_us)) -+ -+/* Timer helpers */ -+typedef uint64_t mtk_snand_time_t; -+ -+static inline mtk_snand_time_t timer_get_ticks(void) -+{ -+ return get_ticks(); -+} -+ -+static inline mtk_snand_time_t timer_time_to_tick(uint32_t timeout_us) -+{ -+ return usec_to_tick(timeout_us); -+} -+ -+static inline bool timer_is_timeout(mtk_snand_time_t start_tick, -+ mtk_snand_time_t timeout_tick) -+{ -+ return get_ticks() - start_tick > timeout_tick; -+} -+ -+/* Memory helpers */ -+static inline void *generic_mem_alloc(struct mtk_snand_plat_dev *pdev, -+ size_t size) -+{ -+ return calloc(1, size); -+} -+ -+static inline void generic_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) -+{ -+ free(ptr); -+} -+ -+static inline void *dma_mem_alloc(struct mtk_snand_plat_dev *pdev, size_t size) -+{ -+ return memalign(ARCH_DMA_MINALIGN, size); -+} -+ -+static inline void dma_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) -+{ -+ free(ptr); -+} -+ -+static inline int dma_mem_map(struct mtk_snand_plat_dev *pdev, void *vaddr, -+ uintptr_t *dma_addr, size_t size, bool to_device) -+{ -+ size_t cachelen = roundup(size, ARCH_DMA_MINALIGN); -+ uintptr_t endaddr = (uintptr_t)vaddr + cachelen; -+ -+ if (to_device) -+ flush_dcache_range((uintptr_t)vaddr, endaddr); -+ else -+ invalidate_dcache_range((uintptr_t)vaddr, endaddr); -+ -+ *dma_addr = (uintptr_t)vaddr; -+ -+ return 0; -+} -+ -+static inline void dma_mem_unmap(struct mtk_snand_plat_dev *pdev, -+ uintptr_t dma_addr, size_t size, -+ bool to_device) -+{ -+} -+ -+/* Interrupt helpers */ -+static inline void irq_completion_done(struct mtk_snand_plat_dev *pdev) -+{ -+} -+ -+static inline void irq_completion_init(struct mtk_snand_plat_dev *pdev) -+{ -+} -+ -+static inline int irq_completion_wait(struct mtk_snand_plat_dev *pdev, -+ void __iomem *reg, uint32_t bit, -+ uint32_t timeout_us) -+{ -+ uint32_t val; -+ -+ return read32_poll_timeout(reg, val, val & bit, 0, timeout_us); -+} -+ -+#endif /* _MTK_SNAND_OS_H_ */ ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand.c -@@ -0,0 +1,1776 @@ -+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include "mtk-snand-def.h" -+ -+/* NFI registers */ -+#define NFI_CNFG 0x000 -+#define CNFG_OP_MODE_S 12 -+#define CNFG_OP_MODE_CUST 6 -+#define CNFG_OP_MODE_PROGRAM 3 -+#define CNFG_AUTO_FMT_EN BIT(9) -+#define CNFG_HW_ECC_EN BIT(8) -+#define CNFG_DMA_BURST_EN BIT(2) -+#define CNFG_READ_MODE BIT(1) -+#define CNFG_DMA_MODE BIT(0) -+ -+#define NFI_PAGEFMT 0x0004 -+#define NFI_SPARE_SIZE_LS_S 16 -+#define NFI_FDM_ECC_NUM_S 12 -+#define NFI_FDM_NUM_S 8 -+#define NFI_SPARE_SIZE_S 4 -+#define NFI_SEC_SEL_512 BIT(2) -+#define NFI_PAGE_SIZE_S 0 -+#define NFI_PAGE_SIZE_512_2K 0 -+#define NFI_PAGE_SIZE_2K_4K 1 -+#define NFI_PAGE_SIZE_4K_8K 2 -+#define NFI_PAGE_SIZE_8K_16K 3 -+ -+#define NFI_CON 0x008 -+#define CON_SEC_NUM_S 12 -+#define CON_BWR BIT(9) -+#define CON_BRD BIT(8) -+#define CON_NFI_RST BIT(1) -+#define CON_FIFO_FLUSH BIT(0) -+ -+#define NFI_INTR_EN 0x010 -+#define NFI_INTR_STA 0x014 -+#define NFI_IRQ_INTR_EN BIT(31) -+#define NFI_IRQ_CUS_READ BIT(8) -+#define NFI_IRQ_CUS_PG BIT(7) -+ -+#define NFI_CMD 0x020 -+ -+#define NFI_STRDATA 0x040 -+#define STR_DATA BIT(0) -+ -+#define NFI_STA 0x060 -+#define NFI_NAND_FSM GENMASK(28, 24) -+#define NFI_FSM GENMASK(19, 16) -+#define READ_EMPTY BIT(12) -+ -+#define NFI_FIFOSTA 0x064 -+#define FIFO_WR_REMAIN_S 8 -+#define FIFO_RD_REMAIN_S 0 -+ -+#define NFI_STRADDR 0x080 -+ -+#define NFI_FDM0L 0x0a0 -+#define NFI_FDM0M 0x0a4 -+#define NFI_FDML(n) (NFI_FDM0L + (n) * 8) -+#define NFI_FDMM(n) (NFI_FDM0M + (n) * 8) -+ -+#define NFI_DEBUG_CON1 0x220 -+#define WBUF_EN BIT(2) -+ -+#define NFI_MASTERSTA 0x224 -+#define MAS_ADDR GENMASK(11, 9) -+#define MAS_RD GENMASK(8, 6) -+#define MAS_WR GENMASK(5, 3) -+#define MAS_RDDLY GENMASK(2, 0) -+#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_7986 (AHB_BUS_BUSY | BUS_BUSY) -+ -+/* SNFI registers */ -+#define SNF_MAC_CTL 0x500 -+#define MAC_XIO_SEL BIT(4) -+#define SF_MAC_EN BIT(3) -+#define SF_TRIG BIT(2) -+#define WIP_READY BIT(1) -+#define WIP BIT(0) -+ -+#define SNF_MAC_OUTL 0x504 -+#define SNF_MAC_INL 0x508 -+ -+#define SNF_RD_CTL2 0x510 -+#define DATA_READ_DUMMY_S 8 -+#define DATA_READ_CMD_S 0 -+ -+#define SNF_RD_CTL3 0x514 -+ -+#define SNF_PG_CTL1 0x524 -+#define PG_LOAD_CMD_S 8 -+ -+#define SNF_PG_CTL2 0x528 -+ -+#define SNF_MISC_CTL 0x538 -+#define SW_RST BIT(28) -+#define FIFO_RD_LTC_S 25 -+#define PG_LOAD_X4_EN BIT(20) -+#define DATA_READ_MODE_S 16 -+#define DATA_READ_MODE GENMASK(18, 16) -+#define DATA_READ_MODE_X1 0 -+#define DATA_READ_MODE_X2 1 -+#define DATA_READ_MODE_X4 2 -+#define DATA_READ_MODE_DUAL 5 -+#define DATA_READ_MODE_QUAD 6 -+#define PG_LOAD_CUSTOM_EN BIT(7) -+#define DATARD_CUSTOM_EN BIT(6) -+#define CS_DESELECT_CYC_S 0 -+ -+#define SNF_MISC_CTL2 0x53c -+#define PROGRAM_LOAD_BYTE_NUM_S 16 -+#define READ_DATA_BYTE_NUM_S 11 -+ -+#define SNF_DLY_CTL3 0x548 -+#define SFCK_SAM_DLY_S 0 -+ -+#define SNF_STA_CTL1 0x550 -+#define CUS_PG_DONE BIT(28) -+#define CUS_READ_DONE BIT(27) -+#define SPI_STATE_S 0 -+#define SPI_STATE GENMASK(3, 0) -+ -+#define SNF_CFG 0x55c -+#define SPI_MODE BIT(0) -+ -+#define SNF_GPRAM 0x800 -+#define SNF_GPRAM_SIZE 0xa0 -+ -+#define SNFI_POLL_INTERVAL 1000000 -+ -+static const uint8_t mt7622_spare_sizes[] = { 16, 26, 27, 28 }; -+ -+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 -+}; -+ -+static const struct mtk_snand_soc_data mtk_snand_socs[__SNAND_SOC_MAX] = { -+ [SNAND_SOC_MT7622] = { -+ .sector_size = 512, -+ .max_sectors = 8, -+ .fdm_size = 8, -+ .fdm_ecc_size = 1, -+ .fifo_size = 32, -+ .bbm_swap = false, -+ .empty_page_check = false, -+ .mastersta_mask = NFI_MASTERSTA_MASK_7622, -+ .spare_sizes = mt7622_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) -+ }, -+ [SNAND_SOC_MT7629] = { -+ .sector_size = 512, -+ .max_sectors = 8, -+ .fdm_size = 8, -+ .fdm_ecc_size = 1, -+ .fifo_size = 32, -+ .bbm_swap = true, -+ .empty_page_check = false, -+ .mastersta_mask = NFI_MASTERSTA_MASK_7622, -+ .spare_sizes = mt7622_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes) -+ }, -+ [SNAND_SOC_MT7986] = { -+ .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_7986, -+ .spare_sizes = mt7986_spare_sizes, -+ .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes) -+ }, -+}; -+ -+static inline uint32_t nfi_read32(struct mtk_snand *snf, uint32_t reg) -+{ -+ return readl(snf->nfi_base + reg); -+} -+ -+static inline void nfi_write32(struct mtk_snand *snf, uint32_t reg, -+ uint32_t val) -+{ -+ writel(val, snf->nfi_base + reg); -+} -+ -+static inline void nfi_write16(struct mtk_snand *snf, uint32_t reg, -+ uint16_t val) -+{ -+ writew(val, snf->nfi_base + reg); -+} -+ -+static inline void nfi_rmw32(struct mtk_snand *snf, uint32_t reg, uint32_t clr, -+ uint32_t set) -+{ -+ uint32_t val; -+ -+ val = readl(snf->nfi_base + reg); -+ val &= ~clr; -+ val |= set; -+ writel(val, snf->nfi_base + reg); -+} -+ -+static void nfi_write_data(struct mtk_snand *snf, uint32_t reg, -+ const uint8_t *data, uint32_t len) -+{ -+ uint32_t i, val = 0, es = sizeof(uint32_t); -+ -+ for (i = reg; i < reg + len; i++) { -+ val |= ((uint32_t)*data++) << (8 * (i % es)); -+ -+ if (i % es == es - 1 || i == reg + len - 1) { -+ nfi_write32(snf, i & ~(es - 1), val); -+ val = 0; -+ } -+ } -+} -+ -+static void nfi_read_data(struct mtk_snand *snf, uint32_t reg, uint8_t *data, -+ uint32_t len) -+{ -+ uint32_t i, val = 0, es = sizeof(uint32_t); -+ -+ for (i = reg; i < reg + len; i++) { -+ if (i == reg || i % es == 0) -+ val = nfi_read32(snf, i & ~(es - 1)); -+ -+ *data++ = (uint8_t)(val >> (8 * (i % es))); -+ } -+} -+ -+static inline void do_bm_swap(uint8_t *bm1, uint8_t *bm2) -+{ -+ uint8_t tmp = *bm1; -+ *bm1 = *bm2; -+ *bm2 = tmp; -+} -+ -+static void mtk_snand_bm_swap_raw(struct mtk_snand *snf) -+{ -+ uint32_t fdm_bbm_pos; -+ -+ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) -+ return; -+ -+ fdm_bbm_pos = (snf->ecc_steps - 1) * snf->raw_sector_size + -+ snf->nfi_soc->sector_size; -+ do_bm_swap(&snf->page_cache[fdm_bbm_pos], -+ &snf->page_cache[snf->writesize]); -+} -+ -+static void mtk_snand_bm_swap(struct mtk_snand *snf) -+{ -+ uint32_t buf_bbm_pos, fdm_bbm_pos; -+ -+ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) -+ return; -+ -+ buf_bbm_pos = snf->writesize - -+ (snf->ecc_steps - 1) * snf->spare_per_sector; -+ fdm_bbm_pos = snf->writesize + -+ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; -+ do_bm_swap(&snf->page_cache[fdm_bbm_pos], -+ &snf->page_cache[buf_bbm_pos]); -+} -+ -+static void mtk_snand_fdm_bm_swap_raw(struct mtk_snand *snf) -+{ -+ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; -+ -+ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) -+ return; -+ -+ fdm_bbm_pos1 = snf->nfi_soc->sector_size; -+ fdm_bbm_pos2 = (snf->ecc_steps - 1) * snf->raw_sector_size + -+ snf->nfi_soc->sector_size; -+ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], -+ &snf->page_cache[fdm_bbm_pos2]); -+} -+ -+static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf) -+{ -+ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; -+ -+ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) -+ return; -+ -+ fdm_bbm_pos1 = snf->writesize; -+ fdm_bbm_pos2 = snf->writesize + -+ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; -+ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], -+ &snf->page_cache[fdm_bbm_pos2]); -+} -+ -+static int mtk_nfi_reset(struct mtk_snand *snf) -+{ -+ uint32_t val, fifo_mask; -+ int ret; -+ -+ nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); -+ -+ ret = read16_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, -+ "NFI master is still busy after reset\n"); -+ return ret; -+ } -+ -+ ret = read32_poll_timeout(snf->nfi_base + NFI_STA, val, -+ !(val & (NFI_FSM | NFI_NAND_FSM)), 0, -+ SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_nfi(snf->pdev, "Failed to reset NFI\n"); -+ return ret; -+ } -+ -+ fifo_mask = ((snf->nfi_soc->fifo_size - 1) << FIFO_RD_REMAIN_S) | -+ ((snf->nfi_soc->fifo_size - 1) << FIFO_WR_REMAIN_S); -+ ret = read16_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val, -+ !(val & fifo_mask), 0, SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_nfi(snf->pdev, "NFI FIFOs are not empty\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int mtk_snand_mac_reset(struct mtk_snand *snf) -+{ -+ int ret; -+ uint32_t val; -+ -+ nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST); -+ -+ ret = read32_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val, -+ !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL); -+ if (ret) -+ 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)); -+ -+ return ret; -+} -+ -+static int mtk_snand_mac_trigger(struct mtk_snand *snf, uint32_t outlen, -+ uint32_t inlen) -+{ -+ int ret; -+ uint32_t val; -+ -+ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN); -+ nfi_write32(snf, SNF_MAC_OUTL, outlen); -+ nfi_write32(snf, SNF_MAC_INL, inlen); -+ -+ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG); -+ -+ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, -+ val & WIP_READY, 0, SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_snfi(snf->pdev, "Timed out waiting for WIP_READY\n"); -+ goto cleanup; -+ } -+ -+ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, -+ !(val & WIP), 0, SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_snfi(snf->pdev, -+ "Timed out waiting for WIP cleared\n"); -+ } -+ -+cleanup: -+ nfi_write32(snf, SNF_MAC_CTL, 0); -+ -+ return ret; -+} -+ -+int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, -+ uint8_t *in, uint32_t inlen) -+{ -+ int ret; -+ -+ if (outlen + inlen > SNF_GPRAM_SIZE) -+ return -EINVAL; -+ -+ mtk_snand_mac_reset(snf); -+ -+ nfi_write_data(snf, SNF_GPRAM, out, outlen); -+ -+ ret = mtk_snand_mac_trigger(snf, outlen, inlen); -+ if (ret) -+ return ret; -+ -+ if (!inlen) -+ return 0; -+ -+ nfi_read_data(snf, SNF_GPRAM + outlen, in, inlen); -+ -+ return 0; -+} -+ -+static int mtk_snand_get_feature(struct mtk_snand *snf, uint32_t addr) -+{ -+ uint8_t op[2], val; -+ int ret; -+ -+ op[0] = SNAND_CMD_GET_FEATURE; -+ op[1] = (uint8_t)addr; -+ -+ ret = mtk_snand_mac_io(snf, op, sizeof(op), &val, 1); -+ if (ret) -+ return ret; -+ -+ return val; -+} -+ -+int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val) -+{ -+ uint8_t op[3]; -+ -+ op[0] = SNAND_CMD_SET_FEATURE; -+ op[1] = (uint8_t)addr; -+ op[2] = (uint8_t)val; -+ -+ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); -+} -+ -+static int mtk_snand_poll_status(struct mtk_snand *snf, uint32_t wait_us) -+{ -+ int val; -+ mtk_snand_time_t time_start, tmo; -+ -+ time_start = timer_get_ticks(); -+ tmo = timer_time_to_tick(wait_us); -+ -+ do { -+ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); -+ if (!(val & SNAND_STATUS_OIP)) -+ return val & (SNAND_STATUS_ERASE_FAIL | -+ SNAND_STATUS_PROGRAM_FAIL); -+ } while (!timer_is_timeout(time_start, tmo)); -+ -+ return -ETIMEDOUT; -+} -+ -+int mtk_snand_chip_reset(struct mtk_snand *snf) -+{ -+ uint8_t op = SNAND_CMD_RESET; -+ int ret; -+ -+ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static int mtk_snand_config_feature(struct mtk_snand *snf, uint8_t clr, -+ uint8_t set) -+{ -+ int val, newval; -+ int ret; -+ -+ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); -+ if (val < 0) { -+ snand_log_chip(snf->pdev, -+ "Failed to get configuration feature\n"); -+ return val; -+ } -+ -+ newval = (val & (~clr)) | set; -+ -+ if (newval == val) -+ return 0; -+ -+ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_CONFIG_ADDR, -+ (uint8_t)newval); -+ if (val < 0) { -+ snand_log_chip(snf->pdev, -+ "Failed to set configuration feature\n"); -+ return ret; -+ } -+ -+ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); -+ if (val < 0) { -+ snand_log_chip(snf->pdev, -+ "Failed to get configuration feature\n"); -+ return val; -+ } -+ -+ if (newval != val) -+ return -ENOTSUPP; -+ -+ return 0; -+} -+ -+static int mtk_snand_ondie_ecc_control(struct mtk_snand *snf, bool enable) -+{ -+ int ret; -+ -+ if (enable) -+ ret = mtk_snand_config_feature(snf, 0, SNAND_FEATURE_ECC_EN); -+ else -+ ret = mtk_snand_config_feature(snf, SNAND_FEATURE_ECC_EN, 0); -+ -+ if (ret) { -+ snand_log_chip(snf->pdev, "Failed to %s On-Die ECC engine\n", -+ enable ? "enable" : "disable"); -+ } -+ -+ return ret; -+} -+ -+static int mtk_snand_qspi_control(struct mtk_snand *snf, bool enable) -+{ -+ int ret; -+ -+ if (enable) { -+ ret = mtk_snand_config_feature(snf, 0, -+ SNAND_FEATURE_QUAD_ENABLE); -+ } else { -+ ret = mtk_snand_config_feature(snf, -+ SNAND_FEATURE_QUAD_ENABLE, 0); -+ } -+ -+ if (ret) { -+ snand_log_chip(snf->pdev, "Failed to %s quad spi\n", -+ enable ? "enable" : "disable"); -+ } -+ -+ return ret; -+} -+ -+static int mtk_snand_unlock(struct mtk_snand *snf) -+{ -+ int ret; -+ -+ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_PROTECT_ADDR, 0); -+ if (ret) { -+ snand_log_chip(snf->pdev, "Failed to set protection feature\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int mtk_snand_write_enable(struct mtk_snand *snf) -+{ -+ uint8_t op = SNAND_CMD_WRITE_ENABLE; -+ int ret, val; -+ -+ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); -+ if (ret) -+ return ret; -+ -+ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); -+ if (val < 0) -+ return ret; -+ -+ if (val & SNAND_STATUS_WEL) -+ return 0; -+ -+ snand_log_chip(snf->pdev, "Failed to send write-enable command\n"); -+ -+ return -ENOTSUPP; -+} -+ -+static int mtk_snand_select_die(struct mtk_snand *snf, uint32_t dieidx) -+{ -+ if (!snf->select_die) -+ return 0; -+ -+ return snf->select_die(snf, dieidx); -+} -+ -+static uint64_t mtk_snand_select_die_address(struct mtk_snand *snf, -+ uint64_t addr) -+{ -+ uint32_t dieidx; -+ -+ if (!snf->select_die) -+ return addr; -+ -+ dieidx = addr >> snf->die_shift; -+ -+ mtk_snand_select_die(snf, dieidx); -+ -+ return addr & snf->die_mask; -+} -+ -+static uint32_t mtk_snand_get_plane_address(struct mtk_snand *snf, -+ uint32_t page) -+{ -+ uint32_t pages_per_block; -+ -+ pages_per_block = 1 << (snf->erasesize_shift - snf->writesize_shift); -+ -+ if (page & pages_per_block) -+ return 1 << (snf->writesize_shift + 1); -+ -+ return 0; -+} -+ -+static int mtk_snand_page_op(struct mtk_snand *snf, uint32_t page, uint8_t cmd) -+{ -+ uint8_t op[4]; -+ -+ op[0] = cmd; -+ op[1] = (page >> 16) & 0xff; -+ op[2] = (page >> 8) & 0xff; -+ op[3] = page & 0xff; -+ -+ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); -+} -+ -+static void mtk_snand_read_fdm(struct mtk_snand *snf, uint8_t *buf) -+{ -+ uint32_t vall, valm; -+ uint8_t *oobptr = buf; -+ int i, j; -+ -+ for (i = 0; i < snf->ecc_steps; i++) { -+ vall = nfi_read32(snf, NFI_FDML(i)); -+ valm = nfi_read32(snf, NFI_FDMM(i)); -+ -+ for (j = 0; j < snf->nfi_soc->fdm_size; j++) -+ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); -+ -+ oobptr += snf->nfi_soc->fdm_size; -+ } -+} -+ -+static int mtk_snand_read_cache(struct mtk_snand *snf, uint32_t page, bool raw) -+{ -+ uint32_t coladdr, rwbytes, mode, len; -+ uintptr_t dma_addr; -+ int ret; -+ -+ /* Column address with plane bit */ -+ coladdr = mtk_snand_get_plane_address(snf, page); -+ -+ mtk_snand_mac_reset(snf); -+ mtk_nfi_reset(snf); -+ -+ /* Command and dummy cycles */ -+ nfi_write32(snf, SNF_RD_CTL2, -+ ((uint32_t)snf->dummy_rfc << DATA_READ_DUMMY_S) | -+ (snf->opcode_rfc << DATA_READ_CMD_S)); -+ -+ /* Column address */ -+ nfi_write32(snf, SNF_RD_CTL3, coladdr); -+ -+ /* 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); -+ -+ /* Set bytes to read */ -+ rwbytes = snf->ecc_steps * snf->raw_sector_size; -+ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | -+ rwbytes); -+ -+ /* NFI read prepare */ -+ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; -+ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) | -+ CNFG_DMA_BURST_EN | CNFG_READ_MODE | CNFG_DMA_MODE | mode); -+ -+ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); -+ -+ /* Prepare for DMA read */ -+ len = snf->writesize + snf->oobsize; -+ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, false); -+ if (ret) { -+ snand_log_nfi(snf->pdev, -+ "DMA map from device failed with %d\n", ret); -+ return ret; -+ } -+ -+ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); -+ -+ if (!raw) -+ mtk_snand_ecc_decoder_start(snf); -+ -+ /* Prepare for custom read interrupt */ -+ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ); -+ irq_completion_init(snf->pdev); -+ -+ /* Trigger NFI into custom mode */ -+ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ); -+ -+ /* Start DMA read */ -+ nfi_rmw32(snf, NFI_CON, 0, CON_BRD); -+ nfi_write16(snf, NFI_STRDATA, STR_DATA); -+ -+ /* Wait for operation finished */ -+ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, -+ CUS_READ_DONE, SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_nfi(snf->pdev, -+ "DMA timed out for reading from cache\n"); -+ goto cleanup; -+ } -+ -+ if (!raw) { -+ ret = mtk_ecc_wait_decoder_done(snf); -+ if (ret) -+ goto cleanup; -+ -+ 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_snand_ecc_decoder_stop(snf); -+ } -+ -+cleanup: -+ /* DMA cleanup */ -+ dma_mem_unmap(snf->pdev, dma_addr, len, false); -+ -+ /* Stop read */ -+ nfi_write32(snf, NFI_CON, 0); -+ -+ /* Clear SNF done flag */ -+ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE); -+ nfi_write32(snf, SNF_STA_CTL1, 0); -+ -+ /* Disable interrupt */ -+ nfi_read32(snf, NFI_INTR_STA); -+ nfi_write32(snf, NFI_INTR_EN, 0); -+ -+ nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN, 0); -+ -+ return ret; -+} -+ -+static void mtk_snand_from_raw_page(struct mtk_snand *snf, void *buf, void *oob) -+{ -+ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; -+ uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; -+ uint8_t *bufptr = buf, *oobptr = oob, *raw_sector; -+ -+ for (i = 0; i < snf->ecc_steps; i++) { -+ raw_sector = snf->page_cache + i * snf->raw_sector_size; -+ -+ if (buf) { -+ memcpy(bufptr, raw_sector, snf->nfi_soc->sector_size); -+ bufptr += snf->nfi_soc->sector_size; -+ } -+ -+ raw_sector += snf->nfi_soc->sector_size; -+ -+ if (oob) { -+ memcpy(oobptr, raw_sector, snf->nfi_soc->fdm_size); -+ oobptr += snf->nfi_soc->fdm_size; -+ raw_sector += snf->nfi_soc->fdm_size; -+ -+ memcpy(eccptr, raw_sector, ecc_bytes); -+ eccptr += ecc_bytes; -+ } -+ } -+} -+ -+static int mtk_snand_do_read_page(struct mtk_snand *snf, uint64_t addr, -+ void *buf, void *oob, bool raw, bool format) -+{ -+ uint64_t die_addr; -+ uint32_t page; -+ int ret; -+ -+ die_addr = mtk_snand_select_die_address(snf, addr); -+ page = die_addr >> snf->writesize_shift; -+ -+ ret = mtk_snand_page_op(snf, page, SNAND_CMD_READ_TO_CACHE); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); -+ if (ret < 0) { -+ snand_log_chip(snf->pdev, "Read to cache command timed out\n"); -+ return ret; -+ } -+ -+ ret = mtk_snand_read_cache(snf, page, raw); -+ if (ret < 0 && ret != -EBADMSG) -+ return ret; -+ -+ if (raw) { -+ if (format) { -+ mtk_snand_bm_swap_raw(snf); -+ mtk_snand_fdm_bm_swap_raw(snf); -+ mtk_snand_from_raw_page(snf, buf, oob); -+ } else { -+ if (buf) -+ memcpy(buf, snf->page_cache, snf->writesize); -+ -+ if (oob) { -+ memset(oob, 0xff, snf->oobsize); -+ memcpy(oob, snf->page_cache + snf->writesize, -+ snf->ecc_steps * snf->spare_per_sector); -+ } -+ } -+ } else { -+ mtk_snand_bm_swap(snf); -+ mtk_snand_fdm_bm_swap(snf); -+ -+ if (buf) -+ memcpy(buf, snf->page_cache, snf->writesize); -+ -+ if (oob) { -+ memset(oob, 0xff, snf->oobsize); -+ memcpy(oob, snf->page_cache + snf->writesize, -+ snf->ecc_steps * snf->nfi_soc->fdm_size); -+ } -+ } -+ -+ return ret; -+} -+ -+int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, -+ void *oob, bool raw) -+{ -+ if (!snf || (!buf && !oob)) -+ return -EINVAL; -+ -+ if (addr >= snf->size) -+ return -EINVAL; -+ -+ return mtk_snand_do_read_page(snf, addr, buf, oob, raw, true); -+} -+ -+static void mtk_snand_write_fdm(struct mtk_snand *snf, const uint8_t *buf) -+{ -+ uint32_t vall, valm, fdm_size = snf->nfi_soc->fdm_size; -+ const uint8_t *oobptr = buf; -+ int i, j; -+ -+ for (i = 0; i < snf->ecc_steps; i++) { -+ vall = 0; -+ valm = 0; -+ -+ for (j = 0; j < 8; j++) { -+ if (j < 4) -+ vall |= (j < fdm_size ? oobptr[j] : 0xff) -+ << (j * 8); -+ else -+ valm |= (j < fdm_size ? oobptr[j] : 0xff) -+ << ((j - 4) * 8); -+ } -+ -+ nfi_write32(snf, NFI_FDML(i), vall); -+ nfi_write32(snf, NFI_FDMM(i), valm); -+ -+ oobptr += fdm_size; -+ } -+} -+ -+static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page, -+ bool raw) -+{ -+ uint32_t coladdr, rwbytes, mode, len; -+ uintptr_t dma_addr; -+ int ret; -+ -+ /* Column address with plane bit */ -+ coladdr = mtk_snand_get_plane_address(snf, page); -+ -+ mtk_snand_mac_reset(snf); -+ mtk_nfi_reset(snf); -+ -+ /* Write FDM registers if necessary */ -+ if (!raw) -+ mtk_snand_write_fdm(snf, snf->page_cache + snf->writesize); -+ -+ /* Command */ -+ nfi_write32(snf, SNF_PG_CTL1, (snf->opcode_pl << PG_LOAD_CMD_S)); -+ -+ /* Column address */ -+ nfi_write32(snf, SNF_PG_CTL2, coladdr); -+ -+ /* Set write mode */ -+ mode = snf->mode_pl ? PG_LOAD_X4_EN : 0; -+ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, mode | PG_LOAD_CUSTOM_EN); -+ -+ /* Set bytes to write */ -+ rwbytes = snf->ecc_steps * snf->raw_sector_size; -+ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | -+ rwbytes); -+ -+ /* NFI write prepare */ -+ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; -+ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) | -+ CNFG_DMA_BURST_EN | CNFG_DMA_MODE | mode); -+ -+ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); -+ -+ /* Prepare for DMA write */ -+ len = snf->writesize + snf->oobsize; -+ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, true); -+ if (ret) { -+ snand_log_nfi(snf->pdev, -+ "DMA map to device failed with %d\n", ret); -+ return ret; -+ } -+ -+ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); -+ -+ if (!raw) -+ mtk_snand_ecc_encoder_start(snf); -+ -+ /* Prepare for custom write interrupt */ -+ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG); -+ irq_completion_init(snf->pdev); -+ -+ /* Trigger NFI into custom mode */ -+ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE); -+ -+ /* Start DMA write */ -+ nfi_rmw32(snf, NFI_CON, 0, CON_BWR); -+ nfi_write16(snf, NFI_STRDATA, STR_DATA); -+ -+ /* Wait for operation finished */ -+ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, -+ CUS_PG_DONE, SNFI_POLL_INTERVAL); -+ if (ret) { -+ snand_log_nfi(snf->pdev, -+ "DMA timed out for program load\n"); -+ goto cleanup; -+ } -+ -+ if (!raw) -+ mtk_snand_ecc_encoder_stop(snf); -+ -+cleanup: -+ /* DMA cleanup */ -+ dma_mem_unmap(snf->pdev, dma_addr, len, true); -+ -+ /* Stop write */ -+ nfi_write16(snf, NFI_CON, 0); -+ -+ /* Clear SNF done flag */ -+ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE); -+ nfi_write32(snf, SNF_STA_CTL1, 0); -+ -+ /* Disable interrupt */ -+ nfi_read32(snf, NFI_INTR_STA); -+ nfi_write32(snf, NFI_INTR_EN, 0); -+ -+ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0); -+ -+ return ret; -+} -+ -+static void mtk_snand_to_raw_page(struct mtk_snand *snf, -+ const void *buf, const void *oob, -+ bool empty_ecc) -+{ -+ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; -+ const uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; -+ const uint8_t *bufptr = buf, *oobptr = oob; -+ uint8_t *raw_sector; -+ -+ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); -+ for (i = 0; i < snf->ecc_steps; i++) { -+ raw_sector = snf->page_cache + i * snf->raw_sector_size; -+ -+ if (buf) { -+ memcpy(raw_sector, bufptr, snf->nfi_soc->sector_size); -+ bufptr += snf->nfi_soc->sector_size; -+ } -+ -+ raw_sector += snf->nfi_soc->sector_size; -+ -+ if (oob) { -+ memcpy(raw_sector, oobptr, snf->nfi_soc->fdm_size); -+ oobptr += snf->nfi_soc->fdm_size; -+ raw_sector += snf->nfi_soc->fdm_size; -+ -+ if (empty_ecc) -+ memset(raw_sector, 0xff, ecc_bytes); -+ else -+ memcpy(raw_sector, eccptr, ecc_bytes); -+ eccptr += ecc_bytes; -+ } -+ } -+} -+ -+static bool mtk_snand_is_empty_page(struct mtk_snand *snf, const void *buf, -+ const void *oob) -+{ -+ const uint8_t *p = buf; -+ uint32_t i, j; -+ -+ if (buf) { -+ for (i = 0; i < snf->writesize; i++) { -+ if (p[i] != 0xff) -+ return false; -+ } -+ } -+ -+ if (oob) { -+ for (j = 0; j < snf->ecc_steps; j++) { -+ p = oob + j * snf->nfi_soc->fdm_size; -+ -+ for (i = 0; i < snf->nfi_soc->fdm_ecc_size; i++) { -+ if (p[i] != 0xff) -+ return false; -+ } -+ } -+ } -+ -+ return true; -+} -+ -+static int mtk_snand_do_write_page(struct mtk_snand *snf, uint64_t addr, -+ const void *buf, const void *oob, -+ bool raw, bool format) -+{ -+ uint64_t die_addr; -+ bool empty_ecc = false; -+ uint32_t page; -+ int ret; -+ -+ die_addr = mtk_snand_select_die_address(snf, addr); -+ page = die_addr >> snf->writesize_shift; -+ -+ if (!raw && mtk_snand_is_empty_page(snf, buf, oob)) { -+ /* -+ * If the data in the page to be ecc-ed is full 0xff, -+ * change to raw write mode -+ */ -+ raw = true; -+ format = true; -+ -+ /* fill ecc parity code region with 0xff */ -+ empty_ecc = true; -+ } -+ -+ if (raw) { -+ if (format) { -+ mtk_snand_to_raw_page(snf, buf, oob, empty_ecc); -+ mtk_snand_fdm_bm_swap_raw(snf); -+ mtk_snand_bm_swap_raw(snf); -+ } else { -+ memset(snf->page_cache, 0xff, -+ snf->writesize + snf->oobsize); -+ -+ if (buf) -+ memcpy(snf->page_cache, buf, snf->writesize); -+ -+ if (oob) { -+ memcpy(snf->page_cache + snf->writesize, oob, -+ snf->ecc_steps * snf->spare_per_sector); -+ } -+ } -+ } else { -+ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); -+ if (buf) -+ memcpy(snf->page_cache, buf, snf->writesize); -+ -+ if (oob) { -+ memcpy(snf->page_cache + snf->writesize, oob, -+ snf->ecc_steps * snf->nfi_soc->fdm_size); -+ } -+ -+ mtk_snand_fdm_bm_swap(snf); -+ mtk_snand_bm_swap(snf); -+ } -+ -+ ret = mtk_snand_write_enable(snf); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_program_load(snf, page, raw); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_page_op(snf, page, SNAND_CMD_PROGRAM_EXECUTE); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); -+ if (ret < 0) { -+ snand_log_chip(snf->pdev, -+ "Page program command timed out on page %u\n", -+ page); -+ return ret; -+ } -+ -+ if (ret & SNAND_STATUS_PROGRAM_FAIL) { -+ snand_log_chip(snf->pdev, -+ "Page program failed on page %u\n", page); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, -+ const void *oob, bool raw) -+{ -+ if (!snf || (!buf && !oob)) -+ return -EINVAL; -+ -+ if (addr >= snf->size) -+ return -EINVAL; -+ -+ return mtk_snand_do_write_page(snf, addr, buf, oob, raw, true); -+} -+ -+int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr) -+{ -+ uint64_t die_addr; -+ uint32_t page, block; -+ int ret; -+ -+ if (!snf) -+ return -EINVAL; -+ -+ if (addr >= snf->size) -+ return -EINVAL; -+ -+ die_addr = mtk_snand_select_die_address(snf, addr); -+ block = die_addr >> snf->erasesize_shift; -+ page = block << (snf->erasesize_shift - snf->writesize_shift); -+ -+ ret = mtk_snand_write_enable(snf); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_page_op(snf, page, SNAND_CMD_BLOCK_ERASE); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); -+ if (ret < 0) { -+ snand_log_chip(snf->pdev, -+ "Block erase command timed out on block %u\n", -+ block); -+ return ret; -+ } -+ -+ if (ret & SNAND_STATUS_ERASE_FAIL) { -+ snand_log_chip(snf->pdev, -+ "Block erase failed on block %u\n", block); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int mtk_snand_block_isbad_std(struct mtk_snand *snf, uint64_t addr) -+{ -+ int ret; -+ -+ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, -+ false); -+ if (ret && ret != -EBADMSG) -+ return ret; -+ -+ return snf->buf_cache[0] != 0xff; -+} -+ -+static int mtk_snand_block_isbad_mtk(struct mtk_snand *snf, uint64_t addr) -+{ -+ int ret; -+ -+ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, -+ true); -+ if (ret && ret != -EBADMSG) -+ return ret; -+ -+ return snf->buf_cache[0] != 0xff; -+} -+ -+int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr) -+{ -+ if (!snf) -+ return -EINVAL; -+ -+ if (addr >= snf->size) -+ return -EINVAL; -+ -+ addr &= ~snf->erasesize_mask; -+ -+ if (snf->nfi_soc->bbm_swap) -+ return mtk_snand_block_isbad_std(snf, addr); -+ -+ return mtk_snand_block_isbad_mtk(snf, addr); -+} -+ -+static int mtk_snand_block_markbad_std(struct mtk_snand *snf, uint64_t addr) -+{ -+ /* Standard BBM position */ -+ memset(snf->buf_cache, 0xff, snf->oobsize); -+ snf->buf_cache[0] = 0; -+ -+ return mtk_snand_do_write_page(snf, addr, NULL, snf->buf_cache, true, -+ false); -+} -+ -+static int mtk_snand_block_markbad_mtk(struct mtk_snand *snf, uint64_t addr) -+{ -+ /* Write the whole page with zeros */ -+ memset(snf->buf_cache, 0, snf->writesize + snf->oobsize); -+ -+ return mtk_snand_do_write_page(snf, addr, snf->buf_cache, -+ snf->buf_cache + snf->writesize, true, -+ true); -+} -+ -+int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr) -+{ -+ if (!snf) -+ return -EINVAL; -+ -+ if (addr >= snf->size) -+ return -EINVAL; -+ -+ addr &= ~snf->erasesize_mask; -+ -+ if (snf->nfi_soc->bbm_swap) -+ return mtk_snand_block_markbad_std(snf, addr); -+ -+ return mtk_snand_block_markbad_mtk(snf, addr); -+} -+ -+int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, -+ const uint8_t *oobbuf, size_t ooblen) -+{ -+ size_t len = ooblen, sect_fdm_len; -+ const uint8_t *oob = oobbuf; -+ uint32_t step = 0; -+ -+ if (!snf || !oobraw || !oob) -+ return -EINVAL; -+ -+ while (len && step < snf->ecc_steps) { -+ sect_fdm_len = snf->nfi_soc->fdm_size - 1; -+ if (sect_fdm_len > len) -+ sect_fdm_len = len; -+ -+ memcpy(oobraw + step * snf->nfi_soc->fdm_size + 1, oob, -+ sect_fdm_len); -+ -+ len -= sect_fdm_len; -+ oob += sect_fdm_len; -+ step++; -+ } -+ -+ return len; -+} -+ -+int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, -+ size_t ooblen, const uint8_t *oobraw) -+{ -+ size_t len = ooblen, sect_fdm_len; -+ uint8_t *oob = oobbuf; -+ uint32_t step = 0; -+ -+ if (!snf || !oobraw || !oob) -+ return -EINVAL; -+ -+ while (len && step < snf->ecc_steps) { -+ sect_fdm_len = snf->nfi_soc->fdm_size - 1; -+ if (sect_fdm_len > len) -+ sect_fdm_len = len; -+ -+ memcpy(oob, oobraw + step * snf->nfi_soc->fdm_size + 1, -+ sect_fdm_len); -+ -+ len -= sect_fdm_len; -+ oob += sect_fdm_len; -+ step++; -+ } -+ -+ return len; -+} -+ -+int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, -+ void *buf, void *oob, size_t ooblen, -+ size_t *actualooblen, bool raw) -+{ -+ int ret, oobremain; -+ -+ if (!snf) -+ return -EINVAL; -+ -+ if (!oob) -+ return mtk_snand_read_page(snf, addr, buf, NULL, raw); -+ -+ ret = mtk_snand_read_page(snf, addr, buf, snf->buf_cache, raw); -+ if (ret && ret != -EBADMSG) { -+ if (actualooblen) -+ *actualooblen = 0; -+ return ret; -+ } -+ -+ oobremain = mtk_snand_transfer_oob(snf, oob, ooblen, snf->buf_cache); -+ if (actualooblen) -+ *actualooblen = ooblen - oobremain; -+ -+ return ret; -+} -+ -+int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, -+ const void *buf, const void *oob, -+ size_t ooblen, size_t *actualooblen, bool raw) -+{ -+ int oobremain; -+ -+ if (!snf) -+ return -EINVAL; -+ -+ if (!oob) -+ return mtk_snand_write_page(snf, addr, buf, NULL, raw); -+ -+ memset(snf->buf_cache, 0xff, snf->oobsize); -+ oobremain = mtk_snand_fill_oob(snf, snf->buf_cache, oob, ooblen); -+ if (actualooblen) -+ *actualooblen = ooblen - oobremain; -+ -+ return mtk_snand_write_page(snf, addr, buf, snf->buf_cache, raw); -+} -+ -+int mtk_snand_get_chip_info(struct mtk_snand *snf, -+ struct mtk_snand_chip_info *info) -+{ -+ if (!snf || !info) -+ return -EINVAL; -+ -+ info->model = snf->model; -+ info->chipsize = snf->size; -+ info->blocksize = snf->erasesize; -+ info->pagesize = snf->writesize; -+ info->sparesize = snf->oobsize; -+ info->spare_per_sector = snf->spare_per_sector; -+ info->fdm_size = snf->nfi_soc->fdm_size; -+ info->fdm_ecc_size = snf->nfi_soc->fdm_ecc_size; -+ info->num_sectors = snf->ecc_steps; -+ info->sector_size = snf->nfi_soc->sector_size; -+ info->ecc_strength = snf->ecc_strength; -+ info->ecc_bytes = snf->ecc_bytes; -+ -+ return 0; -+} -+ -+int mtk_snand_irq_process(struct mtk_snand *snf) -+{ -+ uint32_t sta, ien; -+ -+ if (!snf) -+ return -EINVAL; -+ -+ sta = nfi_read32(snf, NFI_INTR_STA); -+ ien = nfi_read32(snf, NFI_INTR_EN); -+ -+ if (!(sta & ien)) -+ return 0; -+ -+ nfi_write32(snf, NFI_INTR_EN, 0); -+ irq_completion_done(snf->pdev); -+ -+ return 1; -+} -+ -+static int mtk_snand_select_spare_per_sector(struct mtk_snand *snf) -+{ -+ uint32_t spare_per_step = snf->oobsize / snf->ecc_steps; -+ int i, mul = 1; -+ -+ /* -+ * If we're using the 1KB sector size, HW will automatically -+ * double the spare size. So we should only use half of the value. -+ */ -+ if (snf->nfi_soc->sector_size == 1024) -+ mul = 2; -+ -+ spare_per_step /= mul; -+ -+ for (i = snf->nfi_soc->num_spare_size - 1; i >= 0; i--) { -+ if (snf->nfi_soc->spare_sizes[i] <= spare_per_step) { -+ snf->spare_per_sector = snf->nfi_soc->spare_sizes[i]; -+ snf->spare_per_sector *= mul; -+ return i; -+ } -+ } -+ -+ snand_log_nfi(snf->pdev, -+ "Page size %u+%u is not supported\n", snf->writesize, -+ snf->oobsize); -+ -+ return -1; -+} -+ -+static int mtk_snand_pagefmt_setup(struct mtk_snand *snf) -+{ -+ uint32_t spare_size_idx, spare_size_shift, pagesize_idx; -+ uint32_t sector_size_512; -+ -+ if (snf->nfi_soc->sector_size == 512) { -+ sector_size_512 = NFI_SEC_SEL_512; -+ spare_size_shift = NFI_SPARE_SIZE_S; -+ } else { -+ sector_size_512 = 0; -+ spare_size_shift = NFI_SPARE_SIZE_LS_S; -+ } -+ -+ switch (snf->writesize) { -+ case SZ_512: -+ pagesize_idx = NFI_PAGE_SIZE_512_2K; -+ break; -+ case SZ_2K: -+ if (snf->nfi_soc->sector_size == 512) -+ pagesize_idx = NFI_PAGE_SIZE_2K_4K; -+ else -+ pagesize_idx = NFI_PAGE_SIZE_512_2K; -+ break; -+ case SZ_4K: -+ if (snf->nfi_soc->sector_size == 512) -+ pagesize_idx = NFI_PAGE_SIZE_4K_8K; -+ else -+ pagesize_idx = NFI_PAGE_SIZE_2K_4K; -+ break; -+ case SZ_8K: -+ if (snf->nfi_soc->sector_size == 512) -+ pagesize_idx = NFI_PAGE_SIZE_8K_16K; -+ else -+ pagesize_idx = NFI_PAGE_SIZE_4K_8K; -+ break; -+ case SZ_16K: -+ pagesize_idx = NFI_PAGE_SIZE_8K_16K; -+ break; -+ default: -+ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", -+ snf->writesize); -+ return -ENOTSUPP; -+ } -+ -+ spare_size_idx = mtk_snand_select_spare_per_sector(snf); -+ if (unlikely(spare_size_idx < 0)) -+ return -ENOTSUPP; -+ -+ snf->raw_sector_size = snf->nfi_soc->sector_size + -+ snf->spare_per_sector; -+ -+ /* Setup page format */ -+ nfi_write32(snf, NFI_PAGEFMT, -+ (snf->nfi_soc->fdm_ecc_size << NFI_FDM_ECC_NUM_S) | -+ (snf->nfi_soc->fdm_size << NFI_FDM_NUM_S) | -+ (spare_size_idx << spare_size_shift) | -+ (pagesize_idx << NFI_PAGE_SIZE_S) | -+ sector_size_512); -+ -+ return 0; -+} -+ -+static enum snand_flash_io mtk_snand_select_opcode(struct mtk_snand *snf, -+ uint32_t snfi_caps, uint8_t *opcode, -+ uint8_t *dummy, -+ const struct snand_io_cap *op_cap) -+{ -+ uint32_t i, caps; -+ -+ caps = snfi_caps & op_cap->caps; -+ -+ i = fls(caps); -+ if (i > 0) { -+ *opcode = op_cap->opcodes[i - 1].opcode; -+ if (dummy) -+ *dummy = op_cap->opcodes[i - 1].dummy; -+ return i - 1; -+ } -+ -+ return __SNAND_IO_MAX; -+} -+ -+static int mtk_snand_select_opcode_rfc(struct mtk_snand *snf, -+ uint32_t snfi_caps, -+ const struct snand_io_cap *op_cap) -+{ -+ enum snand_flash_io idx; -+ -+ static const uint8_t rfc_modes[__SNAND_IO_MAX] = { -+ [SNAND_IO_1_1_1] = DATA_READ_MODE_X1, -+ [SNAND_IO_1_1_2] = DATA_READ_MODE_X2, -+ [SNAND_IO_1_2_2] = DATA_READ_MODE_DUAL, -+ [SNAND_IO_1_1_4] = DATA_READ_MODE_X4, -+ [SNAND_IO_1_4_4] = DATA_READ_MODE_QUAD, -+ }; -+ -+ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_rfc, -+ &snf->dummy_rfc, op_cap); -+ if (idx >= __SNAND_IO_MAX) { -+ snand_log_snfi(snf->pdev, -+ "No capable opcode for read from cache\n"); -+ return -ENOTSUPP; -+ } -+ -+ snf->mode_rfc = rfc_modes[idx]; -+ -+ if (idx == SNAND_IO_1_1_4 || idx == SNAND_IO_1_4_4) -+ snf->quad_spi_op = true; -+ -+ return 0; -+} -+ -+static int mtk_snand_select_opcode_pl(struct mtk_snand *snf, uint32_t snfi_caps, -+ const struct snand_io_cap *op_cap) -+{ -+ enum snand_flash_io idx; -+ -+ static const uint8_t pl_modes[__SNAND_IO_MAX] = { -+ [SNAND_IO_1_1_1] = 0, -+ [SNAND_IO_1_1_4] = 1, -+ }; -+ -+ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_pl, -+ NULL, op_cap); -+ if (idx >= __SNAND_IO_MAX) { -+ snand_log_snfi(snf->pdev, -+ "No capable opcode for program load\n"); -+ return -ENOTSUPP; -+ } -+ -+ snf->mode_pl = pl_modes[idx]; -+ -+ if (idx == SNAND_IO_1_1_4) -+ snf->quad_spi_op = true; -+ -+ return 0; -+} -+ -+static int mtk_snand_setup(struct mtk_snand *snf, -+ const struct snand_flash_info *snand_info) -+{ -+ const struct snand_mem_org *memorg = &snand_info->memorg; -+ uint32_t i, msg_size, snfi_caps; -+ int ret; -+ -+ /* Calculate flash memory organization */ -+ snf->model = snand_info->model; -+ snf->writesize = memorg->pagesize; -+ snf->oobsize = memorg->sparesize; -+ snf->erasesize = snf->writesize * memorg->pages_per_block; -+ snf->die_size = (uint64_t)snf->erasesize * memorg->blocks_per_die; -+ snf->size = snf->die_size * memorg->ndies; -+ snf->num_dies = memorg->ndies; -+ -+ snf->writesize_mask = snf->writesize - 1; -+ snf->erasesize_mask = snf->erasesize - 1; -+ snf->die_mask = snf->die_size - 1; -+ -+ snf->writesize_shift = ffs(snf->writesize) - 1; -+ snf->erasesize_shift = ffs(snf->erasesize) - 1; -+ snf->die_shift = mtk_snand_ffs64(snf->die_size) - 1; -+ -+ snf->select_die = snand_info->select_die; -+ -+ /* Determine opcodes for read from cache/program load */ -+ snfi_caps = SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2; -+ if (snf->snfi_quad_spi) -+ snfi_caps |= SPI_IO_1_1_4 | SPI_IO_1_4_4; -+ -+ ret = mtk_snand_select_opcode_rfc(snf, snfi_caps, snand_info->cap_rd); -+ if (ret) -+ return ret; -+ -+ ret = mtk_snand_select_opcode_pl(snf, snfi_caps, snand_info->cap_pl); -+ if (ret) -+ return ret; -+ -+ /* ECC and page format */ -+ snf->ecc_steps = snf->writesize / snf->nfi_soc->sector_size; -+ if (snf->ecc_steps > snf->nfi_soc->max_sectors) { -+ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", -+ snf->writesize); -+ return -ENOTSUPP; -+ } -+ -+ ret = mtk_snand_pagefmt_setup(snf); -+ if (ret) -+ return ret; -+ -+ msg_size = snf->nfi_soc->sector_size + snf->nfi_soc->fdm_ecc_size; -+ ret = mtk_ecc_setup(snf, snf->nfi_base + NFI_FDM0L, -+ snf->spare_per_sector - snf->nfi_soc->fdm_size, -+ msg_size); -+ if (ret) -+ return ret; -+ -+ nfi_write16(snf, NFI_CNFG, 0); -+ -+ /* Tuning options */ -+ nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN); -+ nfi_write32(snf, SNF_DLY_CTL3, (40 << SFCK_SAM_DLY_S)); -+ -+ /* Interrupts */ -+ nfi_read32(snf, NFI_INTR_STA); -+ nfi_write32(snf, NFI_INTR_EN, 0); -+ -+ /* Clear SNF done flag */ -+ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE | CUS_PG_DONE); -+ nfi_write32(snf, SNF_STA_CTL1, 0); -+ -+ /* Initialization on all dies */ -+ for (i = 0; i < snf->num_dies; i++) { -+ mtk_snand_select_die(snf, i); -+ -+ /* Disable On-Die ECC engine */ -+ ret = mtk_snand_ondie_ecc_control(snf, false); -+ if (ret) -+ return ret; -+ -+ /* Disable block protection */ -+ mtk_snand_unlock(snf); -+ -+ /* Enable/disable quad-spi */ -+ mtk_snand_qspi_control(snf, snf->quad_spi_op); -+ } -+ -+ mtk_snand_select_die(snf, 0); -+ -+ return 0; -+} -+ -+static int mtk_snand_id_probe(struct mtk_snand *snf, -+ const struct snand_flash_info **snand_info) -+{ -+ uint8_t id[4], op[2]; -+ int ret; -+ -+ /* Read SPI-NAND JEDEC ID, OP + dummy/addr + ID */ -+ op[0] = SNAND_CMD_READID; -+ op[1] = 0; -+ ret = mtk_snand_mac_io(snf, op, 2, id, sizeof(id)); -+ if (ret) -+ return ret; -+ -+ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); -+ if (*snand_info) -+ return 0; -+ -+ /* Read SPI-NAND JEDEC ID, OP + ID */ -+ op[0] = SNAND_CMD_READID; -+ ret = mtk_snand_mac_io(snf, op, 1, id, sizeof(id)); -+ if (ret) -+ return ret; -+ -+ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); -+ if (*snand_info) -+ return 0; -+ -+ snand_log_chip(snf->pdev, -+ "Unrecognized SPI-NAND ID: %02x %02x %02x %02x\n", -+ id[0], id[1], id[2], id[3]); -+ -+ return -EINVAL; -+} -+ -+int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, -+ struct mtk_snand **psnf) -+{ -+ const struct snand_flash_info *snand_info; -+ struct mtk_snand tmpsnf, *snf; -+ uint32_t rawpage_size; -+ int ret; -+ -+ if (!pdata || !psnf) -+ return -EINVAL; -+ -+ if (pdata->soc >= __SNAND_SOC_MAX) { -+ snand_log_chip(dev, "Invalid SOC %u for MTK-SNAND\n", -+ pdata->soc); -+ return -EINVAL; -+ } -+ -+ /* Dummy instance only for initial reset and id probe */ -+ tmpsnf.nfi_base = pdata->nfi_base; -+ tmpsnf.ecc_base = pdata->ecc_base; -+ tmpsnf.soc = pdata->soc; -+ tmpsnf.nfi_soc = &mtk_snand_socs[pdata->soc]; -+ tmpsnf.pdev = dev; -+ -+ /* Switch to SNFI mode */ -+ writel(SPI_MODE, tmpsnf.nfi_base + SNF_CFG); -+ -+ /* Reset SNFI & NFI */ -+ mtk_snand_mac_reset(&tmpsnf); -+ mtk_nfi_reset(&tmpsnf); -+ -+ /* Reset SPI-NAND chip */ -+ ret = mtk_snand_chip_reset(&tmpsnf); -+ if (ret) { -+ snand_log_chip(dev, "Failed to reset SPI-NAND chip\n"); -+ return ret; -+ } -+ -+ /* Probe SPI-NAND flash by JEDEC ID */ -+ ret = mtk_snand_id_probe(&tmpsnf, &snand_info); -+ if (ret) -+ return ret; -+ -+ rawpage_size = snand_info->memorg.pagesize + -+ snand_info->memorg.sparesize; -+ -+ /* Allocate memory for instance and cache */ -+ snf = generic_mem_alloc(dev, sizeof(*snf) + rawpage_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)); -+ -+ /* Allocate memory for DMA buffer */ -+ snf->page_cache = dma_mem_alloc(dev, rawpage_size); -+ if (!snf->page_cache) { -+ generic_mem_free(dev, snf); -+ snand_log_chip(dev, -+ "Failed to allocate memory for DMA buffer\n"); -+ return -ENOMEM; -+ } -+ -+ /* Fill up instance */ -+ snf->pdev = dev; -+ snf->nfi_base = pdata->nfi_base; -+ snf->ecc_base = pdata->ecc_base; -+ snf->soc = pdata->soc; -+ snf->nfi_soc = &mtk_snand_socs[pdata->soc]; -+ snf->snfi_quad_spi = pdata->quad_spi; -+ -+ /* Initialize SNFI & ECC engine */ -+ ret = mtk_snand_setup(snf, snand_info); -+ if (ret) { -+ dma_mem_free(dev, snf->page_cache); -+ generic_mem_free(dev, snf); -+ return ret; -+ } -+ -+ *psnf = snf; -+ -+ return 0; -+} -+ -+int mtk_snand_cleanup(struct mtk_snand *snf) -+{ -+ if (!snf) -+ return 0; -+ -+ dma_mem_free(snf->pdev, snf->page_cache); -+ generic_mem_free(snf->pdev, snf); -+ -+ return 0; -+} ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand.h -@@ -0,0 +1,77 @@ -+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -+/* -+ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#ifndef _MTK_SNAND_H_ -+#define _MTK_SNAND_H_ -+ -+#ifndef PRIVATE_MTK_SNAND_HEADER -+#include -+#include -+#include -+#endif -+ -+enum mtk_snand_soc { -+ SNAND_SOC_MT7622, -+ SNAND_SOC_MT7629, -+ SNAND_SOC_MT7986, -+ -+ __SNAND_SOC_MAX -+}; -+ -+struct mtk_snand_platdata { -+ void *nfi_base; -+ void *ecc_base; -+ enum mtk_snand_soc soc; -+ bool quad_spi; -+}; -+ -+struct mtk_snand_chip_info { -+ const char *model; -+ uint64_t chipsize; -+ uint32_t blocksize; -+ uint32_t pagesize; -+ uint32_t sparesize; -+ uint32_t spare_per_sector; -+ uint32_t fdm_size; -+ uint32_t fdm_ecc_size; -+ uint32_t num_sectors; -+ uint32_t sector_size; -+ uint32_t ecc_strength; -+ uint32_t ecc_bytes; -+}; -+ -+struct mtk_snand; -+struct snand_flash_info; -+ -+int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, -+ struct mtk_snand **psnf); -+int mtk_snand_cleanup(struct mtk_snand *snf); -+ -+int mtk_snand_chip_reset(struct mtk_snand *snf); -+int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, -+ void *oob, bool raw); -+int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, -+ const void *oob, bool raw); -+int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr); -+int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr); -+int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr); -+int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, -+ const uint8_t *oobbuf, size_t ooblen); -+int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, -+ size_t ooblen, const uint8_t *oobraw); -+int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, -+ void *buf, void *oob, size_t ooblen, -+ size_t *actualooblen, bool raw); -+int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, -+ const void *buf, const void *oob, -+ size_t ooblen, size_t *actualooblen, -+ bool raw); -+int mtk_snand_get_chip_info(struct mtk_snand *snf, -+ struct mtk_snand_chip_info *info); -+int mtk_snand_irq_process(struct mtk_snand *snf); -+ -+#endif /* _MTK_SNAND_H_ */ 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/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch deleted file mode 100644 index 4a06acc2dc..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch +++ /dev/null @@ -1,174 +0,0 @@ -From b7fb0e0674db12bcf53df4b107a17c80758ee5d3 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 - -Add support to initialize SPI-NAND in SPL. -Add implementation for SPL NAND loader. - -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(+) - create mode 100644 drivers/mtd/mtk-snand/mtk-snand-spl.c - ---- a/drivers/mtd/mtk-snand/Kconfig -+++ b/drivers/mtd/mtk-snand/Kconfig -@@ -19,3 +19,9 @@ config MTK_SPI_NAND_MTD - help - This option enables access to SPI-NAND flashes through the - MTD interface of MediaTek SPI NAND Flash Controller -+ -+config SPL_MTK_SPI_NAND -+ tristate "SPL support for MediaTek SPI NAND flash controller" -+ depends on MTK_SPI_NAND -+ help -+ This option enables access to SPI-NAND flashes in the SPL stage ---- a/drivers/mtd/mtk-snand/Makefile -+++ b/drivers/mtd/mtk-snand/Makefile -@@ -8,4 +8,8 @@ - obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o - obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o - -+ifdef CONFIG_SPL_BUILD -+obj-$(CONFIG_SPL_MTK_SPI_NAND) += mtk-snand-spl.o -+endif -+ - ccflags-y += -DPRIVATE_MTK_SNAND_HEADER ---- /dev/null -+++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c -@@ -0,0 +1,132 @@ -+// 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 "mtk-snand.h" -+ -+static struct mtk_snand *snf; -+static struct mtk_snand_chip_info cinfo; -+static u32 oobavail; -+ -+static u8 *page_cache; -+ -+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) -+{ -+ u32 sizeremain = size, chunksize, leading; -+ uint32_t off = offs, writesize_mask = cinfo.pagesize - 1; -+ uint8_t *ptr = dst; -+ int ret; -+ -+ if (!snf) -+ return -ENODEV; -+ -+ while (sizeremain) { -+ WATCHDOG_RESET(); -+ -+ leading = off & writesize_mask; -+ chunksize = cinfo.pagesize - leading; -+ if (chunksize > sizeremain) -+ chunksize = sizeremain; -+ -+ if (chunksize == cinfo.pagesize) { -+ ret = mtk_snand_read_page(snf, off - leading, ptr, -+ NULL, false); -+ if (ret) -+ break; -+ } else { -+ ret = mtk_snand_read_page(snf, off - leading, -+ page_cache, NULL, false); -+ if (ret) -+ break; -+ -+ memcpy(ptr, page_cache + leading, chunksize); -+ } -+ -+ off += chunksize; -+ ptr += chunksize; -+ sizeremain -= chunksize; -+ } -+ -+ return ret; -+} -+ -+void nand_init(void) -+{ -+ struct mtk_snand_platdata mtk_snand_pdata = {}; -+ struct udevice *dev; -+ fdt_addr_t base; -+ int ret; -+ -+ ret = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_GET(mtk_snand), -+ &dev); -+ if (ret) { -+ printf("mtk-snand-spl: Device instance not found!\n"); -+ return; -+ } -+ -+ base = dev_read_addr_name(dev, "nfi"); -+ if (base == FDT_ADDR_T_NONE) { -+ printf("mtk-snand-spl: NFI base not set\n"); -+ return; -+ } -+ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); -+ -+ base = dev_read_addr_name(dev, "ecc"); -+ if (base == FDT_ADDR_T_NONE) { -+ printf("mtk-snand-spl: ECC base not set\n"); -+ return; -+ } -+ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); -+ -+ mtk_snand_pdata.soc = dev_get_driver_data(dev); -+ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); -+ -+ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &snf); -+ if (ret) { -+ printf("mtk-snand-spl: failed to initialize SPI-NAND\n"); -+ return; -+ } -+ -+ mtk_snand_get_chip_info(snf, &cinfo); -+ -+ oobavail = cinfo.num_sectors * (cinfo.fdm_size - 1); -+ -+ printf("SPI-NAND: %s (%uMB)\n", cinfo.model, -+ (u32)(cinfo.chipsize >> 20)); -+ -+ page_cache = malloc(cinfo.pagesize + cinfo.sparesize); -+ if (!page_cache) { -+ mtk_snand_cleanup(snf); -+ printf("mtk-snand-spl: failed to allocate page cache\n"); -+ } -+} -+ -+void nand_deselect(void) -+{ -+ -+} -+ -+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,mt7986-snand", .data = SNAND_SOC_MT7986 }, -+ { /* sentinel */ }, -+}; -+ -+U_BOOT_DRIVER(mtk_snand) = { -+ .name = "mtk-snand", -+ .id = UCLASS_MTD, -+ .of_match = mtk_snand_ids, -+ .flags = DM_FLAG_PRE_RELOC, -+}; 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/000-mtk-16-env-add-support-for-generic-MTD-device.patch deleted file mode 100644 index f2e91671fb..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch +++ /dev/null @@ -1,409 +0,0 @@ -From a26620ec83fa3077f0c261046e82091f7455736f 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 - -Add an env driver for generic MTD device. - -Signed-off-by: Weijie Gao ---- - cmd/nvedit.c | 3 +- - env/Kconfig | 37 +++++- - env/Makefile | 1 + - env/env.c | 3 + - env/mtd.c | 256 +++++++++++++++++++++++++++++++++++++++++ - include/env_internal.h | 1 + - tools/Makefile | 1 + - 7 files changed, 299 insertions(+), 3 deletions(-) - create mode 100644 env/mtd.c - ---- a/cmd/nvedit.c -+++ b/cmd/nvedit.c -@@ -48,6 +48,7 @@ DECLARE_GLOBAL_DATA_PTR; - defined(CONFIG_ENV_IS_IN_MMC) || \ - defined(CONFIG_ENV_IS_IN_FAT) || \ - defined(CONFIG_ENV_IS_IN_EXT4) || \ -+ defined(CONFIG_ENV_IS_IN_MTD) || \ - defined(CONFIG_ENV_IS_IN_NAND) || \ - defined(CONFIG_ENV_IS_IN_NVRAM) || \ - defined(CONFIG_ENV_IS_IN_ONENAND) || \ -@@ -62,7 +63,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|\ -+# 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 - #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_UBI && !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 -@@ -226,6 +226,27 @@ config ENV_IS_IN_MMC - This value is also in units of bytes, but must also be aligned to - an MMC sector boundary. - -+config ENV_IS_IN_MTD -+ bool "Environment in a MTD device" -+ depends on !CHAIN_OF_TRUST -+ depends on MTD -+ help -+ Define this if you have a MTD device which you want to use for -+ the environment. -+ -+ - CONFIG_ENV_MTD_NAME: -+ - CONFIG_ENV_OFFSET: -+ - CONFIG_ENV_SIZE: -+ -+ These three #defines specify the MTD device where the environment -+ is stored, offset and size of the environment area within the MTD -+ device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary. -+ -+ - CONFIG_ENV_SIZE_REDUND: -+ -+ This #define specify the maximum size allowed for read/write/erase -+ with skipped bad blocks starting from ENV_OFFSET. -+ - config ENV_IS_IN_NAND - bool "Environment in a NAND device" - depends on !CHAIN_OF_TRUST -@@ -531,10 +552,16 @@ config ENV_ADDR_REDUND - Offset from the start of the device (or partition) of the redundant - environment location. - -+config ENV_MTD_NAME -+ string "Name of the MTD device storing the environment" -+ depends on ENV_IS_IN_MTD -+ help -+ Name of the MTD device that stores the environment -+ - 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_SPI_FLASH || 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 -@@ -581,6 +608,12 @@ config ENV_SECT_SIZE - help - Size of the sector containing the environment. - -+config ENV_SIZE_REDUND -+ hex "Redundant environment size" -+ depends on ENV_IS_IN_MTD -+ help -+ The maximum size allowed for read/write/erase with skipped bad blocks. -+ - config ENV_UBI_PART - string "UBI partition name" - depends on ENV_IS_IN_UBI ---- a/env/Makefile -+++ b/env/Makefile -@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE) - obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o - obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o - 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_SPI_FLASH) += sf.o - obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o ---- a/env/env.c -+++ b/env/env.c -@@ -69,6 +69,9 @@ static enum env_location env_locations[] - #ifdef CONFIG_ENV_IS_IN_MMC - ENVL_MMC, - #endif -+#ifdef CONFIG_ENV_IS_IN_MTD -+ ENVL_MTD, -+#endif - #ifdef CONFIG_ENV_IS_IN_NAND - ENVL_NAND, - #endif ---- /dev/null -+++ b/env/mtd.c -@@ -0,0 +1,256 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. -+ * -+ * Author: Weijie Gao -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE -+#undef CONFIG_ENV_SIZE_REDUND -+#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE -+#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_mtd_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; -+} -+ -+static struct mtd_info *env_mtd_get_dev(void) -+{ -+ struct mtd_info *mtd; -+ -+ mtd_probe_devices(); -+ -+ mtd = get_mtd_device_nm(CONFIG_ENV_MTD_NAME); -+ if (IS_ERR(mtd) || !mtd) { -+ printf("MTD device '%s' not found\n", CONFIG_ENV_MTD_NAME); -+ return NULL; -+ } -+ -+ return mtd; -+} -+ -+static inline bool mtd_addr_is_block_aligned(struct mtd_info *mtd, u64 addr) -+{ -+ return (addr & mtd->erasesize_mask) == 0; -+} -+ -+static int mtd_io_skip_bad(struct mtd_info *mtd, bool read, loff_t offset, -+ size_t length, size_t redund, u8 *buffer) -+{ -+ struct mtd_oob_ops io_op = {}; -+ size_t remaining = length; -+ loff_t off, end; -+ int ret; -+ -+ io_op.mode = MTD_OPS_PLACE_OOB; -+ io_op.len = mtd->writesize; -+ io_op.datbuf = (void *)buffer; -+ -+ /* Search for the first good block after the given offset */ -+ off = offset; -+ end = (off + redund) | (mtd->erasesize - 1); -+ while (mtd_block_isbad(mtd, off) && off < end) -+ off += mtd->erasesize; -+ -+ /* Reached end position */ -+ if (off >= end) -+ return -EIO; -+ -+ /* Loop over the pages to do the actual read/write */ -+ while (remaining) { -+ /* Skip the block if it is bad */ -+ if (mtd_addr_is_block_aligned(mtd, off) && -+ mtd_block_isbad(mtd, off)) { -+ off += mtd->erasesize; -+ continue; -+ } -+ -+ if (read) -+ ret = mtd_read_oob(mtd, off, &io_op); -+ else -+ ret = mtd_write_oob(mtd, off, &io_op); -+ -+ if (ret) { -+ printf("Failure while %s at offset 0x%llx\n", -+ read ? "reading" : "writing", off); -+ break; -+ } -+ -+ off += io_op.retlen; -+ remaining -= io_op.retlen; -+ io_op.datbuf += io_op.retlen; -+ io_op.oobbuf += io_op.oobretlen; -+ -+ /* Reached end position */ -+ if (off >= end) -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+#ifdef CONFIG_CMD_SAVEENV -+static int mtd_erase_skip_bad(struct mtd_info *mtd, loff_t offset, -+ size_t length, size_t redund) -+{ -+ struct erase_info erase_op = {}; -+ loff_t end = (offset + redund) | (mtd->erasesize - 1); -+ int ret; -+ -+ erase_op.mtd = mtd; -+ erase_op.addr = offset; -+ erase_op.len = length; -+ -+ while (erase_op.len) { -+ ret = mtd_erase(mtd, &erase_op); -+ -+ /* Abort if its not a bad block error */ -+ if (ret != -EIO) -+ return ret; -+ -+ printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr); -+ -+ /* Skip bad block and continue behind it */ -+ erase_op.len -= erase_op.fail_addr - erase_op.addr; -+ erase_op.len -= mtd->erasesize; -+ erase_op.addr = erase_op.fail_addr + mtd->erasesize; -+ -+ /* Reached end position */ -+ if (erase_op.addr >= end) -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int env_mtd_save(void) -+{ -+ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); -+ struct mtd_info *mtd; -+ int ret = 0; -+ -+ ret = env_export(env_new); -+ if (ret) -+ return ret; -+ -+ mtd = env_mtd_get_dev(); -+ if (!mtd) -+ return 1; -+ -+ printf("Erasing on MTD device '%s'... ", mtd->name); -+ -+ ret = mtd_erase_skip_bad(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, -+ CONFIG_ENV_SIZE_REDUND); -+ -+ puts(ret ? "FAILED\n" : "OK\n"); -+ -+ if (ret) { -+ put_mtd_device(mtd); -+ return 1; -+ } -+ -+ printf("Writing to MTD device '%s'... ", mtd->name); -+ -+ ret = mtd_io_skip_bad(mtd, false, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, -+ CONFIG_ENV_SIZE_REDUND, (u8 *)env_new); -+ -+ puts(ret ? "FAILED\n" : "OK\n"); -+ -+ put_mtd_device(mtd); -+ -+ return !!ret; -+} -+#endif /* CONFIG_CMD_SAVEENV */ -+ -+static int readenv(size_t offset, u_char *buf) -+{ -+ struct mtd_info *mtd; -+ int ret; -+ -+ mtd = env_mtd_get_dev(); -+ if (!mtd) -+ return 1; -+ -+ ret = mtd_io_skip_bad(mtd, true, offset, CONFIG_ENV_SIZE, -+ CONFIG_ENV_SIZE_REDUND, buf); -+ -+ put_mtd_device(mtd); -+ -+ return !!ret; -+} -+ -+static int env_mtd_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(mtd) = { -+ .location = ENVL_MTD, -+ ENV_NAME("MTD") -+ .load = env_mtd_load, -+#if defined(CONFIG_CMD_SAVEENV) -+ .save = env_save_ptr(env_mtd_save), -+#endif -+ .init = env_mtd_init, -+}; ---- a/include/env_internal.h -+++ b/include/env_internal.h -@@ -130,6 +130,7 @@ enum env_location { - ENVL_FAT, - ENVL_FLASH, - ENVL_MMC, -+ ENVL_MTD, - ENVL_NAND, - ENVL_NVRAM, - ENVL_ONENAND, ---- a/tools/Makefile -+++ b/tools/Makefile -@@ -41,6 +41,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y - ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y - 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_NVRAM) = y - ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y 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/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch deleted file mode 100644 index 4ee3d6f320..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch +++ /dev/null @@ -1,223 +0,0 @@ -From 3757223c3354b9feeffcbe916eb18eb8873bd133 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 - -Add support for mt7629 to boot from SPI-NAND. -Add a new defconfig for mt7629+spi-nand configuration. - -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(-) - create mode 100644 configs/mt7629_nand_rfb_defconfig - ---- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi -+++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi -@@ -40,3 +40,11 @@ - &snfi { - u-boot,dm-pre-reloc; - }; -+ -+&pinctrl { -+ u-boot,dm-pre-reloc; -+}; -+ -+&snand { -+ u-boot,dm-pre-reloc; -+}; ---- a/arch/arm/dts/mt7629-rfb.dts -+++ b/arch/arm/dts/mt7629-rfb.dts -@@ -47,9 +47,12 @@ - }; - - snfi_pins: snfi-pins { -+ u-boot,dm-pre-reloc; -+ - mux { - function = "flash"; - groups = "snfi"; -+ u-boot,dm-pre-reloc; - }; - }; - -@@ -102,6 +105,13 @@ - }; - }; - -+&snand { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&snfi_pins>; -+ status = "okay"; -+ quad-spi; -+}; -+ - &uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; ---- a/arch/arm/dts/mt7629.dtsi -+++ b/arch/arm/dts/mt7629.dtsi -@@ -229,6 +229,22 @@ - #size-cells = <0>; - }; - -+ snand: snand@1100d000 { -+ compatible = "mediatek,mt7629-snand"; -+ reg = <0x1100d000 0x1000>, -+ <0x1100e000 0x1000>; -+ reg-names = "nfi", "ecc"; -+ clocks = <&pericfg CLK_PERI_NFI_PD>, -+ <&pericfg CLK_PERI_SNFI_PD>, -+ <&pericfg CLK_PERI_NFIECC_PD>; -+ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; -+ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, -+ <&topckgen CLK_TOP_NFI_INFRA_SEL>; -+ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, -+ <&topckgen CLK_TOP_UNIVPLL2_D8>; -+ status = "disabled"; -+ }; -+ - snor: snor@11014000 { - compatible = "mediatek,mtk-snor"; - reg = <0x11014000 0x1000>; ---- /dev/null -+++ b/configs/mt7629_nand_rfb_defconfig -@@ -0,0 +1,111 @@ -+CONFIG_ARM=y -+CONFIG_SYS_ARCH_TIMER=y -+CONFIG_SYS_THUMB_BUILD=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_ENV_OFFSET=0x100000 -+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_STACK_R_ADDR=0x40800000 -+CONFIG_SPL_PAYLOAD="u-boot.img" -+CONFIG_BUILD_TARGET="u-boot-mtk.bin" -+CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb" -+CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin" -+CONFIG_FIT=y -+CONFIG_FIT_VERBOSE=y -+CONFIG_BOOTDELAY=3 -+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_SYS_MALLOC_SIMPLE=y -+CONFIG_SPL_STACK_R=y -+CONFIG_SPL_MTD_SUPPORT=y -+CONFIG_SPL_NAND_SUPPORT=y -+CONFIG_SPL_WATCHDOG_SUPPORT=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_CMD_ELF is not set -+# CONFIG_CMD_XIMG is not set -+CONFIG_CMD_BIND=y -+CONFIG_CMD_DM=y -+# CONFIG_CMD_FLASH is not set -+CONFIG_CMD_GPIO=y -+CONFIG_CMD_MTD=y -+CONFIG_CMD_USB=y -+# CONFIG_CMD_SETEXPR is not set -+# CONFIG_CMD_NFS is not set -+CONFIG_CMD_PING=y -+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_SIZE_REDUND=0x40000 -+CONFIG_SYS_RELOC_GD_ENV_ADDR=y -+CONFIG_NET_RANDOM_ETHADDR=y -+CONFIG_SPL_DM_SEQ_ALIAS=y -+CONFIG_REGMAP=y -+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 -+CONFIG_MTD=y -+CONFIG_DM_MTD=y -+CONFIG_MTK_SPI_NAND=y -+CONFIG_MTK_SPI_NAND_MTD=y -+CONFIG_SPL_MTK_SPI_NAND=y -+CONFIG_DM_ETH=y -+CONFIG_MEDIATEK_ETH=y -+CONFIG_PHY=y -+CONFIG_PHY_MTK_TPHY=y -+CONFIG_PINCTRL=y -+CONFIG_PINCONF=y -+CONFIG_SPL_PINCTRL=y -+CONFIG_SPL_PINCONF=y -+CONFIG_PINCTRL_MT7629=y -+CONFIG_POWER_DOMAIN=y -+CONFIG_MTK_POWER_DOMAIN=y -+CONFIG_DM_REGULATOR=y -+CONFIG_DM_REGULATOR_FIXED=y -+CONFIG_RAM=y -+CONFIG_SPL_RAM=y -+CONFIG_DM_SERIAL=y -+CONFIG_MTK_SERIAL=y -+CONFIG_SPI=y -+CONFIG_DM_SPI=y -+CONFIG_SPI_MEM=y -+CONFIG_MTK_SNFI_SPI=y -+CONFIG_SYSRESET=y -+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_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/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch deleted file mode 100644 index 84101d0ef9..0000000000 --- a/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 6bcd65ed47844e747ff6db066b092632f1760256 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 - -Enable new spi-nand driver support for mt7622_rfb_defconfig - -Signed-off-by: Weijie Gao ---- - arch/arm/dts/mt7622-rfb.dts | 7 +++++++ - arch/arm/dts/mt7622.dtsi | 16 ++++++++++++++++ - configs/mt7622_rfb_defconfig | 5 +++++ - 3 files changed, 28 insertions(+) - ---- a/arch/arm/dts/mt7622-rfb.dts -+++ b/arch/arm/dts/mt7622-rfb.dts -@@ -188,6 +188,13 @@ - }; - }; - -+&snand { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&snfi_pins>; -+ status = "okay"; -+ quad-spi; -+}; -+ - &uart0 { - pinctrl-names = "default"; - pinctrl-0 = <&uart0_pins>; ---- a/arch/arm/dts/mt7622.dtsi -+++ b/arch/arm/dts/mt7622.dtsi -@@ -53,6 +53,22 @@ - #size-cells = <0>; - }; - -+ snand: snand@1100d000 { -+ compatible = "mediatek,mt7622-snand"; -+ reg = <0x1100d000 0x1000>, -+ <0x1100e000 0x1000>; -+ reg-names = "nfi", "ecc"; -+ clocks = <&pericfg CLK_PERI_NFI_PD>, -+ <&pericfg CLK_PERI_SNFI_PD>, -+ <&pericfg CLK_PERI_NFIECC_PD>; -+ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; -+ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, -+ <&topckgen CLK_TOP_NFI_INFRA_SEL>; -+ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, -+ <&topckgen CLK_TOP_UNIVPLL2_D8>; -+ status = "disabled"; -+ }; -+ - snor: snor@11014000 { - compatible = "mediatek,mtk-snor"; - reg = <0x11014000 0x1000>; ---- a/configs/mt7622_rfb_defconfig -+++ b/configs/mt7622_rfb_defconfig -@@ -16,6 +16,7 @@ CONFIG_LOG=y - CONFIG_SYS_PROMPT="MT7622> " - CONFIG_CMD_BOOTMENU=y - CONFIG_CMD_MMC=y -+CONFIG_CMD_MTD=y - CONFIG_CMD_PCI=y - CONFIG_CMD_SF_TEST=y - CONFIG_CMD_PING=y -@@ -28,6 +29,10 @@ CONFIG_SYSCON=y - CONFIG_CLK=y - CONFIG_MMC_HS200_SUPPORT=y - CONFIG_MMC_MTK=y -+CONFIG_MTD=y -+CONFIG_DM_MTD=y -+CONFIG_MTK_SPI_NAND=y -+CONFIG_MTK_SPI_NAND_MTD=y - CONFIG_DM_SPI_FLASH=y - CONFIG_SPI_FLASH_EON=y - CONFIG_SPI_FLASH_GIGADEVICE=y 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 cc5fb57324..cf2e48d5c4 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 63e189f56c..603896f260 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 085d66776f..7ed6083c89 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 415b37ba55..378078882e 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 8ecfda2d46..9a5332f695 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 0000000000..e54b46f5e4 --- /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 0000000000..b0299ac6d8 --- /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 0000000000..b88120052c --- /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 0000000000..30dcffd59e --- /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/100-01-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch new file mode 100644 index 0000000000..9591463373 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch @@ -0,0 +1,70 @@ +From 97df847f8f895cc2692bb0e4e933269c275da378 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 15:47:45 +0800 +Subject: [PATCH 35/71] board: mediatek: add more network configurations + +Make the network configurations uniform for mediatek boards + +Signed-off-by: Weijie Gao +--- + include/configs/mt7622.h | 3 ++- + include/configs/mt7623.h | 1 + + include/configs/mt7629.h | 1 + + 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 +@@ -30,6 +30,7 @@ + + /* Ethernet */ + #define CONFIG_IPADDR 192.168.1.1 +-#define CONFIG_SERVERIP 192.168.1.3 ++#define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 + + #endif +--- a/include/configs/mt7623.h ++++ b/include/configs/mt7623.h +@@ -45,6 +45,7 @@ + /* Ethernet */ + #define CONFIG_IPADDR 192.168.1.1 + #define CONFIG_SERVERIP 192.168.1.2 ++#define CONFIG_NETMASK 255.255.255.0 + + #ifdef CONFIG_DISTRO_DEFAULTS + +--- a/include/configs/mt7629.h ++++ b/include/configs/mt7629.h +@@ -45,5 +45,6 @@ + /* 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/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/100-02-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 new file mode 100644 index 0000000000..e2316c62c4 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch @@ -0,0 +1,4000 @@ +From f7704275957852cd4c4632d6da126979ef24b83a Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 2 Mar 2021 16:58:01 +0800 +Subject: [PATCH 36/71] drivers: mtd: add support for MediaTek SPI-NAND flash + controller + +Add mtd driver for MediaTek SPI-NAND flash controller + +This driver is written from scratch, and uses standard mtd framework, not +the nand framework which only applies for raw parallel nand flashes so that +this driver can have a smaller size in binary. + +Signed-off-by: Weijie Gao +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 2 + + drivers/mtd/mtk-snand/Kconfig | 21 + + drivers/mtd/mtk-snand/Makefile | 11 + + 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 | 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 | 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 + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ecc.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ids.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-mtd.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.h + create mode 100644 drivers/mtd/mtk-snand/mtk-snand.c + create mode 100644 drivers/mtd/mtk-snand/mtk-snand.h + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -158,6 +158,8 @@ config SYS_MAX_FLASH_BANKS_DETECT + to reduce the effective number of flash bank, between 0 and + CONFIG_SYS_MAX_FLASH_BANKS + ++source "drivers/mtd/mtk-snand/Kconfig" ++ + source "drivers/mtd/nand/Kconfig" + + config SYS_NAND_MAX_CHIPS +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -39,3 +39,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR + obj-$(CONFIG_SPL_UBI) += ubispl/ + + endif ++ ++obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/Kconfig +@@ -0,0 +1,21 @@ ++# ++# Copyright (C) 2020 MediaTek Inc. All rights reserved. ++# Author: Weijie Gao ++# ++# SPDX-License-Identifier: GPL-2.0 ++# ++ ++config MTK_SPI_NAND ++ tristate "MediaTek SPI NAND flash controller driver" ++ depends on !MTD_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes through the ++ MediaTek SPI NAND Flash Controller ++ ++config MTK_SPI_NAND_MTD ++ tristate "MTD support for MediaTek SPI NAND flash controller" ++ depends on DM_MTD ++ depends on MTK_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes through the ++ MTD interface of MediaTek SPI NAND Flash Controller +--- /dev/null ++++ b/drivers/mtd/mtk-snand/Makefile +@@ -0,0 +1,11 @@ ++# ++# Copyright (C) 2020 MediaTek Inc. All rights reserved. ++# Author: Weijie Gao ++# ++# SPDX-License-Identifier: GPL-2.0 ++# ++ ++obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o ++obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o ++ ++ccflags-y += -DPRIVATE_MTK_SNAND_HEADER +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-def.h +@@ -0,0 +1,271 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_DEF_H_ ++#define _MTK_SNAND_DEF_H_ ++ ++#include "mtk-snand-os.h" ++ ++#ifdef PRIVATE_MTK_SNAND_HEADER ++#include "mtk-snand.h" ++#else ++#include ++#endif ++ ++struct mtk_snand_plat_dev; ++ ++enum snand_flash_io { ++ SNAND_IO_1_1_1, ++ SNAND_IO_1_1_2, ++ SNAND_IO_1_2_2, ++ SNAND_IO_1_1_4, ++ SNAND_IO_1_4_4, ++ ++ __SNAND_IO_MAX ++}; ++ ++#define SPI_IO_1_1_1 BIT(SNAND_IO_1_1_1) ++#define SPI_IO_1_1_2 BIT(SNAND_IO_1_1_2) ++#define SPI_IO_1_2_2 BIT(SNAND_IO_1_2_2) ++#define SPI_IO_1_1_4 BIT(SNAND_IO_1_1_4) ++#define SPI_IO_1_4_4 BIT(SNAND_IO_1_4_4) ++ ++struct snand_opcode { ++ uint8_t opcode; ++ uint8_t dummy; ++}; ++ ++struct snand_io_cap { ++ uint8_t caps; ++ struct snand_opcode opcodes[__SNAND_IO_MAX]; ++}; ++ ++#define SNAND_OP(_io, _opcode, _dummy) [_io] = { .opcode = (_opcode), \ ++ .dummy = (_dummy) } ++ ++#define SNAND_IO_CAP(_name, _caps, ...) \ ++ struct snand_io_cap _name = { .caps = (_caps), \ ++ .opcodes = { __VA_ARGS__ } } ++ ++#define SNAND_MAX_ID_LEN 4 ++ ++enum snand_id_type { ++ SNAND_ID_DYMMY, ++ SNAND_ID_ADDR = SNAND_ID_DYMMY, ++ SNAND_ID_DIRECT, ++ ++ __SNAND_ID_TYPE_MAX ++}; ++ ++struct snand_id { ++ uint8_t type; /* enum snand_id_type */ ++ uint8_t len; ++ uint8_t id[SNAND_MAX_ID_LEN]; ++}; ++ ++#define SNAND_ID(_type, ...) \ ++ { .type = (_type), .id = { __VA_ARGS__ }, \ ++ .len = sizeof((uint8_t[]) { __VA_ARGS__ }) } ++ ++struct snand_mem_org { ++ uint16_t pagesize; ++ uint16_t sparesize; ++ uint16_t pages_per_block; ++ uint16_t blocks_per_die; ++ uint16_t planes_per_die; ++ uint16_t ndies; ++}; ++ ++#define SNAND_MEMORG(_ps, _ss, _ppb, _bpd, _ppd, _nd) \ ++ { .pagesize = (_ps), .sparesize = (_ss), .pages_per_block = (_ppb), \ ++ .blocks_per_die = (_bpd), .planes_per_die = (_ppd), .ndies = (_nd) } ++ ++typedef int (*snand_select_die_t)(struct mtk_snand *snf, uint32_t dieidx); ++ ++struct snand_flash_info { ++ const char *model; ++ struct snand_id id; ++ const struct snand_mem_org memorg; ++ const struct snand_io_cap *cap_rd; ++ const struct snand_io_cap *cap_pl; ++ snand_select_die_t select_die; ++}; ++ ++#define SNAND_INFO(_model, _id, _memorg, _cap_rd, _cap_pl, ...) \ ++ { .model = (_model), .id = _id, .memorg = _memorg, \ ++ .cap_rd = (_cap_rd), .cap_pl = (_cap_pl), __VA_ARGS__ } ++ ++const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, ++ const uint8_t *id); ++ ++struct mtk_snand_soc_data { ++ uint16_t sector_size; ++ uint16_t max_sectors; ++ uint16_t fdm_size; ++ uint16_t fdm_ecc_size; ++ uint16_t fifo_size; ++ ++ bool bbm_swap; ++ bool empty_page_check; ++ uint32_t mastersta_mask; ++ ++ const uint8_t *spare_sizes; ++ uint32_t num_spare_size; ++ ++ uint16_t latch_lat; ++ uint16_t sample_delay; ++}; ++ ++enum mtk_ecc_regs { ++ ECC_DECDONE, ++}; ++ ++struct mtk_ecc_soc_data { ++ const uint8_t *ecc_caps; ++ uint32_t num_ecc_cap; ++ const uint32_t *regs; ++ uint16_t mode_shift; ++ uint8_t errnum_bits; ++ uint8_t errnum_shift; ++}; ++ ++struct mtk_snand { ++ struct mtk_snand_plat_dev *pdev; ++ ++ void __iomem *nfi_base; ++ void __iomem *ecc_base; ++ ++ enum mtk_snand_soc soc; ++ const struct mtk_snand_soc_data *nfi_soc; ++ const struct mtk_ecc_soc_data *ecc_soc; ++ bool snfi_quad_spi; ++ bool quad_spi_op; ++ ++ const char *model; ++ uint64_t size; ++ uint64_t die_size; ++ uint32_t erasesize; ++ uint32_t writesize; ++ uint32_t oobsize; ++ ++ uint32_t num_dies; ++ snand_select_die_t select_die; ++ ++ uint8_t opcode_rfc; ++ uint8_t opcode_pl; ++ uint8_t dummy_rfc; ++ uint8_t mode_rfc; ++ uint8_t mode_pl; ++ ++ uint32_t writesize_mask; ++ uint32_t writesize_shift; ++ uint32_t erasesize_mask; ++ uint32_t erasesize_shift; ++ uint64_t die_mask; ++ uint32_t die_shift; ++ ++ uint32_t spare_per_sector; ++ uint32_t raw_sector_size; ++ uint32_t ecc_strength; ++ uint32_t ecc_steps; ++ uint32_t ecc_bytes; ++ uint32_t ecc_parity_bits; ++ ++ 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 { ++ SNAND_LOG_NFI, ++ SNAND_LOG_SNFI, ++ SNAND_LOG_ECC, ++ SNAND_LOG_CHIP, ++ ++ __SNAND_LOG_CAT_MAX ++}; ++ ++int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, ++ uint32_t msg_size); ++int mtk_snand_ecc_encoder_start(struct mtk_snand *snf); ++void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf); ++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); ++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); ++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val); ++ ++int mtk_snand_log(struct mtk_snand_plat_dev *pdev, ++ enum mtk_snand_log_category cat, const char *fmt, ...); ++ ++#define snand_log_nfi(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_NFI, fmt, ##__VA_ARGS__) ++ ++#define snand_log_snfi(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_SNFI, fmt, ##__VA_ARGS__) ++ ++#define snand_log_ecc(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_ECC, fmt, ##__VA_ARGS__) ++ ++#define snand_log_chip(pdev, fmt, ...) \ ++ mtk_snand_log(pdev, SNAND_LOG_CHIP, fmt, ##__VA_ARGS__) ++ ++/* ffs64 */ ++static inline int mtk_snand_ffs64(uint64_t x) ++{ ++ if (!x) ++ return 0; ++ ++ if (!(x & 0xffffffff)) ++ return ffs((uint32_t)(x >> 32)) + 32; ++ ++ return ffs((uint32_t)(x & 0xffffffff)); ++} ++ ++/* NFI dummy commands */ ++#define NFI_CMD_DUMMY_READ 0x00 ++#define NFI_CMD_DUMMY_WRITE 0x80 ++ ++/* SPI-NAND opcodes */ ++#define SNAND_CMD_RESET 0xff ++#define SNAND_CMD_BLOCK_ERASE 0xd8 ++#define SNAND_CMD_READ_FROM_CACHE_QUAD 0xeb ++#define SNAND_CMD_WINBOND_SELECT_DIE 0xc2 ++#define SNAND_CMD_READ_FROM_CACHE_DUAL 0xbb ++#define SNAND_CMD_READID 0x9f ++#define SNAND_CMD_READ_FROM_CACHE_X4 0x6b ++#define SNAND_CMD_READ_FROM_CACHE_X2 0x3b ++#define SNAND_CMD_PROGRAM_LOAD_X4 0x32 ++#define SNAND_CMD_SET_FEATURE 0x1f ++#define SNAND_CMD_READ_TO_CACHE 0x13 ++#define SNAND_CMD_PROGRAM_EXECUTE 0x10 ++#define SNAND_CMD_GET_FEATURE 0x0f ++#define SNAND_CMD_READ_FROM_CACHE 0x0b ++#define SNAND_CMD_WRITE_ENABLE 0x06 ++#define SNAND_CMD_PROGRAM_LOAD 0x02 ++ ++/* SPI-NAND feature addresses */ ++#define SNAND_FEATURE_MICRON_DIE_ADDR 0xd0 ++#define SNAND_MICRON_DIE_SEL_1 BIT(6) ++ ++#define SNAND_FEATURE_STATUS_ADDR 0xc0 ++#define SNAND_STATUS_OIP BIT(0) ++#define SNAND_STATUS_WEL BIT(1) ++#define SNAND_STATUS_ERASE_FAIL BIT(2) ++#define SNAND_STATUS_PROGRAM_FAIL BIT(3) ++ ++#define SNAND_FEATURE_CONFIG_ADDR 0xb0 ++#define SNAND_FEATURE_QUAD_ENABLE BIT(0) ++#define SNAND_FEATURE_ECC_EN BIT(4) ++ ++#define SNAND_FEATURE_PROTECT_ADDR 0xa0 ++ ++#endif /* _MTK_SNAND_DEF_H_ */ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-ecc.c +@@ -0,0 +1,395 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++/* ECC registers */ ++#define ECC_ENCCON 0x000 ++#define ENC_EN BIT(0) ++ ++#define ECC_ENCCNFG 0x004 ++#define ENC_MS_S 16 ++#define ENC_BURST_EN BIT(8) ++#define ENC_TNUM_S 0 ++ ++#define ECC_ENCIDLE 0x00c ++#define ENC_IDLE BIT(0) ++ ++#define ECC_DECCON 0x100 ++#define DEC_EN BIT(0) ++ ++#define ECC_DECCNFG 0x104 ++#define DEC_EMPTY_EN BIT(31) ++#define DEC_CS_S 16 ++#define DEC_CON_S 12 ++#define DEC_CON_CORRECT 3 ++#define DEC_BURST_EN BIT(8) ++#define DEC_TNUM_S 0 ++ ++#define ECC_DECIDLE 0x10c ++#define DEC_IDLE BIT(0) ++ ++#define ECC_DECENUM0 0x114 ++#define ECC_DECENUM(n) (ECC_DECENUM0 + (n) * 4) ++ ++/* ECC_ENCIDLE & ECC_DECIDLE */ ++#define ECC_IDLE BIT(0) ++ ++/* ENC_MODE & DEC_MODE */ ++#define ECC_MODE_NFI 1 ++ ++#define ECC_TIMEOUT 500000 ++ ++static const uint8_t mt7622_ecc_caps[] = { 4, 6, 8, 10, 12 }; ++ ++static const uint8_t mt7986_ecc_caps[] = { ++ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24 ++}; ++ ++static const uint32_t mt7622_ecc_regs[] = { ++ [ECC_DECDONE] = 0x11c, ++}; ++ ++static const uint32_t mt7986_ecc_regs[] = { ++ [ECC_DECDONE] = 0x124, ++}; ++ ++static const struct mtk_ecc_soc_data mtk_ecc_socs[__SNAND_SOC_MAX] = { ++ [SNAND_SOC_MT7622] = { ++ .ecc_caps = mt7622_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), ++ .regs = mt7622_ecc_regs, ++ .mode_shift = 4, ++ .errnum_bits = 5, ++ .errnum_shift = 5, ++ }, ++ [SNAND_SOC_MT7629] = { ++ .ecc_caps = mt7622_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7622_ecc_caps), ++ .regs = mt7622_ecc_regs, ++ .mode_shift = 4, ++ .errnum_bits = 5, ++ .errnum_shift = 5, ++ }, ++ [SNAND_SOC_MT7986] = { ++ .ecc_caps = mt7986_ecc_caps, ++ .num_ecc_cap = ARRAY_SIZE(mt7986_ecc_caps), ++ .regs = mt7986_ecc_regs, ++ .mode_shift = 5, ++ .errnum_bits = 5, ++ .errnum_shift = 8, ++ }, ++}; ++ ++static inline uint32_t ecc_read32(struct mtk_snand *snf, uint32_t reg) ++{ ++ return readl(snf->ecc_base + reg); ++} ++ ++static inline void ecc_write32(struct mtk_snand *snf, uint32_t reg, ++ uint32_t val) ++{ ++ writel(val, snf->ecc_base + reg); ++} ++ ++static inline void ecc_write16(struct mtk_snand *snf, uint32_t reg, ++ uint16_t val) ++{ ++ writew(val, snf->ecc_base + reg); ++} ++ ++static int mtk_ecc_poll(struct mtk_snand *snf, uint32_t reg, uint32_t bits) ++{ ++ uint32_t val; ++ ++ return read16_poll_timeout(snf->ecc_base + reg, val, (val & bits), 0, ++ ECC_TIMEOUT); ++} ++ ++static int mtk_ecc_wait_idle(struct mtk_snand *snf, uint32_t reg) ++{ ++ int ret; ++ ++ ret = mtk_ecc_poll(snf, reg, ECC_IDLE); ++ if (ret) { ++ snand_log_ecc(snf->pdev, "ECC engine is busy\n"); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes, ++ uint32_t msg_size) ++{ ++ uint32_t i, val, ecc_msg_bits, ecc_strength; ++ int ret; ++ ++ snf->ecc_soc = &mtk_ecc_socs[snf->soc]; ++ ++ snf->ecc_parity_bits = fls(1 + 8 * msg_size); ++ ecc_strength = max_ecc_bytes * 8 / snf->ecc_parity_bits; ++ ++ for (i = snf->ecc_soc->num_ecc_cap - 1; i >= 0; i--) { ++ if (snf->ecc_soc->ecc_caps[i] <= ecc_strength) ++ break; ++ } ++ ++ if (unlikely(i < 0)) { ++ snand_log_ecc(snf->pdev, "Page size %u+%u is not supported\n", ++ snf->writesize, snf->oobsize); ++ return -ENOTSUPP; ++ } ++ ++ snf->ecc_strength = snf->ecc_soc->ecc_caps[i]; ++ snf->ecc_bytes = DIV_ROUND_UP(snf->ecc_strength * snf->ecc_parity_bits, ++ 8); ++ ++ /* Encoder config */ ++ ecc_write16(snf, ECC_ENCCON, 0); ++ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ if (ret) ++ return ret; ++ ++ ecc_msg_bits = msg_size * 8; ++ val = (ecc_msg_bits << ENC_MS_S) | ++ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; ++ ecc_write32(snf, ECC_ENCCNFG, val); ++ ++ /* Decoder config */ ++ ecc_write16(snf, ECC_DECCON, 0); ++ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ if (ret) ++ return ret; ++ ++ ecc_msg_bits += snf->ecc_strength * snf->ecc_parity_bits; ++ val = DEC_EMPTY_EN | (ecc_msg_bits << DEC_CS_S) | ++ (DEC_CON_CORRECT << DEC_CON_S) | ++ (ECC_MODE_NFI << snf->ecc_soc->mode_shift) | i; ++ ecc_write32(snf, ECC_DECCNFG, val); ++ ++ return 0; ++} ++ ++int mtk_snand_ecc_encoder_start(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ if (ret) { ++ ecc_write16(snf, ECC_ENCCON, 0); ++ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ } ++ ++ ecc_write16(snf, ECC_ENCCON, ENC_EN); ++ ++ return 0; ++} ++ ++void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf) ++{ ++ mtk_ecc_wait_idle(snf, ECC_ENCIDLE); ++ ecc_write16(snf, ECC_ENCCON, 0); ++} ++ ++int mtk_snand_ecc_decoder_start(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ if (ret) { ++ ecc_write16(snf, ECC_DECCON, 0); ++ mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ } ++ ++ ecc_write16(snf, ECC_DECCON, DEC_EN); ++ ++ return 0; ++} ++ ++void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf) ++{ ++ mtk_ecc_wait_idle(snf, ECC_DECIDLE); ++ ecc_write16(snf, ECC_DECCON, 0); ++} ++ ++int mtk_ecc_wait_decoder_done(struct mtk_snand *snf) ++{ ++ uint16_t val, step_mask = (1 << snf->ecc_steps) - 1; ++ uint32_t reg = snf->ecc_soc->regs[ECC_DECDONE]; ++ int ret; ++ ++ ret = read16_poll_timeout(snf->ecc_base + reg, val, ++ (val & step_mask) == step_mask, 0, ++ ECC_TIMEOUT); ++ if (ret) ++ snand_log_ecc(snf->pdev, "ECC decoder is busy\n"); ++ ++ return ret; ++} ++ ++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; ++ uint32_t errnum_mask = (1 << snf->ecc_soc->errnum_bits) - 1; ++ int ret = 0; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ regi = i / 4; ++ fi = i % 4; ++ ++ errnum = ecc_read32(snf, ECC_DECENUM(regi)); ++ errnum = (errnum >> (fi * errnum_shift)) & errnum_mask; ++ ++ if (errnum <= snf->ecc_strength) { ++ snf->sect_bf[i] = errnum; ++ } else { ++ snf->sect_bf[i] = -1; ++ 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 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx); ++static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx); ++ ++#define SNAND_MEMORG_512M_2K_64 SNAND_MEMORG(2048, 64, 64, 512, 1, 1) ++#define SNAND_MEMORG_1G_2K_64 SNAND_MEMORG(2048, 64, 64, 1024, 1, 1) ++#define SNAND_MEMORG_2G_2K_64 SNAND_MEMORG(2048, 64, 64, 2048, 1, 1) ++#define SNAND_MEMORG_2G_2K_120 SNAND_MEMORG(2048, 120, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_2K_64 SNAND_MEMORG(2048, 64, 64, 4096, 1, 1) ++#define SNAND_MEMORG_1G_2K_120 SNAND_MEMORG(2048, 120, 64, 1024, 1, 1) ++#define SNAND_MEMORG_1G_2K_128 SNAND_MEMORG(2048, 128, 64, 1024, 1, 1) ++#define SNAND_MEMORG_2G_2K_128 SNAND_MEMORG(2048, 128, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_2K_128 SNAND_MEMORG(2048, 128, 64, 4096, 1, 1) ++#define SNAND_MEMORG_4G_4K_240 SNAND_MEMORG(4096, 240, 64, 2048, 1, 1) ++#define SNAND_MEMORG_4G_4K_256 SNAND_MEMORG(4096, 256, 64, 2048, 1, 1) ++#define SNAND_MEMORG_8G_4K_256 SNAND_MEMORG(4096, 256, 64, 4096, 1, 1) ++#define SNAND_MEMORG_2G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 2048, 2, 1) ++#define SNAND_MEMORG_2G_2K_64_2D SNAND_MEMORG(2048, 64, 64, 1024, 1, 2) ++#define SNAND_MEMORG_2G_2K_128_2P SNAND_MEMORG(2048, 128, 64, 2048, 2, 1) ++#define SNAND_MEMORG_4G_2K_64_2P SNAND_MEMORG(2048, 64, 64, 4096, 2, 1) ++#define SNAND_MEMORG_4G_2K_128_2P_2D SNAND_MEMORG(2048, 128, 64, 2048, 2, 2) ++#define SNAND_MEMORG_8G_4K_256_2D SNAND_MEMORG(4096, 256, 64, 2048, 1, 2) ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 4)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_q2d, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 4), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 2)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_quad_a8d, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2 | SPI_IO_1_1_4 | ++ SPI_IO_1_4_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_2_2, SNAND_CMD_READ_FROM_CACHE_DUAL, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8), ++ SNAND_OP(SNAND_IO_1_4_4, SNAND_CMD_READ_FROM_CACHE_QUAD, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_x4, ++ SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_2, SNAND_CMD_READ_FROM_CACHE_X2, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_read_from_cache_x4_only, ++ SPI_IO_1_1_1 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_READ_FROM_CACHE, 8), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_READ_FROM_CACHE_X4, 8)); ++ ++static const SNAND_IO_CAP(snand_cap_program_load_x1, ++ SPI_IO_1_1_1, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0)); ++ ++static const SNAND_IO_CAP(snand_cap_program_load_x4, ++ SPI_IO_1_1_1 | SPI_IO_1_1_4, ++ SNAND_OP(SNAND_IO_1_1_1, SNAND_CMD_PROGRAM_LOAD, 0), ++ SNAND_OP(SNAND_IO_1_1_4, SNAND_CMD_PROGRAM_LOAD_X4, 0)); ++ ++static const struct snand_flash_info snand_flash_ids[] = { ++ SNAND_INFO("W25N512GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x20), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("W25N01GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("W25M02GV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xab, 0x21), ++ SNAND_MEMORG_2G_2K_64_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_winbond_select_die), ++ SNAND_INFO("W25N02KV", SNAND_ID(SNAND_ID_DYMMY, 0xef, 0xaa, 0x22), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("GD5F1GQ4UAWxx", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x10), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd1), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd9), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F1GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf1), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ4UExIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd2), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ5UExxH", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0x32), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_a8d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf2), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4UBxIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xd4), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4xAYIG", SNAND_ID(SNAND_ID_ADDR, 0xc8, 0xf4), ++ SNAND_MEMORG_4G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F2GQ5UExxG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x52), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("GD5F4GQ4UCxIG", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0xb4), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("MX35LF1GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF1G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x14), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX31LF1GE4BC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x1e), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2GE4AB", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x22), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x24), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x26), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF2G14AC", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x20), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF4G24AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x35), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MX35LF4GE4AD", SNAND_ID(SNAND_ID_DYMMY, 0xc2, 0x37), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("MT29F1G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F1G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x14), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F2G01AAAED", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x9f), ++ SNAND_MEMORG_2G_2K_64_2P, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F2G01ABAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x24), ++ SNAND_MEMORG_2G_2K_128_2P, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F4G01AAADD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x32), ++ SNAND_MEMORG_4G_2K_64_2P, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("MT29F4G01ABAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x34), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("MT29F4G01ADAGD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x36), ++ SNAND_MEMORG_4G_2K_128_2P_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_micron_select_die), ++ SNAND_INFO("MT29F8G01ADAFD", SNAND_ID(SNAND_ID_DYMMY, 0x2c, 0x46), ++ SNAND_MEMORG_8G_4K_256_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_micron_select_die), ++ ++ SNAND_INFO("TC58CVG0S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xc2), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG1S3HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcb), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG2S0HRAIG", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xcd), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x1), ++ SNAND_INFO("TC58CVG0S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe2), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TC58CVG1S3HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xeb), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TC58CVG2S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xed), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("TH58CVG3S0HRAIJ", SNAND_ID(SNAND_ID_DYMMY, 0x98, 0xe4), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("F50L512M41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x20), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L1G41A", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L1G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x01), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("F50L2G41LB", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x0a), ++ SNAND_MEMORG_2G_2K_64_2D, ++ &snand_cap_read_from_cache_quad, ++ &snand_cap_program_load_x4, ++ mtk_snand_winbond_select_die), ++ ++ SNAND_INFO("CS11G0T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x00), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G0G0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x10), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G0S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x20), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G1T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x01), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G1S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x21), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G2T0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x02), ++ SNAND_MEMORG_4G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("CS11G2S0A0AA", SNAND_ID(SNAND_ID_DYMMY, 0x6b, 0x22), ++ SNAND_MEMORG_4G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("EM73B044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x01), ++ SNAND_MEMORG_512M_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x11), ++ SNAND_MEMORG_1G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x09), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x18), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x19), ++ SNAND_MEMORG(2048, 64, 128, 512, 1, 1), ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1c), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1e), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x22), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044VCF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x25), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x31), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNC", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0a), ++ SNAND_MEMORG_2G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x12), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNF", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x10), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x13), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x14), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCD", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x17), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCH", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1b), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1d), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x1f), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x20), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCL", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2e), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x32), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x03), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SND", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0b), ++ SNAND_MEMORG_4G_4K_240, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x23), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2c), ++ SNAND_MEMORG_4G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044VCB", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2f), ++ SNAND_MEMORG_4G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73F044SNA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x24), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73F044VCA", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x2d), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73E044SNE", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0e), ++ SNAND_MEMORG_8G_4K_256, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73C044SNG", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0c), ++ SNAND_MEMORG_1G_2K_120, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("EM73D044VCN", SNAND_ID(SNAND_ID_DYMMY, 0xd5, 0x0f), ++ SNAND_MEMORG_2G_2K_64, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("FM35Q1GA", SNAND_ID(SNAND_ID_DYMMY, 0xe5, 0x71), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("PN26G01A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe1), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("PN26G02A", SNAND_ID(SNAND_ID_DYMMY, 0xa1, 0xe2), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("IS37SML01G1", SNAND_ID(SNAND_ID_DYMMY, 0xc8, 0x21), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("ATO25D1GA", SNAND_ID(SNAND_ID_DYMMY, 0x9b, 0x12), ++ SNAND_MEMORG_1G_2K_64, ++ &snand_cap_read_from_cache_x4_only, ++ &snand_cap_program_load_x4), ++ ++ SNAND_INFO("HYF1GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x51), ++ SNAND_MEMORG_1G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++ SNAND_INFO("HYF2GQ4U", SNAND_ID(SNAND_ID_DYMMY, 0xc9, 0x52), ++ SNAND_MEMORG_2G_2K_128, ++ &snand_cap_read_from_cache_quad_q2d, ++ &snand_cap_program_load_x4), ++}; ++ ++static int mtk_snand_winbond_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ uint8_t op[2]; ++ ++ if (dieidx > 1) { ++ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); ++ return -EINVAL; ++ } ++ ++ op[0] = SNAND_CMD_WINBOND_SELECT_DIE; ++ op[1] = (uint8_t)dieidx; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static int mtk_snand_micron_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ int ret; ++ ++ if (dieidx > 1) { ++ snand_log_chip(snf->pdev, "Invalid die index %u\n", dieidx); ++ return -EINVAL; ++ } ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_MICRON_DIE_ADDR, ++ SNAND_MICRON_DIE_SEL_1); ++ if (ret) { ++ snand_log_chip(snf->pdev, ++ "Failed to set die selection feature\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++const struct snand_flash_info *snand_flash_id_lookup(enum snand_id_type type, ++ const uint8_t *id) ++{ ++ const struct snand_id *fid; ++ uint32_t i; ++ ++ for (i = 0; i < ARRAY_SIZE(snand_flash_ids); i++) { ++ if (snand_flash_ids[i].id.type != type) ++ continue; ++ ++ fid = &snand_flash_ids[i].id; ++ if (memcmp(fid->id, id, fid->len)) ++ continue; ++ ++ return &snand_flash_ids[i]; ++ } ++ ++ return NULL; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-mtd.c +@@ -0,0 +1,535 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mtk-snand.h" ++ ++struct mtk_snand_mtd { ++ struct udevice *dev; ++ struct mtk_snand *snf; ++ struct mtk_snand_chip_info cinfo; ++ uint8_t *page_cache; ++}; ++ ++static const char snand_mtd_name_prefix[] = "spi-nand"; ++ ++static u32 snandidx; ++ ++static inline struct mtk_snand_mtd *mtd_to_msm(struct mtd_info *mtd) ++{ ++ return mtd->priv; ++} ++ ++static int mtk_snand_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ u64 start_addr, end_addr; ++ int ret; ++ ++ /* Do not allow write past end of device */ ++ if ((instr->addr + instr->len) > mtd->size) { ++ pr_debug("%s: attempt to erase beyond end of device\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ start_addr = instr->addr & (~mtd->erasesize_mask); ++ end_addr = instr->addr + instr->len; ++ if (end_addr & mtd->erasesize_mask) { ++ end_addr = (end_addr + mtd->erasesize_mask) & ++ (~mtd->erasesize_mask); ++ } ++ ++ instr->state = MTD_ERASING; ++ ++ while (start_addr < end_addr) { ++ WATCHDOG_RESET(); ++ ++ if (mtk_snand_block_isbad(msm->snf, start_addr)) { ++ if (!instr->scrub) { ++ instr->fail_addr = start_addr; ++ ret = -EIO; ++ break; ++ } ++ } ++ ++ ret = mtk_snand_erase_block(msm->snf, start_addr); ++ if (ret) { ++ instr->fail_addr = start_addr; ++ break; ++ } ++ ++ start_addr += mtd->erasesize; ++ } ++ ++ if (!ret) { ++ instr->state = MTD_ERASE_DONE; ++ } else { ++ instr->state = MTD_ERASE_FAILED; ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_read_data(struct mtk_snand_mtd *msm, uint64_t addr, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); ++ size_t len, ooblen, maxooblen, chklen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ 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; ++ maxooblen = mtd_oobavail(mtd, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? msm->page_cache : NULL; ++ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ret = mtk_snand_read_page_auto_oob(msm->snf, addr, ++ datcache, oobcache, maxooblen, NULL, raw); ++ else ++ ret = mtk_snand_read_page(msm->snf, addr, datcache, ++ oobcache, raw); ++ ++ 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; ++ 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 += mtd->writesize; ++ } ++ ++ return ecc_failed ? -EBADMSG : max_bitflips; ++} ++ ++static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ 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: ++ case MTD_OPS_AUTO_OOB: ++ case MTD_OPS_RAW: ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); ++ return -EINVAL; ++ } ++ ++ 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 (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 mtk_snand_mtd_read_data(msm, from, ops); ++} ++ ++static int mtk_snand_mtd_write_data(struct mtk_snand_mtd *msm, uint64_t addr, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtd_info *mtd = dev_get_uclass_priv(msm->dev); ++ size_t len, ooblen, maxooblen, chklen, oobwrlen; ++ uint32_t col, ooboffs; ++ uint8_t *datcache, *oobcache; ++ bool raw = ops->mode == MTD_OPS_RAW ? true : false; ++ int ret; ++ ++ col = addr & mtd->writesize_mask; ++ addr &= ~mtd->writesize_mask; ++ maxooblen = mtd_oobavail(mtd, ops); ++ ooboffs = ops->ooboffs; ++ ooblen = ops->ooblen; ++ len = ops->len; ++ ++ datcache = len ? msm->page_cache : NULL; ++ oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL; ++ ++ ops->oobretlen = 0; ++ ops->retlen = 0; ++ ++ while (len || ooblen) { ++ WATCHDOG_RESET(); ++ ++ if (len) { ++ /* Move data */ ++ chklen = mtd->writesize - col; ++ if (chklen > len) ++ chklen = len; ++ ++ memset(datcache, 0xff, col); ++ memcpy(datcache + col, ops->datbuf + ops->retlen, ++ chklen); ++ memset(datcache + col + chklen, 0xff, ++ mtd->writesize - col - chklen); ++ len -= chklen; ++ col = 0; /* (col + chklen) % */ ++ ops->retlen += chklen; ++ } ++ ++ oobwrlen = 0; ++ 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, ++ mtd->oobsize - ooboffs - chklen); ++ oobwrlen = chklen + ooboffs; ++ ooblen -= chklen; ++ ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */ ++ ops->oobretlen += chklen; ++ } ++ ++ if (ops->mode == MTD_OPS_AUTO_OOB) ++ ret = mtk_snand_write_page_auto_oob(msm->snf, addr, ++ datcache, oobcache, oobwrlen, NULL, raw); ++ else ++ ret = mtk_snand_write_page(msm->snf, addr, datcache, ++ oobcache, raw); ++ ++ if (ret) ++ return ret; ++ ++ addr += mtd->writesize; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_mtd_write_oob(struct mtd_info *mtd, loff_t to, ++ struct mtd_oob_ops *ops) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ 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: ++ case MTD_OPS_AUTO_OOB: ++ case MTD_OPS_RAW: ++ break; ++ default: ++ pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode); ++ return -EINVAL; ++ } ++ ++ 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 (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 mtk_snand_mtd_write_data(msm, to, ops); ++} ++ ++static int mtk_snand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .datbuf = buf, ++ .len = len, ++ }; ++ int ret; ++ ++ ret = mtk_snand_mtd_read_oob(mtd, from, &ops); ++ ++ if (retlen) ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct mtd_oob_ops ops = { ++ .mode = MTD_OPS_PLACE_OOB, ++ .datbuf = (void *)buf, ++ .len = len, ++ }; ++ int ret; ++ ++ ret = mtk_snand_mtd_write_oob(mtd, to, &ops); ++ ++ if (retlen) ++ *retlen = ops.retlen; ++ ++ return ret; ++} ++ ++static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ return mtk_snand_block_isbad(msm->snf, offs); ++} ++ ++static int mtk_snand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ return mtk_snand_block_markbad(msm->snf, offs); ++} ++ ++static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobecc) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ if (section) ++ return -ERANGE; ++ ++ oobecc->offset = msm->cinfo.fdm_size * msm->cinfo.num_sectors; ++ oobecc->length = mtd->oobsize - oobecc->offset; ++ ++ return 0; ++} ++ ++static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobfree) ++{ ++ struct mtk_snand_mtd *msm = mtd_to_msm(mtd); ++ ++ if (section >= msm->cinfo.num_sectors) ++ return -ERANGE; ++ ++ oobfree->length = msm->cinfo.fdm_size - 1; ++ oobfree->offset = section * msm->cinfo.fdm_size + 1; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops mtk_snand_ooblayout = { ++ .ecc = mtk_snand_ooblayout_ecc, ++ .rfree = mtk_snand_ooblayout_free, ++}; ++ ++static int mtk_snand_mtd_probe(struct udevice *dev) ++{ ++ struct mtk_snand_mtd *msm = dev_get_priv(dev); ++ struct mtd_info *mtd = dev_get_uclass_priv(dev); ++ struct mtk_snand_platdata mtk_snand_pdata = {}; ++ size_t namelen; ++ fdt_addr_t base; ++ int ret; ++ ++ base = dev_read_addr_name(dev, "nfi"); ++ if (base == FDT_ADDR_T_NONE) ++ return -EINVAL; ++ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); ++ ++ base = dev_read_addr_name(dev, "ecc"); ++ if (base == FDT_ADDR_T_NONE) ++ return -EINVAL; ++ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); ++ ++ mtk_snand_pdata.soc = dev_get_driver_data(dev); ++ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); ++ ++ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &msm->snf); ++ if (ret) ++ return ret; ++ ++ mtk_snand_get_chip_info(msm->snf, &msm->cinfo); ++ ++ msm->page_cache = malloc(msm->cinfo.pagesize + msm->cinfo.sparesize); ++ if (!msm->page_cache) { ++ printf("%s: failed to allocate memory for page cache\n", ++ __func__); ++ ret = -ENOMEM; ++ goto errout1; ++ } ++ ++ namelen = sizeof(snand_mtd_name_prefix) + 12; ++ ++ mtd->name = malloc(namelen); ++ if (!mtd->name) { ++ printf("%s: failed to allocate memory for MTD name\n", ++ __func__); ++ ret = -ENOMEM; ++ goto errout2; ++ } ++ ++ msm->dev = dev; ++ ++ snprintf(mtd->name, namelen, "%s%u", snand_mtd_name_prefix, snandidx++); ++ ++ mtd->priv = msm; ++ mtd->dev = dev; ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ ++ mtd->size = msm->cinfo.chipsize; ++ mtd->erasesize = msm->cinfo.blocksize; ++ mtd->writesize = msm->cinfo.pagesize; ++ mtd->writebufsize = mtd->writesize; ++ mtd->oobsize = msm->cinfo.sparesize; ++ mtd->oobavail = msm->cinfo.num_sectors * (msm->cinfo.fdm_size - 1); ++ ++ mtd->ooblayout = &mtk_snand_ooblayout; ++ ++ mtd->ecc_strength = msm->cinfo.ecc_strength; ++ mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4; ++ mtd->ecc_step_size = msm->cinfo.sector_size; ++ ++ mtd->_read = mtk_snand_mtd_read; ++ mtd->_write = mtk_snand_mtd_write; ++ mtd->_erase = mtk_snand_mtd_erase; ++ mtd->_read_oob = mtk_snand_mtd_read_oob; ++ mtd->_write_oob = mtk_snand_mtd_write_oob; ++ mtd->_block_isbad = mtk_snand_mtd_block_isbad; ++ mtd->_block_markbad = mtk_snand_mtd_block_markbad; ++ ++ ret = add_mtd_device(mtd); ++ if (ret) { ++ printf("%s: failed to add SPI-NAND MTD device\n", __func__); ++ ret = -ENODEV; ++ goto errout3; ++ } ++ ++ printf("SPI-NAND: %s (%lluMB)\n", msm->cinfo.model, ++ msm->cinfo.chipsize >> 20); ++ ++ return 0; ++ ++errout3: ++ free(mtd->name); ++ ++errout2: ++ free(msm->page_cache); ++ ++errout1: ++ mtk_snand_cleanup(msm->snf); ++ ++ return ret; ++} ++ ++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 */ }, ++}; ++ ++U_BOOT_DRIVER(spinand) = { ++ .name = "mtk-snand", ++ .id = UCLASS_MTD, ++ .of_match = mtk_snand_ids, ++ .priv_auto = sizeof(struct mtk_snand_mtd), ++ .probe = mtk_snand_mtd_probe, ++}; +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-os.c +@@ -0,0 +1,39 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++int mtk_snand_log(struct mtk_snand_plat_dev *pdev, ++ enum mtk_snand_log_category cat, const char *fmt, ...) ++{ ++ const char *catname = ""; ++ va_list ap; ++ int ret; ++ ++ switch (cat) { ++ case SNAND_LOG_NFI: ++ catname = "NFI: "; ++ break; ++ case SNAND_LOG_SNFI: ++ catname = "SNFI: "; ++ break; ++ case SNAND_LOG_ECC: ++ catname = "ECC: "; ++ break; ++ default: ++ break; ++ } ++ ++ puts("SPI-NAND: "); ++ puts(catname); ++ ++ va_start(ap, fmt); ++ ret = vprintf(fmt, ap); ++ va_end(ap); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-os.h +@@ -0,0 +1,120 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_OS_H_ ++#define _MTK_SNAND_OS_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef ARCH_DMA_MINALIGN ++#define ARCH_DMA_MINALIGN 64 ++#endif ++ ++struct mtk_snand_plat_dev { ++ ulong unused; ++}; ++ ++/* Polling helpers */ ++#define read16_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++ readw_poll_timeout((addr), (val), (cond), (timeout_us)) ++ ++#define read32_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ ++ readl_poll_timeout((addr), (val), (cond), (timeout_us)) ++ ++/* Timer helpers */ ++typedef uint64_t mtk_snand_time_t; ++ ++static inline mtk_snand_time_t timer_get_ticks(void) ++{ ++ return get_ticks(); ++} ++ ++static inline mtk_snand_time_t timer_time_to_tick(uint32_t timeout_us) ++{ ++ return usec_to_tick(timeout_us); ++} ++ ++static inline bool timer_is_timeout(mtk_snand_time_t start_tick, ++ mtk_snand_time_t timeout_tick) ++{ ++ return get_ticks() - start_tick > timeout_tick; ++} ++ ++/* Memory helpers */ ++static inline void *generic_mem_alloc(struct mtk_snand_plat_dev *pdev, ++ size_t size) ++{ ++ return calloc(1, size); ++} ++ ++static inline void generic_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) ++{ ++ free(ptr); ++} ++ ++static inline void *dma_mem_alloc(struct mtk_snand_plat_dev *pdev, size_t size) ++{ ++ return memalign(ARCH_DMA_MINALIGN, size); ++} ++ ++static inline void dma_mem_free(struct mtk_snand_plat_dev *pdev, void *ptr) ++{ ++ free(ptr); ++} ++ ++static inline int dma_mem_map(struct mtk_snand_plat_dev *pdev, void *vaddr, ++ uintptr_t *dma_addr, size_t size, bool to_device) ++{ ++ size_t cachelen = roundup(size, ARCH_DMA_MINALIGN); ++ uintptr_t endaddr = (uintptr_t)vaddr + cachelen; ++ ++ if (to_device) ++ flush_dcache_range((uintptr_t)vaddr, endaddr); ++ else ++ invalidate_dcache_range((uintptr_t)vaddr, endaddr); ++ ++ *dma_addr = (uintptr_t)vaddr; ++ ++ return 0; ++} ++ ++static inline void dma_mem_unmap(struct mtk_snand_plat_dev *pdev, ++ uintptr_t dma_addr, size_t size, ++ bool to_device) ++{ ++} ++ ++/* Interrupt helpers */ ++static inline void irq_completion_done(struct mtk_snand_plat_dev *pdev) ++{ ++} ++ ++static inline void irq_completion_init(struct mtk_snand_plat_dev *pdev) ++{ ++} ++ ++static inline int irq_completion_wait(struct mtk_snand_plat_dev *pdev, ++ void __iomem *reg, uint32_t bit, ++ uint32_t timeout_us) ++{ ++ uint32_t val; ++ ++ return read32_poll_timeout(reg, val, val & bit, 0, timeout_us); ++} ++ ++#endif /* _MTK_SNAND_OS_H_ */ +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand.c +@@ -0,0 +1,1933 @@ ++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include "mtk-snand-def.h" ++ ++/* NFI registers */ ++#define NFI_CNFG 0x000 ++#define CNFG_OP_MODE_S 12 ++#define CNFG_OP_MODE_CUST 6 ++#define CNFG_OP_MODE_PROGRAM 3 ++#define CNFG_AUTO_FMT_EN BIT(9) ++#define CNFG_HW_ECC_EN BIT(8) ++#define CNFG_DMA_BURST_EN BIT(2) ++#define CNFG_READ_MODE BIT(1) ++#define CNFG_DMA_MODE BIT(0) ++ ++#define NFI_PAGEFMT 0x0004 ++#define NFI_SPARE_SIZE_LS_S 16 ++#define NFI_FDM_ECC_NUM_S 12 ++#define NFI_FDM_NUM_S 8 ++#define NFI_SPARE_SIZE_S 4 ++#define NFI_SEC_SEL_512 BIT(2) ++#define NFI_PAGE_SIZE_S 0 ++#define NFI_PAGE_SIZE_512_2K 0 ++#define NFI_PAGE_SIZE_2K_4K 1 ++#define NFI_PAGE_SIZE_4K_8K 2 ++#define NFI_PAGE_SIZE_8K_16K 3 ++ ++#define NFI_CON 0x008 ++#define CON_SEC_NUM_S 12 ++#define CON_BWR BIT(9) ++#define CON_BRD BIT(8) ++#define CON_NFI_RST BIT(1) ++#define CON_FIFO_FLUSH BIT(0) ++ ++#define NFI_INTR_EN 0x010 ++#define NFI_INTR_STA 0x014 ++#define NFI_IRQ_INTR_EN BIT(31) ++#define NFI_IRQ_CUS_READ BIT(8) ++#define NFI_IRQ_CUS_PG BIT(7) ++ ++#define NFI_CMD 0x020 ++ ++#define NFI_STRDATA 0x040 ++#define STR_DATA BIT(0) ++ ++#define NFI_STA 0x060 ++#define NFI_NAND_FSM GENMASK(28, 24) ++#define NFI_FSM GENMASK(19, 16) ++#define READ_EMPTY BIT(12) ++ ++#define NFI_FIFOSTA 0x064 ++#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) ++#define NFI_FDMM(n) (NFI_FDM0M + (n) * 8) ++ ++#define NFI_DEBUG_CON1 0x220 ++#define WBUF_EN BIT(2) ++ ++#define NFI_MASTERSTA 0x224 ++#define MAS_ADDR GENMASK(11, 9) ++#define MAS_RD GENMASK(8, 6) ++#define MAS_WR GENMASK(5, 3) ++#define MAS_RDDLY GENMASK(2, 0) ++#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 */ ++#define SNF_MAC_CTL 0x500 ++#define MAC_XIO_SEL BIT(4) ++#define SF_MAC_EN BIT(3) ++#define SF_TRIG BIT(2) ++#define WIP_READY BIT(1) ++#define WIP BIT(0) ++ ++#define SNF_MAC_OUTL 0x504 ++#define SNF_MAC_INL 0x508 ++ ++#define SNF_RD_CTL2 0x510 ++#define DATA_READ_DUMMY_S 8 ++#define DATA_READ_CMD_S 0 ++ ++#define SNF_RD_CTL3 0x514 ++ ++#define SNF_PG_CTL1 0x524 ++#define PG_LOAD_CMD_S 8 ++ ++#define SNF_PG_CTL2 0x528 ++ ++#define SNF_MISC_CTL 0x538 ++#define SW_RST BIT(28) ++#define FIFO_RD_LTC_S 25 ++#define PG_LOAD_X4_EN BIT(20) ++#define DATA_READ_MODE_S 16 ++#define DATA_READ_MODE GENMASK(18, 16) ++#define DATA_READ_MODE_X1 0 ++#define DATA_READ_MODE_X2 1 ++#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 ++ ++#define SNF_MISC_CTL2 0x53c ++#define PROGRAM_LOAD_BYTE_NUM_S 16 ++#define READ_DATA_BYTE_NUM_S 11 ++ ++#define SNF_DLY_CTL3 0x548 ++#define SFCK_SAM_DLY_S 0 ++ ++#define SNF_STA_CTL1 0x550 ++#define CUS_PG_DONE BIT(28) ++#define CUS_READ_DONE BIT(27) ++#define SPI_STATE_S 0 ++#define SPI_STATE GENMASK(3, 0) ++ ++#define SNF_CFG 0x55c ++#define SPI_MODE BIT(0) ++ ++#define SNF_GPRAM 0x800 ++#define SNF_GPRAM_SIZE 0xa0 ++ ++#define SNFI_POLL_INTERVAL 1000000 ++ ++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 ++}; ++ ++static const struct mtk_snand_soc_data mtk_snand_socs[__SNAND_SOC_MAX] = { ++ [SNAND_SOC_MT7622] = { ++ .sector_size = 512, ++ .max_sectors = 8, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 32, ++ .bbm_swap = false, ++ .empty_page_check = false, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7622, ++ .spare_sizes = mt7622_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 ++ }, ++ [SNAND_SOC_MT7629] = { ++ .sector_size = 512, ++ .max_sectors = 8, ++ .fdm_size = 8, ++ .fdm_ecc_size = 1, ++ .fifo_size = 32, ++ .bbm_swap = true, ++ .empty_page_check = false, ++ .mastersta_mask = NFI_MASTERSTA_MASK_7622, ++ .spare_sizes = 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, ++ .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_7986, ++ .spare_sizes = mt7986_spare_sizes, ++ .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes), ++ .latch_lat = 0, ++ .sample_delay = 40 ++ }, ++}; ++ ++static inline uint32_t nfi_read32(struct mtk_snand *snf, uint32_t reg) ++{ ++ return readl(snf->nfi_base + reg); ++} ++ ++static inline void nfi_write32(struct mtk_snand *snf, uint32_t reg, ++ uint32_t val) ++{ ++ writel(val, snf->nfi_base + reg); ++} ++ ++static inline void nfi_write16(struct mtk_snand *snf, uint32_t reg, ++ uint16_t val) ++{ ++ writew(val, snf->nfi_base + reg); ++} ++ ++static inline void nfi_rmw32(struct mtk_snand *snf, uint32_t reg, uint32_t clr, ++ uint32_t set) ++{ ++ uint32_t val; ++ ++ val = readl(snf->nfi_base + reg); ++ val &= ~clr; ++ val |= set; ++ writel(val, snf->nfi_base + reg); ++} ++ ++static void nfi_write_data(struct mtk_snand *snf, uint32_t reg, ++ const uint8_t *data, uint32_t len) ++{ ++ uint32_t i, val = 0, es = sizeof(uint32_t); ++ ++ for (i = reg; i < reg + len; i++) { ++ val |= ((uint32_t)*data++) << (8 * (i % es)); ++ ++ if (i % es == es - 1 || i == reg + len - 1) { ++ nfi_write32(snf, i & ~(es - 1), val); ++ val = 0; ++ } ++ } ++} ++ ++static void nfi_read_data(struct mtk_snand *snf, uint32_t reg, uint8_t *data, ++ uint32_t len) ++{ ++ uint32_t i, val = 0, es = sizeof(uint32_t); ++ ++ for (i = reg; i < reg + len; i++) { ++ if (i == reg || i % es == 0) ++ val = nfi_read32(snf, i & ~(es - 1)); ++ ++ *data++ = (uint8_t)(val >> (8 * (i % es))); ++ } ++} ++ ++static inline void do_bm_swap(uint8_t *bm1, uint8_t *bm2) ++{ ++ uint8_t tmp = *bm1; ++ *bm1 = *bm2; ++ *bm2 = tmp; ++} ++ ++static void mtk_snand_bm_swap_raw(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos = (snf->ecc_steps - 1) * snf->raw_sector_size + ++ snf->nfi_soc->sector_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos], ++ &snf->page_cache[snf->writesize]); ++} ++ ++static void mtk_snand_bm_swap(struct mtk_snand *snf) ++{ ++ uint32_t buf_bbm_pos, fdm_bbm_pos; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ buf_bbm_pos = snf->writesize - ++ (snf->ecc_steps - 1) * snf->spare_per_sector; ++ fdm_bbm_pos = snf->writesize + ++ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos], ++ &snf->page_cache[buf_bbm_pos]); ++} ++ ++static void mtk_snand_fdm_bm_swap_raw(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos1 = snf->nfi_soc->sector_size; ++ fdm_bbm_pos2 = (snf->ecc_steps - 1) * snf->raw_sector_size + ++ snf->nfi_soc->sector_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], ++ &snf->page_cache[fdm_bbm_pos2]); ++} ++ ++static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf) ++{ ++ uint32_t fdm_bbm_pos1, fdm_bbm_pos2; ++ ++ if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1) ++ return; ++ ++ fdm_bbm_pos1 = snf->writesize; ++ fdm_bbm_pos2 = snf->writesize + ++ (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size; ++ do_bm_swap(&snf->page_cache[fdm_bbm_pos1], ++ &snf->page_cache[fdm_bbm_pos2]); ++} ++ ++static int mtk_nfi_reset(struct mtk_snand *snf) ++{ ++ uint32_t val, fifo_mask; ++ int ret; ++ ++ nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST); ++ ++ ret = read16_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, ++ "NFI master is still busy after reset\n"); ++ return ret; ++ } ++ ++ ret = read32_poll_timeout(snf->nfi_base + NFI_STA, val, ++ !(val & (NFI_FSM | NFI_NAND_FSM)), 0, ++ SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, "Failed to reset NFI\n"); ++ return ret; ++ } ++ ++ fifo_mask = ((snf->nfi_soc->fifo_size - 1) << FIFO_RD_REMAIN_S) | ++ ((snf->nfi_soc->fifo_size - 1) << FIFO_WR_REMAIN_S); ++ ret = read16_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val, ++ !(val & fifo_mask), 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, "NFI FIFOs are not empty\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_mac_reset(struct mtk_snand *snf) ++{ ++ int ret; ++ uint32_t val; ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST); ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val, ++ !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL); ++ if (ret) ++ 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) | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); ++ ++ return ret; ++} ++ ++static int mtk_snand_mac_trigger(struct mtk_snand *snf, uint32_t outlen, ++ uint32_t inlen) ++{ ++ int ret; ++ uint32_t val; ++ ++ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN); ++ nfi_write32(snf, SNF_MAC_OUTL, outlen); ++ nfi_write32(snf, SNF_MAC_INL, inlen); ++ ++ nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG); ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, ++ val & WIP_READY, 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_snfi(snf->pdev, "Timed out waiting for WIP_READY\n"); ++ goto cleanup; ++ } ++ ++ ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val, ++ !(val & WIP), 0, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_snfi(snf->pdev, ++ "Timed out waiting for WIP cleared\n"); ++ } ++ ++cleanup: ++ nfi_write32(snf, SNF_MAC_CTL, 0); ++ ++ return ret; ++} ++ ++int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen, ++ uint8_t *in, uint32_t inlen) ++{ ++ int ret; ++ ++ if (outlen + inlen > SNF_GPRAM_SIZE) ++ return -EINVAL; ++ ++ mtk_snand_mac_reset(snf); ++ ++ nfi_write_data(snf, SNF_GPRAM, out, outlen); ++ ++ ret = mtk_snand_mac_trigger(snf, outlen, inlen); ++ if (ret) ++ return ret; ++ ++ if (!inlen) ++ return 0; ++ ++ nfi_read_data(snf, SNF_GPRAM + outlen, in, inlen); ++ ++ return 0; ++} ++ ++static int mtk_snand_get_feature(struct mtk_snand *snf, uint32_t addr) ++{ ++ uint8_t op[2], val; ++ int ret; ++ ++ op[0] = SNAND_CMD_GET_FEATURE; ++ op[1] = (uint8_t)addr; ++ ++ ret = mtk_snand_mac_io(snf, op, sizeof(op), &val, 1); ++ if (ret) ++ return ret; ++ ++ return val; ++} ++ ++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val) ++{ ++ uint8_t op[3]; ++ ++ op[0] = SNAND_CMD_SET_FEATURE; ++ op[1] = (uint8_t)addr; ++ op[2] = (uint8_t)val; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static int mtk_snand_poll_status(struct mtk_snand *snf, uint32_t wait_us) ++{ ++ int val; ++ mtk_snand_time_t time_start, tmo; ++ ++ time_start = timer_get_ticks(); ++ tmo = timer_time_to_tick(wait_us); ++ ++ do { ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); ++ if (!(val & SNAND_STATUS_OIP)) ++ return val & (SNAND_STATUS_ERASE_FAIL | ++ SNAND_STATUS_PROGRAM_FAIL); ++ } while (!timer_is_timeout(time_start, tmo)); ++ ++ return -ETIMEDOUT; ++} ++ ++int mtk_snand_chip_reset(struct mtk_snand *snf) ++{ ++ uint8_t op = SNAND_CMD_RESET; ++ int ret; ++ ++ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mtk_snand_config_feature(struct mtk_snand *snf, uint8_t clr, ++ uint8_t set) ++{ ++ int val, newval; ++ int ret; ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to get configuration feature\n"); ++ return val; ++ } ++ ++ newval = (val & (~clr)) | set; ++ ++ if (newval == val) ++ return 0; ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_CONFIG_ADDR, ++ (uint8_t)newval); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to set configuration feature\n"); ++ return ret; ++ } ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR); ++ if (val < 0) { ++ snand_log_chip(snf->pdev, ++ "Failed to get configuration feature\n"); ++ return val; ++ } ++ ++ if (newval != val) ++ return -ENOTSUPP; ++ ++ return 0; ++} ++ ++static int mtk_snand_ondie_ecc_control(struct mtk_snand *snf, bool enable) ++{ ++ int ret; ++ ++ if (enable) ++ ret = mtk_snand_config_feature(snf, 0, SNAND_FEATURE_ECC_EN); ++ else ++ ret = mtk_snand_config_feature(snf, SNAND_FEATURE_ECC_EN, 0); ++ ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to %s On-Die ECC engine\n", ++ enable ? "enable" : "disable"); ++ } ++ ++ return ret; ++} ++ ++static int mtk_snand_qspi_control(struct mtk_snand *snf, bool enable) ++{ ++ int ret; ++ ++ if (enable) { ++ ret = mtk_snand_config_feature(snf, 0, ++ SNAND_FEATURE_QUAD_ENABLE); ++ } else { ++ ret = mtk_snand_config_feature(snf, ++ SNAND_FEATURE_QUAD_ENABLE, 0); ++ } ++ ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to %s quad spi\n", ++ enable ? "enable" : "disable"); ++ } ++ ++ return ret; ++} ++ ++static int mtk_snand_unlock(struct mtk_snand *snf) ++{ ++ int ret; ++ ++ ret = mtk_snand_set_feature(snf, SNAND_FEATURE_PROTECT_ADDR, 0); ++ if (ret) { ++ snand_log_chip(snf->pdev, "Failed to set protection feature\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_write_enable(struct mtk_snand *snf) ++{ ++ uint8_t op = SNAND_CMD_WRITE_ENABLE; ++ int ret, val; ++ ++ ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0); ++ if (ret) ++ return ret; ++ ++ val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR); ++ if (val < 0) ++ return ret; ++ ++ if (val & SNAND_STATUS_WEL) ++ return 0; ++ ++ snand_log_chip(snf->pdev, "Failed to send write-enable command\n"); ++ ++ return -ENOTSUPP; ++} ++ ++static int mtk_snand_select_die(struct mtk_snand *snf, uint32_t dieidx) ++{ ++ if (!snf->select_die) ++ return 0; ++ ++ return snf->select_die(snf, dieidx); ++} ++ ++static uint64_t mtk_snand_select_die_address(struct mtk_snand *snf, ++ uint64_t addr) ++{ ++ uint32_t dieidx; ++ ++ if (!snf->select_die) ++ return addr; ++ ++ dieidx = addr >> snf->die_shift; ++ ++ mtk_snand_select_die(snf, dieidx); ++ ++ return addr & snf->die_mask; ++} ++ ++static uint32_t mtk_snand_get_plane_address(struct mtk_snand *snf, ++ uint32_t page) ++{ ++ uint32_t pages_per_block; ++ ++ pages_per_block = 1 << (snf->erasesize_shift - snf->writesize_shift); ++ ++ if (page & pages_per_block) ++ return 1 << (snf->writesize_shift + 1); ++ ++ return 0; ++} ++ ++static int mtk_snand_page_op(struct mtk_snand *snf, uint32_t page, uint8_t cmd) ++{ ++ uint8_t op[4]; ++ ++ op[0] = cmd; ++ op[1] = (page >> 16) & 0xff; ++ op[2] = (page >> 8) & 0xff; ++ op[3] = page & 0xff; ++ ++ return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0); ++} ++ ++static void mtk_snand_read_fdm(struct mtk_snand *snf, uint8_t *buf) ++{ ++ uint32_t vall, valm; ++ uint8_t *oobptr = buf; ++ int i, j; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ vall = nfi_read32(snf, NFI_FDML(i)); ++ valm = nfi_read32(snf, NFI_FDMM(i)); ++ ++ for (j = 0; j < snf->nfi_soc->fdm_size; j++) ++ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); ++ ++ oobptr += snf->nfi_soc->fdm_size; ++ } ++} ++ ++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, val; ++ uintptr_t dma_addr; ++ int ret; ++ ++ /* Column address with plane bit */ ++ coladdr = mtk_snand_get_plane_address(snf, page); ++ ++ mtk_snand_mac_reset(snf); ++ mtk_nfi_reset(snf); ++ ++ /* Command and dummy cycles */ ++ nfi_write32(snf, SNF_RD_CTL2, ++ ((uint32_t)snf->dummy_rfc << DATA_READ_DUMMY_S) | ++ (snf->opcode_rfc << DATA_READ_CMD_S)); ++ ++ /* Column address */ ++ nfi_write32(snf, SNF_RD_CTL3, coladdr); ++ ++ /* 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 | (snf->nfi_soc->latch_lat << LATCH_LAT_S)); ++ ++ /* Set bytes to read */ ++ rwbytes = snf->ecc_steps * snf->raw_sector_size; ++ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | ++ rwbytes); ++ ++ /* NFI read prepare */ ++ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; ++ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) | ++ CNFG_DMA_BURST_EN | CNFG_READ_MODE | CNFG_DMA_MODE | mode); ++ ++ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); ++ ++ /* Prepare for DMA read */ ++ len = snf->writesize + snf->oobsize; ++ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, false); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA map from device failed with %d\n", ret); ++ return ret; ++ } ++ ++ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); ++ ++ if (!raw) ++ mtk_snand_ecc_decoder_start(snf); ++ ++ /* Prepare for custom read interrupt */ ++ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ); ++ irq_completion_init(snf->pdev); ++ ++ /* Trigger NFI into custom mode */ ++ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ); ++ ++ /* Start DMA read */ ++ nfi_rmw32(snf, NFI_CON, 0, CON_BRD); ++ nfi_write16(snf, NFI_STRDATA, STR_DATA); ++ ++ /* Wait for operation finished */ ++ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, ++ CUS_READ_DONE, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA timed out for reading from cache\n"); ++ 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) ++ goto cleanup; ++ ++ mtk_snand_read_fdm(snf, snf->page_cache + snf->writesize); ++ ++ mtk_ecc_check_decode_error(snf); ++ mtk_snand_ecc_decoder_stop(snf); ++ ++ ret = mtk_snand_check_ecc_result(snf, page); ++ } ++ ++cleanup: ++ /* DMA cleanup */ ++ dma_mem_unmap(snf->pdev, dma_addr, len, false); ++ ++ /* 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); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Disable interrupt */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN | LATCH_LAT, 0); ++ ++ return ret; ++} ++ ++static void mtk_snand_from_raw_page(struct mtk_snand *snf, void *buf, void *oob) ++{ ++ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; ++ uint8_t *bufptr = buf, *oobptr = oob, *raw_sector; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ raw_sector = snf->page_cache + i * snf->raw_sector_size; ++ ++ if (buf) { ++ memcpy(bufptr, raw_sector, snf->nfi_soc->sector_size); ++ bufptr += snf->nfi_soc->sector_size; ++ } ++ ++ raw_sector += snf->nfi_soc->sector_size; ++ ++ if (oob) { ++ memcpy(oobptr, raw_sector, snf->nfi_soc->fdm_size); ++ oobptr += snf->nfi_soc->fdm_size; ++ raw_sector += snf->nfi_soc->fdm_size; ++ ++ memcpy(eccptr, raw_sector, ecc_bytes); ++ eccptr += ecc_bytes; ++ } ++ } ++} ++ ++static int mtk_snand_do_read_page(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, bool raw, bool format) ++{ ++ uint64_t die_addr; ++ 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; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, "Read to cache command timed out\n"); ++ 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); ++ mtk_snand_fdm_bm_swap_raw(snf); ++ mtk_snand_from_raw_page(snf, buf, oob); ++ } else { ++ if (buf) ++ memcpy(buf, snf->page_cache, snf->writesize); ++ ++ if (oob) { ++ memset(oob, 0xff, snf->oobsize); ++ memcpy(oob, snf->page_cache + snf->writesize, ++ snf->ecc_steps * snf->spare_per_sector); ++ } ++ } ++ } else { ++ mtk_snand_bm_swap(snf); ++ mtk_snand_fdm_bm_swap(snf); ++ ++ if (buf) ++ memcpy(buf, snf->page_cache, snf->writesize); ++ ++ if (oob) { ++ memset(oob, 0xff, snf->oobsize); ++ memcpy(oob, snf->page_cache + snf->writesize, ++ snf->ecc_steps * snf->nfi_soc->fdm_size); ++ } ++ } ++ ++ return ret; ++} ++ ++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, ++ void *oob, bool raw) ++{ ++ if (!snf || (!buf && !oob)) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ return mtk_snand_do_read_page(snf, addr, buf, oob, raw, true); ++} ++ ++static void mtk_snand_write_fdm(struct mtk_snand *snf, const uint8_t *buf) ++{ ++ uint32_t vall, valm, fdm_size = snf->nfi_soc->fdm_size; ++ const uint8_t *oobptr = buf; ++ int i, j; ++ ++ for (i = 0; i < snf->ecc_steps; i++) { ++ vall = 0; ++ valm = 0; ++ ++ for (j = 0; j < 8; j++) { ++ if (j < 4) ++ vall |= (j < fdm_size ? oobptr[j] : 0xff) ++ << (j * 8); ++ else ++ valm |= (j < fdm_size ? oobptr[j] : 0xff) ++ << ((j - 4) * 8); ++ } ++ ++ nfi_write32(snf, NFI_FDML(i), vall); ++ nfi_write32(snf, NFI_FDMM(i), valm); ++ ++ oobptr += fdm_size; ++ } ++} ++ ++static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page, ++ bool raw) ++{ ++ uint32_t coladdr, rwbytes, mode, len, val; ++ uintptr_t dma_addr; ++ int ret; ++ ++ /* Column address with plane bit */ ++ coladdr = mtk_snand_get_plane_address(snf, page); ++ ++ mtk_snand_mac_reset(snf); ++ mtk_nfi_reset(snf); ++ ++ /* Write FDM registers if necessary */ ++ if (!raw) ++ mtk_snand_write_fdm(snf, snf->page_cache + snf->writesize); ++ ++ /* Command */ ++ nfi_write32(snf, SNF_PG_CTL1, (snf->opcode_pl << PG_LOAD_CMD_S)); ++ ++ /* Column address */ ++ nfi_write32(snf, SNF_PG_CTL2, coladdr); ++ ++ /* Set write mode */ ++ mode = snf->mode_pl ? PG_LOAD_X4_EN : 0; ++ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, mode | PG_LOAD_CUSTOM_EN); ++ ++ /* Set bytes to write */ ++ rwbytes = snf->ecc_steps * snf->raw_sector_size; ++ nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) | ++ rwbytes); ++ ++ /* NFI write prepare */ ++ mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN; ++ nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) | ++ CNFG_DMA_BURST_EN | CNFG_DMA_MODE | mode); ++ ++ nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S)); ++ ++ /* Prepare for DMA write */ ++ len = snf->writesize + snf->oobsize; ++ ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, true); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA map to device failed with %d\n", ret); ++ return ret; ++ } ++ ++ nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr); ++ ++ if (!raw) ++ mtk_snand_ecc_encoder_start(snf); ++ ++ /* Prepare for custom write interrupt */ ++ nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG); ++ irq_completion_init(snf->pdev); ++ ++ /* Trigger NFI into custom mode */ ++ nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE); ++ ++ /* Start DMA write */ ++ nfi_rmw32(snf, NFI_CON, 0, CON_BWR); ++ nfi_write16(snf, NFI_STRDATA, STR_DATA); ++ ++ /* Wait for operation finished */ ++ ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1, ++ CUS_PG_DONE, SNFI_POLL_INTERVAL); ++ if (ret) { ++ snand_log_nfi(snf->pdev, ++ "DMA timed out for program load\n"); ++ 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); ++ ++cleanup: ++ /* DMA cleanup */ ++ dma_mem_unmap(snf->pdev, dma_addr, len, true); ++ ++ /* Stop write */ ++ 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); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Disable interrupt */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0); ++ ++ return ret; ++} ++ ++static void mtk_snand_to_raw_page(struct mtk_snand *snf, ++ const void *buf, const void *oob, ++ bool empty_ecc) ++{ ++ uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size; ++ const uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size; ++ const uint8_t *bufptr = buf, *oobptr = oob; ++ uint8_t *raw_sector; ++ ++ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); ++ for (i = 0; i < snf->ecc_steps; i++) { ++ raw_sector = snf->page_cache + i * snf->raw_sector_size; ++ ++ if (buf) { ++ memcpy(raw_sector, bufptr, snf->nfi_soc->sector_size); ++ bufptr += snf->nfi_soc->sector_size; ++ } ++ ++ raw_sector += snf->nfi_soc->sector_size; ++ ++ if (oob) { ++ memcpy(raw_sector, oobptr, snf->nfi_soc->fdm_size); ++ oobptr += snf->nfi_soc->fdm_size; ++ raw_sector += snf->nfi_soc->fdm_size; ++ ++ if (empty_ecc) ++ memset(raw_sector, 0xff, ecc_bytes); ++ else ++ memcpy(raw_sector, eccptr, ecc_bytes); ++ eccptr += ecc_bytes; ++ } ++ } ++} ++ ++static bool mtk_snand_is_empty_page(struct mtk_snand *snf, const void *buf, ++ const void *oob) ++{ ++ const uint8_t *p = buf; ++ uint32_t i, j; ++ ++ if (buf) { ++ for (i = 0; i < snf->writesize; i++) { ++ if (p[i] != 0xff) ++ return false; ++ } ++ } ++ ++ if (oob) { ++ for (j = 0; j < snf->ecc_steps; j++) { ++ p = oob + j * snf->nfi_soc->fdm_size; ++ ++ for (i = 0; i < snf->nfi_soc->fdm_ecc_size; i++) { ++ if (p[i] != 0xff) ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++static int mtk_snand_do_write_page(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ bool raw, bool format) ++{ ++ uint64_t die_addr; ++ bool empty_ecc = false; ++ uint32_t page; ++ int ret; ++ ++ die_addr = mtk_snand_select_die_address(snf, addr); ++ page = die_addr >> snf->writesize_shift; ++ ++ if (!raw && mtk_snand_is_empty_page(snf, buf, oob)) { ++ /* ++ * If the data in the page to be ecc-ed is full 0xff, ++ * change to raw write mode ++ */ ++ raw = true; ++ format = true; ++ ++ /* fill ecc parity code region with 0xff */ ++ empty_ecc = true; ++ } ++ ++ if (raw) { ++ if (format) { ++ mtk_snand_to_raw_page(snf, buf, oob, empty_ecc); ++ mtk_snand_fdm_bm_swap_raw(snf); ++ mtk_snand_bm_swap_raw(snf); ++ } else { ++ memset(snf->page_cache, 0xff, ++ snf->writesize + snf->oobsize); ++ ++ if (buf) ++ memcpy(snf->page_cache, buf, snf->writesize); ++ ++ if (oob) { ++ memcpy(snf->page_cache + snf->writesize, oob, ++ snf->ecc_steps * snf->spare_per_sector); ++ } ++ } ++ } else { ++ memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize); ++ if (buf) ++ memcpy(snf->page_cache, buf, snf->writesize); ++ ++ if (oob) { ++ memcpy(snf->page_cache + snf->writesize, oob, ++ snf->ecc_steps * snf->nfi_soc->fdm_size); ++ } ++ ++ mtk_snand_fdm_bm_swap(snf); ++ mtk_snand_bm_swap(snf); ++ } ++ ++ ret = mtk_snand_write_enable(snf); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_program_load(snf, page, raw); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_page_op(snf, page, SNAND_CMD_PROGRAM_EXECUTE); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, ++ "Page program command timed out on page %u\n", ++ page); ++ return ret; ++ } ++ ++ if (ret & SNAND_STATUS_PROGRAM_FAIL) { ++ snand_log_chip(snf->pdev, ++ "Page program failed on page %u\n", page); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, ++ const void *oob, bool raw) ++{ ++ if (!snf || (!buf && !oob)) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ return mtk_snand_do_write_page(snf, addr, buf, oob, raw, true); ++} ++ ++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr) ++{ ++ uint64_t die_addr; ++ uint32_t page, block; ++ int ret; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ die_addr = mtk_snand_select_die_address(snf, addr); ++ block = die_addr >> snf->erasesize_shift; ++ page = block << (snf->erasesize_shift - snf->writesize_shift); ++ ++ ret = mtk_snand_write_enable(snf); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_page_op(snf, page, SNAND_CMD_BLOCK_ERASE); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL); ++ if (ret < 0) { ++ snand_log_chip(snf->pdev, ++ "Block erase command timed out on block %u\n", ++ block); ++ return ret; ++ } ++ ++ if (ret & SNAND_STATUS_ERASE_FAIL) { ++ snand_log_chip(snf->pdev, ++ "Block erase failed on block %u\n", block); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int mtk_snand_block_isbad_std(struct mtk_snand *snf, uint64_t addr) ++{ ++ int ret; ++ ++ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, ++ false); ++ if (ret && ret != -EBADMSG) ++ return ret; ++ ++ return snf->buf_cache[0] != 0xff; ++} ++ ++static int mtk_snand_block_isbad_mtk(struct mtk_snand *snf, uint64_t addr) ++{ ++ int ret; ++ ++ ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true, ++ true); ++ if (ret && ret != -EBADMSG) ++ return ret; ++ ++ return snf->buf_cache[0] != 0xff; ++} ++ ++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr) ++{ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ addr &= ~snf->erasesize_mask; ++ ++ if (snf->nfi_soc->bbm_swap) ++ return mtk_snand_block_isbad_std(snf, addr); ++ ++ return mtk_snand_block_isbad_mtk(snf, addr); ++} ++ ++static int mtk_snand_block_markbad_std(struct mtk_snand *snf, uint64_t addr) ++{ ++ /* Standard BBM position */ ++ memset(snf->buf_cache, 0xff, snf->oobsize); ++ snf->buf_cache[0] = 0; ++ ++ return mtk_snand_do_write_page(snf, addr, NULL, snf->buf_cache, true, ++ false); ++} ++ ++static int mtk_snand_block_markbad_mtk(struct mtk_snand *snf, uint64_t addr) ++{ ++ /* Write the whole page with zeros */ ++ memset(snf->buf_cache, 0, snf->writesize + snf->oobsize); ++ ++ return mtk_snand_do_write_page(snf, addr, snf->buf_cache, ++ snf->buf_cache + snf->writesize, true, ++ true); ++} ++ ++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr) ++{ ++ if (!snf) ++ return -EINVAL; ++ ++ if (addr >= snf->size) ++ return -EINVAL; ++ ++ addr &= ~snf->erasesize_mask; ++ ++ if (snf->nfi_soc->bbm_swap) ++ return mtk_snand_block_markbad_std(snf, addr); ++ ++ return mtk_snand_block_markbad_mtk(snf, addr); ++} ++ ++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, ++ const uint8_t *oobbuf, size_t ooblen) ++{ ++ size_t len = ooblen, sect_fdm_len; ++ const uint8_t *oob = oobbuf; ++ uint32_t step = 0; ++ ++ if (!snf || !oobraw || !oob) ++ return -EINVAL; ++ ++ while (len && step < snf->ecc_steps) { ++ sect_fdm_len = snf->nfi_soc->fdm_size - 1; ++ if (sect_fdm_len > len) ++ sect_fdm_len = len; ++ ++ memcpy(oobraw + step * snf->nfi_soc->fdm_size + 1, oob, ++ sect_fdm_len); ++ ++ len -= sect_fdm_len; ++ oob += sect_fdm_len; ++ step++; ++ } ++ ++ return len; ++} ++ ++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, ++ size_t ooblen, const uint8_t *oobraw) ++{ ++ size_t len = ooblen, sect_fdm_len; ++ uint8_t *oob = oobbuf; ++ uint32_t step = 0; ++ ++ if (!snf || !oobraw || !oob) ++ return -EINVAL; ++ ++ while (len && step < snf->ecc_steps) { ++ sect_fdm_len = snf->nfi_soc->fdm_size - 1; ++ if (sect_fdm_len > len) ++ sect_fdm_len = len; ++ ++ memcpy(oob, oobraw + step * snf->nfi_soc->fdm_size + 1, ++ sect_fdm_len); ++ ++ len -= sect_fdm_len; ++ oob += sect_fdm_len; ++ step++; ++ } ++ ++ return len; ++} ++ ++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, size_t ooblen, ++ size_t *actualooblen, bool raw) ++{ ++ int ret, oobremain; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (!oob) ++ return mtk_snand_read_page(snf, addr, buf, NULL, raw); ++ ++ ret = mtk_snand_read_page(snf, addr, buf, snf->buf_cache, raw); ++ if (ret && ret != -EBADMSG) { ++ if (actualooblen) ++ *actualooblen = 0; ++ return ret; ++ } ++ ++ oobremain = mtk_snand_transfer_oob(snf, oob, ooblen, snf->buf_cache); ++ if (actualooblen) ++ *actualooblen = ooblen - oobremain; ++ ++ return ret; ++} ++ ++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ size_t ooblen, size_t *actualooblen, bool raw) ++{ ++ int oobremain; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ if (!oob) ++ return mtk_snand_write_page(snf, addr, buf, NULL, raw); ++ ++ memset(snf->buf_cache, 0xff, snf->oobsize); ++ oobremain = mtk_snand_fill_oob(snf, snf->buf_cache, oob, ooblen); ++ if (actualooblen) ++ *actualooblen = ooblen - oobremain; ++ ++ return mtk_snand_write_page(snf, addr, buf, snf->buf_cache, raw); ++} ++ ++int mtk_snand_get_chip_info(struct mtk_snand *snf, ++ struct mtk_snand_chip_info *info) ++{ ++ if (!snf || !info) ++ return -EINVAL; ++ ++ info->model = snf->model; ++ info->chipsize = snf->size; ++ info->blocksize = snf->erasesize; ++ info->pagesize = snf->writesize; ++ info->sparesize = snf->oobsize; ++ info->spare_per_sector = snf->spare_per_sector; ++ info->fdm_size = snf->nfi_soc->fdm_size; ++ info->fdm_ecc_size = snf->nfi_soc->fdm_ecc_size; ++ info->num_sectors = snf->ecc_steps; ++ info->sector_size = snf->nfi_soc->sector_size; ++ info->ecc_strength = snf->ecc_strength; ++ info->ecc_bytes = snf->ecc_bytes; ++ ++ return 0; ++} ++ ++int mtk_snand_irq_process(struct mtk_snand *snf) ++{ ++ uint32_t sta, ien; ++ ++ if (!snf) ++ return -EINVAL; ++ ++ sta = nfi_read32(snf, NFI_INTR_STA); ++ ien = nfi_read32(snf, NFI_INTR_EN); ++ ++ if (!(sta & ien)) ++ return 0; ++ ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ irq_completion_done(snf->pdev); ++ ++ return 1; ++} ++ ++static int mtk_snand_select_spare_per_sector(struct mtk_snand *snf) ++{ ++ uint32_t spare_per_step = snf->oobsize / snf->ecc_steps; ++ int i, mul = 1; ++ ++ /* ++ * If we're using the 1KB sector size, HW will automatically ++ * double the spare size. So we should only use half of the value. ++ */ ++ if (snf->nfi_soc->sector_size == 1024) ++ mul = 2; ++ ++ spare_per_step /= mul; ++ ++ for (i = snf->nfi_soc->num_spare_size - 1; i >= 0; i--) { ++ if (snf->nfi_soc->spare_sizes[i] <= spare_per_step) { ++ snf->spare_per_sector = snf->nfi_soc->spare_sizes[i]; ++ snf->spare_per_sector *= mul; ++ return i; ++ } ++ } ++ ++ snand_log_nfi(snf->pdev, ++ "Page size %u+%u is not supported\n", snf->writesize, ++ snf->oobsize); ++ ++ return -1; ++} ++ ++static int mtk_snand_pagefmt_setup(struct mtk_snand *snf) ++{ ++ uint32_t spare_size_idx, spare_size_shift, pagesize_idx; ++ uint32_t sector_size_512; ++ ++ if (snf->nfi_soc->sector_size == 512) { ++ sector_size_512 = NFI_SEC_SEL_512; ++ spare_size_shift = NFI_SPARE_SIZE_S; ++ } else { ++ sector_size_512 = 0; ++ spare_size_shift = NFI_SPARE_SIZE_LS_S; ++ } ++ ++ switch (snf->writesize) { ++ case SZ_512: ++ pagesize_idx = NFI_PAGE_SIZE_512_2K; ++ break; ++ case SZ_2K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_2K_4K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_512_2K; ++ break; ++ case SZ_4K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_4K_8K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_2K_4K; ++ break; ++ case SZ_8K: ++ if (snf->nfi_soc->sector_size == 512) ++ pagesize_idx = NFI_PAGE_SIZE_8K_16K; ++ else ++ pagesize_idx = NFI_PAGE_SIZE_4K_8K; ++ break; ++ case SZ_16K: ++ pagesize_idx = NFI_PAGE_SIZE_8K_16K; ++ break; ++ default: ++ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", ++ snf->writesize); ++ return -ENOTSUPP; ++ } ++ ++ spare_size_idx = mtk_snand_select_spare_per_sector(snf); ++ if (unlikely(spare_size_idx < 0)) ++ return -ENOTSUPP; ++ ++ snf->raw_sector_size = snf->nfi_soc->sector_size + ++ snf->spare_per_sector; ++ ++ /* Setup page format */ ++ nfi_write32(snf, NFI_PAGEFMT, ++ (snf->nfi_soc->fdm_ecc_size << NFI_FDM_ECC_NUM_S) | ++ (snf->nfi_soc->fdm_size << NFI_FDM_NUM_S) | ++ (spare_size_idx << spare_size_shift) | ++ (pagesize_idx << NFI_PAGE_SIZE_S) | ++ sector_size_512); ++ ++ return 0; ++} ++ ++static enum snand_flash_io mtk_snand_select_opcode(struct mtk_snand *snf, ++ uint32_t snfi_caps, uint8_t *opcode, ++ uint8_t *dummy, ++ const struct snand_io_cap *op_cap) ++{ ++ uint32_t i, caps; ++ ++ caps = snfi_caps & op_cap->caps; ++ ++ i = fls(caps); ++ if (i > 0) { ++ *opcode = op_cap->opcodes[i - 1].opcode; ++ if (dummy) ++ *dummy = op_cap->opcodes[i - 1].dummy; ++ return i - 1; ++ } ++ ++ return __SNAND_IO_MAX; ++} ++ ++static int mtk_snand_select_opcode_rfc(struct mtk_snand *snf, ++ uint32_t snfi_caps, ++ const struct snand_io_cap *op_cap) ++{ ++ enum snand_flash_io idx; ++ ++ static const uint8_t rfc_modes[__SNAND_IO_MAX] = { ++ [SNAND_IO_1_1_1] = DATA_READ_MODE_X1, ++ [SNAND_IO_1_1_2] = DATA_READ_MODE_X2, ++ [SNAND_IO_1_2_2] = DATA_READ_MODE_DUAL, ++ [SNAND_IO_1_1_4] = DATA_READ_MODE_X4, ++ [SNAND_IO_1_4_4] = DATA_READ_MODE_QUAD, ++ }; ++ ++ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_rfc, ++ &snf->dummy_rfc, op_cap); ++ if (idx >= __SNAND_IO_MAX) { ++ snand_log_snfi(snf->pdev, ++ "No capable opcode for read from cache\n"); ++ return -ENOTSUPP; ++ } ++ ++ snf->mode_rfc = rfc_modes[idx]; ++ ++ if (idx == SNAND_IO_1_1_4 || idx == SNAND_IO_1_4_4) ++ snf->quad_spi_op = true; ++ ++ return 0; ++} ++ ++static int mtk_snand_select_opcode_pl(struct mtk_snand *snf, uint32_t snfi_caps, ++ const struct snand_io_cap *op_cap) ++{ ++ enum snand_flash_io idx; ++ ++ static const uint8_t pl_modes[__SNAND_IO_MAX] = { ++ [SNAND_IO_1_1_1] = 0, ++ [SNAND_IO_1_1_4] = 1, ++ }; ++ ++ idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_pl, ++ NULL, op_cap); ++ if (idx >= __SNAND_IO_MAX) { ++ snand_log_snfi(snf->pdev, ++ "No capable opcode for program load\n"); ++ return -ENOTSUPP; ++ } ++ ++ snf->mode_pl = pl_modes[idx]; ++ ++ if (idx == SNAND_IO_1_1_4) ++ snf->quad_spi_op = true; ++ ++ return 0; ++} ++ ++static int mtk_snand_setup(struct mtk_snand *snf, ++ const struct snand_flash_info *snand_info) ++{ ++ const struct snand_mem_org *memorg = &snand_info->memorg; ++ uint32_t i, msg_size, snfi_caps; ++ int ret; ++ ++ /* Calculate flash memory organization */ ++ snf->model = snand_info->model; ++ snf->writesize = memorg->pagesize; ++ snf->oobsize = memorg->sparesize; ++ snf->erasesize = snf->writesize * memorg->pages_per_block; ++ snf->die_size = (uint64_t)snf->erasesize * memorg->blocks_per_die; ++ snf->size = snf->die_size * memorg->ndies; ++ snf->num_dies = memorg->ndies; ++ ++ snf->writesize_mask = snf->writesize - 1; ++ snf->erasesize_mask = snf->erasesize - 1; ++ snf->die_mask = snf->die_size - 1; ++ ++ snf->writesize_shift = ffs(snf->writesize) - 1; ++ snf->erasesize_shift = ffs(snf->erasesize) - 1; ++ snf->die_shift = mtk_snand_ffs64(snf->die_size) - 1; ++ ++ snf->select_die = snand_info->select_die; ++ ++ /* Determine opcodes for read from cache/program load */ ++ snfi_caps = SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2; ++ if (snf->snfi_quad_spi) ++ snfi_caps |= SPI_IO_1_1_4 | SPI_IO_1_4_4; ++ ++ ret = mtk_snand_select_opcode_rfc(snf, snfi_caps, snand_info->cap_rd); ++ if (ret) ++ return ret; ++ ++ ret = mtk_snand_select_opcode_pl(snf, snfi_caps, snand_info->cap_pl); ++ if (ret) ++ return ret; ++ ++ /* ECC and page format */ ++ snf->ecc_steps = snf->writesize / snf->nfi_soc->sector_size; ++ if (snf->ecc_steps > snf->nfi_soc->max_sectors) { ++ snand_log_nfi(snf->pdev, "Page size %u is not supported\n", ++ snf->writesize); ++ return -ENOTSUPP; ++ } ++ ++ ret = mtk_snand_pagefmt_setup(snf); ++ if (ret) ++ return ret; ++ ++ msg_size = snf->nfi_soc->sector_size + snf->nfi_soc->fdm_ecc_size; ++ ret = mtk_ecc_setup(snf, snf->nfi_base + NFI_FDM0L, ++ snf->spare_per_sector - snf->nfi_soc->fdm_size, ++ msg_size); ++ if (ret) ++ return ret; ++ ++ nfi_write16(snf, NFI_CNFG, 0); ++ ++ /* Tuning options */ ++ nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN); ++ nfi_write32(snf, SNF_DLY_CTL3, (snf->nfi_soc->sample_delay << SFCK_SAM_DLY_S)); ++ ++ /* Interrupts */ ++ nfi_read32(snf, NFI_INTR_STA); ++ nfi_write32(snf, NFI_INTR_EN, 0); ++ ++ /* Clear SNF done flag */ ++ nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE | CUS_PG_DONE); ++ nfi_write32(snf, SNF_STA_CTL1, 0); ++ ++ /* Initialization on all dies */ ++ for (i = 0; i < snf->num_dies; i++) { ++ mtk_snand_select_die(snf, i); ++ ++ /* Disable On-Die ECC engine */ ++ ret = mtk_snand_ondie_ecc_control(snf, false); ++ if (ret) ++ return ret; ++ ++ /* Disable block protection */ ++ mtk_snand_unlock(snf); ++ ++ /* Enable/disable quad-spi */ ++ mtk_snand_qspi_control(snf, snf->quad_spi_op); ++ } ++ ++ mtk_snand_select_die(snf, 0); ++ ++ return 0; ++} ++ ++static int mtk_snand_id_probe(struct mtk_snand *snf, ++ const struct snand_flash_info **snand_info) ++{ ++ uint8_t id[4], op[2]; ++ int ret; ++ ++ /* Read SPI-NAND JEDEC ID, OP + dummy/addr + ID */ ++ op[0] = SNAND_CMD_READID; ++ op[1] = 0; ++ ret = mtk_snand_mac_io(snf, op, 2, id, sizeof(id)); ++ if (ret) ++ return ret; ++ ++ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); ++ if (*snand_info) ++ return 0; ++ ++ /* Read SPI-NAND JEDEC ID, OP + ID */ ++ op[0] = SNAND_CMD_READID; ++ ret = mtk_snand_mac_io(snf, op, 1, id, sizeof(id)); ++ if (ret) ++ return ret; ++ ++ *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id); ++ if (*snand_info) ++ return 0; ++ ++ snand_log_chip(snf->pdev, ++ "Unrecognized SPI-NAND ID: %02x %02x %02x %02x\n", ++ id[0], id[1], id[2], id[3]); ++ ++ return -EINVAL; ++} ++ ++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, ++ struct mtk_snand **psnf) ++{ ++ const struct snand_flash_info *snand_info; ++ uint32_t rawpage_size, sect_bf_size; ++ struct mtk_snand tmpsnf, *snf; ++ int ret; ++ ++ if (!pdata || !psnf) ++ return -EINVAL; ++ ++ if (pdata->soc >= __SNAND_SOC_MAX) { ++ snand_log_chip(dev, "Invalid SOC %u for MTK-SNAND\n", ++ pdata->soc); ++ return -EINVAL; ++ } ++ ++ /* Dummy instance only for initial reset and id probe */ ++ tmpsnf.nfi_base = pdata->nfi_base; ++ tmpsnf.ecc_base = pdata->ecc_base; ++ tmpsnf.soc = pdata->soc; ++ tmpsnf.nfi_soc = &mtk_snand_socs[pdata->soc]; ++ tmpsnf.pdev = dev; ++ ++ /* Switch to SNFI mode */ ++ writel(SPI_MODE, tmpsnf.nfi_base + SNF_CFG); ++ ++ /* Reset SNFI & NFI */ ++ mtk_snand_mac_reset(&tmpsnf); ++ mtk_nfi_reset(&tmpsnf); ++ ++ /* Reset SPI-NAND chip */ ++ ret = mtk_snand_chip_reset(&tmpsnf); ++ if (ret) { ++ snand_log_chip(dev, "Failed to reset SPI-NAND chip\n"); ++ return ret; ++ } ++ ++ /* Probe SPI-NAND flash by JEDEC ID */ ++ ret = mtk_snand_id_probe(&tmpsnf, &snand_info); ++ if (ret) ++ return ret; ++ ++ 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 + sect_bf_size); ++ if (!snf) { ++ snand_log_chip(dev, "Failed to allocate memory for instance\n"); ++ return -ENOMEM; ++ } ++ ++ 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); ++ if (!snf->page_cache) { ++ generic_mem_free(dev, snf); ++ snand_log_chip(dev, ++ "Failed to allocate memory for DMA buffer\n"); ++ return -ENOMEM; ++ } ++ ++ /* Fill up instance */ ++ snf->pdev = dev; ++ snf->nfi_base = pdata->nfi_base; ++ snf->ecc_base = pdata->ecc_base; ++ snf->soc = pdata->soc; ++ snf->nfi_soc = &mtk_snand_socs[pdata->soc]; ++ snf->snfi_quad_spi = pdata->quad_spi; ++ ++ /* Initialize SNFI & ECC engine */ ++ ret = mtk_snand_setup(snf, snand_info); ++ if (ret) { ++ dma_mem_free(dev, snf->page_cache); ++ generic_mem_free(dev, snf); ++ return ret; ++ } ++ ++ *psnf = snf; ++ ++ return 0; ++} ++ ++int mtk_snand_cleanup(struct mtk_snand *snf) ++{ ++ if (!snf) ++ return 0; ++ ++ dma_mem_free(snf->pdev, snf->page_cache); ++ generic_mem_free(snf->pdev, snf); ++ ++ return 0; ++} +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand.h +@@ -0,0 +1,77 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ ++/* ++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#ifndef _MTK_SNAND_H_ ++#define _MTK_SNAND_H_ ++ ++#ifndef PRIVATE_MTK_SNAND_HEADER ++#include ++#include ++#include ++#endif ++ ++enum mtk_snand_soc { ++ SNAND_SOC_MT7622, ++ SNAND_SOC_MT7629, ++ SNAND_SOC_MT7981, ++ SNAND_SOC_MT7986, ++ __SNAND_SOC_MAX ++}; ++ ++struct mtk_snand_platdata { ++ void *nfi_base; ++ void *ecc_base; ++ enum mtk_snand_soc soc; ++ bool quad_spi; ++}; ++ ++struct mtk_snand_chip_info { ++ const char *model; ++ uint64_t chipsize; ++ uint32_t blocksize; ++ uint32_t pagesize; ++ uint32_t sparesize; ++ uint32_t spare_per_sector; ++ uint32_t fdm_size; ++ uint32_t fdm_ecc_size; ++ uint32_t num_sectors; ++ uint32_t sector_size; ++ uint32_t ecc_strength; ++ uint32_t ecc_bytes; ++}; ++ ++struct mtk_snand; ++struct snand_flash_info; ++ ++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata, ++ struct mtk_snand **psnf); ++int mtk_snand_cleanup(struct mtk_snand *snf); ++ ++int mtk_snand_chip_reset(struct mtk_snand *snf); ++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf, ++ void *oob, bool raw); ++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf, ++ const void *oob, bool raw); ++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr); ++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw, ++ const uint8_t *oobbuf, size_t ooblen); ++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf, ++ size_t ooblen, const uint8_t *oobraw); ++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ void *buf, void *oob, size_t ooblen, ++ size_t *actualooblen, bool raw); ++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr, ++ const void *buf, const void *oob, ++ size_t ooblen, size_t *actualooblen, ++ bool raw); ++int mtk_snand_get_chip_info(struct mtk_snand *snf, ++ struct mtk_snand_chip_info *info); ++int mtk_snand_irq_process(struct mtk_snand *snf); ++ ++#endif /* _MTK_SNAND_H_ */ diff --git a/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch new file mode 100644 index 0000000000..ac56638ff7 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch @@ -0,0 +1,175 @@ +From a347e374cb338213632c6dde88dd226d64bd8b27 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 08:57:29 +0800 +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. + +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 | 133 ++++++++++++++++++++++++++ + 3 files changed, 143 insertions(+) + create mode 100644 drivers/mtd/mtk-snand/mtk-snand-spl.c + +--- a/drivers/mtd/mtk-snand/Kconfig ++++ b/drivers/mtd/mtk-snand/Kconfig +@@ -19,3 +19,9 @@ config MTK_SPI_NAND_MTD + help + This option enables access to SPI-NAND flashes through the + MTD interface of MediaTek SPI NAND Flash Controller ++ ++config SPL_MTK_SPI_NAND ++ tristate "SPL support for MediaTek SPI NAND flash controller" ++ depends on MTK_SPI_NAND ++ help ++ This option enables access to SPI-NAND flashes in the SPL stage +--- a/drivers/mtd/mtk-snand/Makefile ++++ b/drivers/mtd/mtk-snand/Makefile +@@ -8,4 +8,8 @@ + obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o + obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o + ++ifdef CONFIG_SPL_BUILD ++obj-$(CONFIG_SPL_MTK_SPI_NAND) += mtk-snand-spl.o ++endif ++ + ccflags-y += -DPRIVATE_MTK_SNAND_HEADER +--- /dev/null ++++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c +@@ -0,0 +1,133 @@ ++// 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 "mtk-snand.h" ++ ++static struct mtk_snand *snf; ++static struct mtk_snand_chip_info cinfo; ++static u32 oobavail; ++ ++static u8 *page_cache; ++ ++int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) ++{ ++ u32 sizeremain = size, chunksize, leading; ++ uint32_t off = offs, writesize_mask = cinfo.pagesize - 1; ++ uint8_t *ptr = dst; ++ int ret; ++ ++ if (!snf) ++ return -ENODEV; ++ ++ while (sizeremain) { ++ WATCHDOG_RESET(); ++ ++ leading = off & writesize_mask; ++ chunksize = cinfo.pagesize - leading; ++ if (chunksize > sizeremain) ++ chunksize = sizeremain; ++ ++ if (chunksize == cinfo.pagesize) { ++ ret = mtk_snand_read_page(snf, off - leading, ptr, ++ NULL, false); ++ if (ret) ++ break; ++ } else { ++ ret = mtk_snand_read_page(snf, off - leading, ++ page_cache, NULL, false); ++ if (ret) ++ break; ++ ++ memcpy(ptr, page_cache + leading, chunksize); ++ } ++ ++ off += chunksize; ++ ptr += chunksize; ++ sizeremain -= chunksize; ++ } ++ ++ return ret; ++} ++ ++void nand_init(void) ++{ ++ struct mtk_snand_platdata mtk_snand_pdata = {}; ++ struct udevice *dev; ++ fdt_addr_t base; ++ int ret; ++ ++ ret = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_GET(mtk_snand), ++ &dev); ++ if (ret) { ++ printf("mtk-snand-spl: Device instance not found!\n"); ++ return; ++ } ++ ++ base = dev_read_addr_name(dev, "nfi"); ++ if (base == FDT_ADDR_T_NONE) { ++ printf("mtk-snand-spl: NFI base not set\n"); ++ return; ++ } ++ mtk_snand_pdata.nfi_base = map_sysmem(base, 0); ++ ++ base = dev_read_addr_name(dev, "ecc"); ++ if (base == FDT_ADDR_T_NONE) { ++ printf("mtk-snand-spl: ECC base not set\n"); ++ return; ++ } ++ mtk_snand_pdata.ecc_base = map_sysmem(base, 0); ++ ++ mtk_snand_pdata.soc = dev_get_driver_data(dev); ++ mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi"); ++ ++ ret = mtk_snand_init(NULL, &mtk_snand_pdata, &snf); ++ if (ret) { ++ printf("mtk-snand-spl: failed to initialize SPI-NAND\n"); ++ return; ++ } ++ ++ mtk_snand_get_chip_info(snf, &cinfo); ++ ++ oobavail = cinfo.num_sectors * (cinfo.fdm_size - 1); ++ ++ printf("SPI-NAND: %s (%uMB)\n", cinfo.model, ++ (u32)(cinfo.chipsize >> 20)); ++ ++ page_cache = malloc(cinfo.pagesize + cinfo.sparesize); ++ if (!page_cache) { ++ mtk_snand_cleanup(snf); ++ printf("mtk-snand-spl: failed to allocate page cache\n"); ++ } ++} ++ ++void nand_deselect(void) ++{ ++ ++} ++ ++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 */ }, ++}; ++ ++U_BOOT_DRIVER(mtk_snand) = { ++ .name = "mtk-snand", ++ .id = UCLASS_MTD, ++ .of_match = mtk_snand_ids, ++ .flags = DM_FLAG_PRE_RELOC, ++}; diff --git a/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch b/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch new file mode 100644 index 0000000000..9d6beb3ebf --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch @@ -0,0 +1,409 @@ +From efc3e6f5d29f87a433b42f15a0b87e04b7cd498d Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:11:32 +0800 +Subject: [PATCH 38/71] env: add support for generic MTD device + +Add an env driver for generic MTD device. + +Signed-off-by: Weijie Gao +--- + cmd/nvedit.c | 3 +- + env/Kconfig | 37 +++++- + env/Makefile | 1 + + env/env.c | 3 + + env/mtd.c | 256 +++++++++++++++++++++++++++++++++++++++++ + include/env_internal.h | 1 + + tools/Makefile | 1 + + 7 files changed, 299 insertions(+), 3 deletions(-) + create mode 100644 env/mtd.c + +--- a/cmd/nvedit.c ++++ b/cmd/nvedit.c +@@ -48,6 +48,7 @@ DECLARE_GLOBAL_DATA_PTR; + defined(CONFIG_ENV_IS_IN_MMC) || \ + defined(CONFIG_ENV_IS_IN_FAT) || \ + defined(CONFIG_ENV_IS_IN_EXT4) || \ ++ defined(CONFIG_ENV_IS_IN_MTD) || \ + defined(CONFIG_ENV_IS_IN_NAND) || \ + defined(CONFIG_ENV_IS_IN_NVRAM) || \ + defined(CONFIG_ENV_IS_IN_ONENAND) || \ +@@ -62,7 +63,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|\ ++# 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 + #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_UBI && !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 +@@ -226,6 +226,27 @@ config ENV_IS_IN_MMC + This value is also in units of bytes, but must also be aligned to + an MMC sector boundary. + ++config ENV_IS_IN_MTD ++ bool "Environment in a MTD device" ++ depends on !CHAIN_OF_TRUST ++ depends on MTD ++ help ++ Define this if you have a MTD device which you want to use for ++ the environment. ++ ++ - CONFIG_ENV_MTD_NAME: ++ - CONFIG_ENV_OFFSET: ++ - CONFIG_ENV_SIZE: ++ ++ These three #defines specify the MTD device where the environment ++ is stored, offset and size of the environment area within the MTD ++ device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary. ++ ++ - CONFIG_ENV_SIZE_REDUND: ++ ++ This #define specify the maximum size allowed for read/write/erase ++ with skipped bad blocks starting from ENV_OFFSET. ++ + config ENV_IS_IN_NAND + bool "Environment in a NAND device" + depends on !CHAIN_OF_TRUST +@@ -531,10 +552,16 @@ config ENV_ADDR_REDUND + Offset from the start of the device (or partition) of the redundant + environment location. + ++config ENV_MTD_NAME ++ string "Name of the MTD device storing the environment" ++ depends on ENV_IS_IN_MTD ++ help ++ Name of the MTD device that stores the environment ++ + 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_SPI_FLASH || 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 +@@ -581,6 +608,12 @@ config ENV_SECT_SIZE + help + Size of the sector containing the environment. + ++config ENV_SIZE_REDUND ++ hex "Redundant environment size" ++ depends on ENV_IS_IN_MTD ++ help ++ The maximum size allowed for read/write/erase with skipped bad blocks. ++ + config ENV_UBI_PART + string "UBI partition name" + depends on ENV_IS_IN_UBI +--- a/env/Makefile ++++ b/env/Makefile +@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE) + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o + 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_SPI_FLASH) += sf.o + obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o +--- a/env/env.c ++++ b/env/env.c +@@ -69,6 +69,9 @@ static enum env_location env_locations[] + #ifdef CONFIG_ENV_IS_IN_MMC + ENVL_MMC, + #endif ++#ifdef CONFIG_ENV_IS_IN_MTD ++ ENVL_MTD, ++#endif + #ifdef CONFIG_ENV_IS_IN_NAND + ENVL_NAND, + #endif +--- /dev/null ++++ b/env/mtd.c +@@ -0,0 +1,256 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved. ++ * ++ * Author: Weijie Gao ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if CONFIG_ENV_SIZE_REDUND < CONFIG_ENV_SIZE ++#undef CONFIG_ENV_SIZE_REDUND ++#define CONFIG_ENV_SIZE_REDUND CONFIG_ENV_SIZE ++#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_mtd_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; ++} ++ ++static struct mtd_info *env_mtd_get_dev(void) ++{ ++ struct mtd_info *mtd; ++ ++ mtd_probe_devices(); ++ ++ mtd = get_mtd_device_nm(CONFIG_ENV_MTD_NAME); ++ if (IS_ERR(mtd) || !mtd) { ++ printf("MTD device '%s' not found\n", CONFIG_ENV_MTD_NAME); ++ return NULL; ++ } ++ ++ return mtd; ++} ++ ++static inline bool mtd_addr_is_block_aligned(struct mtd_info *mtd, u64 addr) ++{ ++ return (addr & mtd->erasesize_mask) == 0; ++} ++ ++static int mtd_io_skip_bad(struct mtd_info *mtd, bool read, loff_t offset, ++ size_t length, size_t redund, u8 *buffer) ++{ ++ struct mtd_oob_ops io_op = {}; ++ size_t remaining = length; ++ loff_t off, end; ++ int ret; ++ ++ io_op.mode = MTD_OPS_PLACE_OOB; ++ io_op.len = mtd->writesize; ++ io_op.datbuf = (void *)buffer; ++ ++ /* Search for the first good block after the given offset */ ++ off = offset; ++ end = (off + redund) | (mtd->erasesize - 1); ++ while (mtd_block_isbad(mtd, off) && off < end) ++ off += mtd->erasesize; ++ ++ /* Reached end position */ ++ if (off >= end) ++ return -EIO; ++ ++ /* Loop over the pages to do the actual read/write */ ++ while (remaining) { ++ /* Skip the block if it is bad */ ++ if (mtd_addr_is_block_aligned(mtd, off) && ++ mtd_block_isbad(mtd, off)) { ++ off += mtd->erasesize; ++ continue; ++ } ++ ++ if (read) ++ ret = mtd_read_oob(mtd, off, &io_op); ++ else ++ ret = mtd_write_oob(mtd, off, &io_op); ++ ++ if (ret) { ++ printf("Failure while %s at offset 0x%llx\n", ++ read ? "reading" : "writing", off); ++ break; ++ } ++ ++ off += io_op.retlen; ++ remaining -= io_op.retlen; ++ io_op.datbuf += io_op.retlen; ++ io_op.oobbuf += io_op.oobretlen; ++ ++ /* Reached end position */ ++ if (off >= end) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_CMD_SAVEENV ++static int mtd_erase_skip_bad(struct mtd_info *mtd, loff_t offset, ++ size_t length, size_t redund) ++{ ++ struct erase_info erase_op = {}; ++ loff_t end = (offset + redund) | (mtd->erasesize - 1); ++ int ret; ++ ++ erase_op.mtd = mtd; ++ erase_op.addr = offset; ++ erase_op.len = length; ++ ++ while (erase_op.len) { ++ ret = mtd_erase(mtd, &erase_op); ++ ++ /* Abort if its not a bad block error */ ++ if (ret != -EIO) ++ return ret; ++ ++ printf("Skipping bad block at 0x%08llx\n", erase_op.fail_addr); ++ ++ /* Skip bad block and continue behind it */ ++ erase_op.len -= erase_op.fail_addr - erase_op.addr; ++ erase_op.len -= mtd->erasesize; ++ erase_op.addr = erase_op.fail_addr + mtd->erasesize; ++ ++ /* Reached end position */ ++ if (erase_op.addr >= end) ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int env_mtd_save(void) ++{ ++ ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1); ++ struct mtd_info *mtd; ++ int ret = 0; ++ ++ ret = env_export(env_new); ++ if (ret) ++ return ret; ++ ++ mtd = env_mtd_get_dev(); ++ if (!mtd) ++ return 1; ++ ++ printf("Erasing on MTD device '%s'... ", mtd->name); ++ ++ ret = mtd_erase_skip_bad(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND); ++ ++ puts(ret ? "FAILED\n" : "OK\n"); ++ ++ if (ret) { ++ put_mtd_device(mtd); ++ return 1; ++ } ++ ++ printf("Writing to MTD device '%s'... ", mtd->name); ++ ++ ret = mtd_io_skip_bad(mtd, false, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND, (u8 *)env_new); ++ ++ puts(ret ? "FAILED\n" : "OK\n"); ++ ++ put_mtd_device(mtd); ++ ++ return !!ret; ++} ++#endif /* CONFIG_CMD_SAVEENV */ ++ ++static int readenv(size_t offset, u_char *buf) ++{ ++ struct mtd_info *mtd; ++ int ret; ++ ++ mtd = env_mtd_get_dev(); ++ if (!mtd) ++ return 1; ++ ++ ret = mtd_io_skip_bad(mtd, true, offset, CONFIG_ENV_SIZE, ++ CONFIG_ENV_SIZE_REDUND, buf); ++ ++ put_mtd_device(mtd); ++ ++ return !!ret; ++} ++ ++static int env_mtd_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(mtd) = { ++ .location = ENVL_MTD, ++ ENV_NAME("MTD") ++ .load = env_mtd_load, ++#if defined(CONFIG_CMD_SAVEENV) ++ .save = env_save_ptr(env_mtd_save), ++#endif ++ .init = env_mtd_init, ++}; +--- a/include/env_internal.h ++++ b/include/env_internal.h +@@ -130,6 +130,7 @@ enum env_location { + ENVL_FAT, + ENVL_FLASH, + ENVL_MMC, ++ ENVL_MTD, + ENVL_NAND, + ENVL_NVRAM, + ENVL_ONENAND, +--- a/tools/Makefile ++++ b/tools/Makefile +@@ -41,6 +41,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y + ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y + 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_NVRAM) = y + ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y 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 0000000000..1c62130cf6 --- /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 0000000000..9f80deb137 --- /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 0000000000..644ac8f105 --- /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 0000000000..3dab053677 --- /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 0000000000..e6e155bc14 --- /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 0000000000..6e38ec4ac9 --- /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 0000000000..2791332b04 --- /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 0000000000..32b21be255 --- /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 0000000000..bf4ed97f13 --- /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 0000000000..6a61045955 --- /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 0000000000..972a022d4b --- /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/100-16-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 new file mode 100644 index 0000000000..ee45ff0de6 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch @@ -0,0 +1,250 @@ +From 5a15437610e8e8c68dc347845a83d0cbad80ca08 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Tue, 19 Jan 2021 10:58:48 +0800 +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 | 34 ++++++++++++++++++++++++----- + common/menu.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-- + include/menu.h | 12 +++++++---- + 3 files changed, 93 insertions(+), 11 deletions(-) + +--- a/cmd/bootmenu.c ++++ b/cmd/bootmenu.c +@@ -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; ++ int choice = -1; + int esc = 0; + int i; + + while (1) { + if (menu->delay >= 0) { + /* Autoboot was not stopped */ +- bootmenu_autoboot_loop(menu, &key, &esc); ++ bootmenu_autoboot_loop(menu, &key, &esc, &choice); + } else { + /* Some key was pressed, so autoboot was stopped */ +- bootmenu_loop(menu, &key, &esc); ++ bootmenu_loop(menu, &key, &esc, &choice); + } + + switch (key) { +@@ -110,6 +111,12 @@ static char *bootmenu_choice_entry(void + ++menu->active; + /* no menu key selected, regenerate menu */ + return NULL; ++ case KEY_CHOICE: ++ menu->active = choice; ++ if (!menu->last_choiced) { ++ menu->last_choiced = true; ++ return NULL; ++ } + case KEY_SELECT: + iter = menu->first; + for (i = 0; i < menu->active; ++i) +@@ -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); ++ /* 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); + + entry->command = strdup(sep + 1); + if (!entry->command) { +@@ -331,6 +354,7 @@ static struct bootmenu_data *bootmenu_cr + menu->delay = delay; + menu->active = 0; + menu->first = NULL; ++ menu->last_choiced = false; + + default_str = env_get("bootmenu_default"); + if (default_str) +@@ -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)) +- entry->title = strdup("U-Boot console"); ++ entry->title = strdup("0. U-Boot console"); + else +- entry->title = strdup("Quit"); ++ entry->title = strdup("0. Quit"); + + if (!entry->title) { + free(entry); +--- a/common/menu.c ++++ b/common/menu.c +@@ -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; ++ ++ for (i = 0; i < ARRAY_SIZE(choice_chars); i++) ++ if (tolower(choice) == choice_chars[i]) ++ return i; ++ ++ 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 +453,7 @@ int menu_destroy(struct menu *m) + } + + void bootmenu_autoboot_loop(struct bootmenu_data *menu, +- enum bootmenu_key *key, int *esc) ++ enum bootmenu_key *key, int *esc, int *choice) + { + int i, c; + +@@ -456,6 +483,19 @@ void bootmenu_autoboot_loop(struct bootm + break; + default: + *key = KEY_NONE; ++ if (*esc) ++ break; ++ ++ *choice = find_choice(c); ++ if ((*choice >= 0 && ++ *choice < menu->count - 1)) { ++ *key = KEY_CHOICE; ++ } else if (c == '0') { ++ *choice = menu->count - 1; ++ *key = KEY_CHOICE; ++ } else { ++ *key = KEY_NONE; ++ } + break; + } + +@@ -475,10 +515,16 @@ void bootmenu_autoboot_loop(struct bootm + } + + void bootmenu_loop(struct bootmenu_data *menu, +- enum bootmenu_key *key, int *esc) ++ enum bootmenu_key *key, int *esc, int *choice) + { + int c; + ++ if (menu->last_choiced) { ++ menu->last_choiced = false; ++ *key = KEY_SELECT; ++ return; ++ } ++ + if (*esc == 1) { + if (tstc()) { + c = getchar(); +@@ -504,6 +550,14 @@ void bootmenu_loop(struct bootmenu_data + if (c == '\e') { + *esc = 1; + *key = KEY_NONE; ++ } else { ++ *choice = find_choice(c); ++ if ((*choice >= 0 && *choice < menu->count - 1)) { ++ *key = KEY_CHOICE; ++ } else if (c == '0') { ++ *choice = menu->count - 1; ++ *key = KEY_CHOICE; ++ } + } + break; + case 1: +--- a/include/menu.h ++++ b/include/menu.h +@@ -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 */ ++ bool last_choiced; + }; + + enum bootmenu_key { +@@ -48,11 +52,11 @@ enum bootmenu_key { + KEY_DOWN, + KEY_SELECT, + KEY_QUIT, ++ KEY_CHOICE, + }; + + void bootmenu_autoboot_loop(struct bootmenu_data *menu, +- enum bootmenu_key *key, int *esc); ++ 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); + #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 0000000000..149a156ba2 --- /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/100-18-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 new file mode 100644 index 0000000000..b6243db9c2 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch @@ -0,0 +1,274 @@ +From 452dc98572f8353f77551bcce5a2ca8cd050f498 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:48:53 +0800 +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. + +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 ++++ + 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 ++++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi +@@ -40,3 +40,11 @@ + &snfi { + u-boot,dm-pre-reloc; + }; ++ ++&pinctrl { ++ u-boot,dm-pre-reloc; ++}; ++ ++&snand { ++ u-boot,dm-pre-reloc; ++}; +--- a/arch/arm/dts/mt7629-rfb.dts ++++ b/arch/arm/dts/mt7629-rfb.dts +@@ -47,9 +47,12 @@ + }; + + snfi_pins: snfi-pins { ++ u-boot,dm-pre-reloc; ++ + mux { + function = "flash"; + groups = "snfi"; ++ u-boot,dm-pre-reloc; + }; + }; + +@@ -102,6 +105,13 @@ + }; + }; + ++&snand { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&snfi_pins>; ++ status = "okay"; ++ quad-spi; ++}; ++ + &uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; +--- a/arch/arm/dts/mt7629.dtsi ++++ b/arch/arm/dts/mt7629.dtsi +@@ -229,6 +229,22 @@ + #size-cells = <0>; + }; + ++ snand: snand@1100d000 { ++ compatible = "mediatek,mt7629-snand"; ++ reg = <0x1100d000 0x1000>, ++ <0x1100e000 0x1000>; ++ reg-names = "nfi", "ecc"; ++ clocks = <&pericfg CLK_PERI_NFI_PD>, ++ <&pericfg CLK_PERI_SNFI_PD>, ++ <&pericfg CLK_PERI_NFIECC_PD>; ++ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; ++ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, ++ <&topckgen CLK_TOP_NFI_INFRA_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, ++ <&topckgen CLK_TOP_UNIVPLL2_D8>; ++ status = "disabled"; ++ }; ++ + 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,113 @@ ++CONFIG_ARM=y ++CONFIG_SYS_ARCH_TIMER=y ++CONFIG_SYS_THUMB_BUILD=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_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=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_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_AUTOBOOT is not set ++CONFIG_DEFAULT_FDT_FILE="mt7629-rfb" ++CONFIG_SYS_CONSOLE_IS_IN_ENV=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=y ++CONFIG_HUSH_PARSER=y ++CONFIG_SYS_PROMPT="U-Boot> " ++# 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 ++CONFIG_CMD_DM=y ++# CONFIG_CMD_FLASH is not set ++CONFIG_CMD_GPIO=y ++CONFIG_CMD_MTD=y ++CONFIG_CMD_USB=y ++# CONFIG_CMD_SETEXPR is not set ++# CONFIG_CMD_NFS is not set ++CONFIG_CMD_PING=y ++CONFIG_CMD_FAT=y ++CONFIG_CMD_FS_GENERIC=y ++CONFIG_CMD_LOG=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="u-boot-env" ++CONFIG_ENV_SIZE_REDUND=0x40000 ++CONFIG_SYS_RELOC_GD_ENV_ADDR=y ++CONFIG_NET_RANDOM_ETHADDR=y ++CONFIG_SPL_DM_SEQ_ALIAS=y ++CONFIG_REGMAP=y ++CONFIG_SPL_REGMAP=y ++CONFIG_SYSCON=y ++CONFIG_SPL_SYSCON=y ++CONFIG_CLK=y ++CONFIG_SPL_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_SPL_MTK_SPI_NAND=y ++CONFIG_DM_ETH=y ++CONFIG_MEDIATEK_ETH=y ++CONFIG_PHY=y ++CONFIG_PHY_MTK_TPHY=y ++CONFIG_PINCTRL=y ++CONFIG_PINCONF=y ++CONFIG_SPL_PINCTRL=y ++CONFIG_SPL_PINCONF=y ++CONFIG_PINCTRL_MT7629=y ++CONFIG_POWER_DOMAIN=y ++CONFIG_MTK_POWER_DOMAIN=y ++CONFIG_DM_REGULATOR=y ++CONFIG_DM_REGULATOR_FIXED=y ++CONFIG_RAM=y ++CONFIG_SPL_RAM=y ++CONFIG_DM_SERIAL=y ++CONFIG_MTK_SERIAL=y ++CONFIG_SPI=y ++CONFIG_DM_SPI=y ++CONFIG_SPI_MEM=y ++CONFIG_MTK_SNFI_SPI=y ++CONFIG_SYSRESET=y ++CONFIG_SPL_SYSRESET=y ++CONFIG_SYSRESET_WATCHDOG=y ++CONFIG_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_SHA256 is not set ++# CONFIG_SPL_SHA1 is not set ++CONFIG_LZMA=y ++CONFIG_SPL_LZMA=y ++# CONFIG_EFI_LOADER is not set diff --git a/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch new file mode 100644 index 0000000000..6e90f47f35 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch @@ -0,0 +1,76 @@ +From 4c1803cc08b1618d935c1386f43f43a4e9c97697 Mon Sep 17 00:00:00 2001 +From: Weijie Gao +Date: Wed, 3 Mar 2021 10:51:43 +0800 +Subject: [PATCH 54/71] board: mt7622: use new spi-nand driver + +Enable new spi-nand driver support for mt7622_rfb_defconfig + +Signed-off-by: Weijie Gao +--- + arch/arm/dts/mt7622-rfb.dts | 7 +++++++ + arch/arm/dts/mt7622.dtsi | 16 ++++++++++++++++ + configs/mt7622_rfb_defconfig | 5 +++++ + 3 files changed, 28 insertions(+) + +--- a/arch/arm/dts/mt7622-rfb.dts ++++ b/arch/arm/dts/mt7622-rfb.dts +@@ -196,6 +196,13 @@ + }; + }; + ++&snand { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&snfi_pins>; ++ status = "okay"; ++ quad-spi; ++}; ++ + &uart0 { + status = "okay"; + }; +--- a/arch/arm/dts/mt7622.dtsi ++++ b/arch/arm/dts/mt7622.dtsi +@@ -77,6 +77,22 @@ + #size-cells = <0>; + }; + ++ snand: snand@1100d000 { ++ compatible = "mediatek,mt7622-snand"; ++ reg = <0x1100d000 0x1000>, ++ <0x1100e000 0x1000>; ++ reg-names = "nfi", "ecc"; ++ clocks = <&pericfg CLK_PERI_NFI_PD>, ++ <&pericfg CLK_PERI_SNFI_PD>, ++ <&pericfg CLK_PERI_NFIECC_PD>; ++ clock-names = "nfi_clk", "pad_clk", "ecc_clk"; ++ assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>, ++ <&topckgen CLK_TOP_NFI_INFRA_SEL>; ++ assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>, ++ <&topckgen CLK_TOP_UNIVPLL2_D8>; ++ status = "disabled"; ++ }; ++ + snor: snor@11014000 { + compatible = "mediatek,mtk-snor"; + reg = <0x11014000 0x1000>; +--- a/configs/mt7622_rfb_defconfig ++++ b/configs/mt7622_rfb_defconfig +@@ -18,6 +18,7 @@ CONFIG_LOG=y + CONFIG_SYS_PROMPT="MT7622> " + CONFIG_CMD_BOOTMENU=y + CONFIG_CMD_MMC=y ++CONFIG_CMD_MTD=y + CONFIG_CMD_PCI=y + CONFIG_CMD_SF_TEST=y + CONFIG_CMD_PING=y +@@ -33,6 +34,10 @@ CONFIG_SYSCON=y + CONFIG_CLK=y + CONFIG_MMC_HS200_SUPPORT=y + CONFIG_MMC_MTK=y ++CONFIG_MTD=y ++CONFIG_DM_MTD=y ++CONFIG_MTK_SPI_NAND=y ++CONFIG_MTK_SPI_NAND_MTD=y + CONFIG_DM_SPI_FLASH=y + CONFIG_SPI_FLASH_EON=y + CONFIG_SPI_FLASH_GIGADEVICE=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 0000000000..2cd62a2c19 --- /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 0000000000..228bde1567 --- /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 0000000000..1c83479d0d --- /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 0000000000..b6a2229170 --- /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 0000000000..ba32fa869d --- /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 0000000000..1d62c05e40 --- /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 0000000000..08b14a6b29 --- /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/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch b/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch new file mode 100644 index 0000000000..cd1794f0d1 --- /dev/null +++ b/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch @@ -0,0 +1,66 @@ +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: + return err; + } + ++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) ++{ ++ return ech->padding1[0] == 'E' && ++ ech->padding1[1] == 'O' && ++ ech->padding1[2] == 'F'; ++} ++ + /** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object +@@ -832,9 +839,21 @@ static int scan_peb(struct ubi_device *u + return 0; + } + +- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); +- if (err < 0) +- return err; ++ if (!ai->eof_found) { ++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); ++ if (err < 0) ++ return err; ++ ++ if (ec_hdr_has_eof(ech)) { ++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", ++ pnum); ++ ai->eof_found = true; ++ } ++ } ++ ++ if (ai->eof_found) ++ err = UBI_IO_FF_BITFLIPS; ++ + switch (err) { + case 0: + break; +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -745,6 +745,7 @@ struct ubi_attach_info { + int mean_ec; + uint64_t ec_sum; + int ec_count; ++ bool eof_found; + struct kmem_cache *aeb_slab_cache; + }; + 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 0000000000..110b1bb237 --- /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 0000000000..9808e2c860 --- /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 6d4af1f174..5fb896d92d 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/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch b/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch deleted file mode 100644 index dc9c9ae5e4..0000000000 --- a/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch +++ /dev/null @@ -1,218 +0,0 @@ -From afea25576fc92d562b248b783cf03564eb4521da 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 - -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(-) - ---- 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 - struct bootmenu_data *menu = data; - struct bootmenu_entry *iter; - enum bootmenu_key key = KEY_NONE; -+ int choice = -1; - int esc = 0; - int i; - - while (1) { - if (menu->delay >= 0) { - /* Autoboot was not stopped */ -- bootmenu_autoboot_loop(menu, &key, &esc); -+ bootmenu_autoboot_loop(menu, &key, &esc, &choice); - } else { - /* Some key was pressed, so autoboot was stopped */ -- bootmenu_loop(menu, &key, &esc); -+ bootmenu_loop(menu, &key, &esc, &choice); - } - - switch (key) { -@@ -110,6 +112,12 @@ static char *bootmenu_choice_entry(void - ++menu->active; - /* no menu key selected, regenerate menu */ - return NULL; -+ case KEY_CHOICE: -+ menu->active = choice; -+ if (!menu->last_choiced) { -+ menu->last_choiced = true; -+ return NULL; -+ } - case KEY_SELECT: - iter = menu->first; - for (i = 0; i < menu->active; ++i) -@@ -181,12 +189,19 @@ static int prepare_bootmenu_entry(struct - if (!entry) - return -ENOMEM; - -- entry->title = strndup(option, sep - option); -+ entry->title = malloc((sep - option) + 4); - if (!entry->title) { - free(entry); - return -ENOMEM; - } - -+ 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 - menu->delay = delay; - menu->active = 0; - menu->first = NULL; -+ menu->last_choiced = false; - - default_str = env_get("bootmenu_default"); - if (default_str) -@@ -356,9 +372,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)) -- entry->title = strdup("U-Boot console"); -+ entry->title = strdup("0. U-Boot console"); - else -- entry->title = strdup("Quit"); -+ entry->title = strdup("0. Quit"); - - if (!entry->title) { - 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 { - int item_cnt; - }; - -+static int find_choice(char choice) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(choice_chars); i++) -+ if (tolower(choice) == choice_chars[i]) -+ return i; -+ -+ return -1; -+} -+ - /* - * 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) - } - - void bootmenu_autoboot_loop(struct bootmenu_data *menu, -- enum bootmenu_key *key, int *esc) -+ enum bootmenu_key *key, int *esc, int *choice) - { - int i, c; - -@@ -456,6 +468,19 @@ void bootmenu_autoboot_loop(struct bootm - break; - default: - *key = KEY_NONE; -+ if (*esc) -+ break; -+ -+ *choice = find_choice(c); -+ if ((*choice >= 0 && -+ *choice < menu->count - 1)) { -+ *key = KEY_CHOICE; -+ } else if (c == '0') { -+ *choice = menu->count - 1; -+ *key = KEY_CHOICE; -+ } else { -+ *key = KEY_NONE; -+ } - break; - } - -@@ -475,10 +500,16 @@ void bootmenu_autoboot_loop(struct bootm - } - - void bootmenu_loop(struct bootmenu_data *menu, -- enum bootmenu_key *key, int *esc) -+ enum bootmenu_key *key, int *esc, int *choice) - { - int c; - -+ if (menu->last_choiced) { -+ menu->last_choiced = false; -+ *key = KEY_SELECT; -+ return; -+ } -+ - if (*esc == 1) { - if (tstc()) { - c = getchar(); -@@ -504,6 +535,14 @@ void bootmenu_loop(struct bootmenu_data - if (c == '\e') { - *esc = 1; - *key = KEY_NONE; -+ } else { -+ *choice = find_choice(c); -+ if ((*choice >= 0 && *choice < menu->count - 1)) { -+ *key = KEY_CHOICE; -+ } else if (c == '0') { -+ *choice = menu->count - 1; -+ *key = KEY_CHOICE; -+ } - } - break; - case 1: ---- a/include/menu.h -+++ b/include/menu.h -@@ -40,6 +40,7 @@ struct bootmenu_data { - int active; /* active menu entry */ - int count; /* total count of menu entries */ - struct bootmenu_entry *first; /* first menu entry */ -+ bool last_choiced; - }; - - enum bootmenu_key { -@@ -48,11 +49,19 @@ enum bootmenu_key { - KEY_DOWN, - KEY_SELECT, - KEY_QUIT, -+ KEY_CHOICE, - }; - - void bootmenu_autoboot_loop(struct bootmenu_data *menu, -- enum bootmenu_key *key, int *esc); -+ 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/211-cmd-bootmenu-custom-title.patch b/package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch index f1e60c6407..72926ea2e7 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 544767e2fa..4ffbc17208 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 8630fc16ce..9fae6d056f 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 da746bb124..0000000000 --- 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 a30baf0074..8279d250f3 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, diff --git a/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch b/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch deleted file mode 100644 index 5d312eca8f..0000000000 --- a/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch +++ /dev/null @@ -1,51 +0,0 @@ ---- a/drivers/mtd/ubi/attach.c -+++ b/drivers/mtd/ubi/attach.c -@@ -802,6 +802,13 @@ out_unlock: - return err; - } - -+static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) -+{ -+ return ech->padding1[0] == 'E' && -+ ech->padding1[1] == 'O' && -+ ech->padding1[2] == 'F'; -+} -+ - /** - * scan_peb - scan and process UBI headers of a PEB. - * @ubi: UBI device description object -@@ -832,9 +839,21 @@ static int scan_peb(struct ubi_device *u - return 0; - } - -- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); -- if (err < 0) -- return err; -+ if (!ai->eof_found) { -+ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); -+ if (err < 0) -+ return err; -+ -+ if (ec_hdr_has_eof(ech)) { -+ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", -+ pnum); -+ ai->eof_found = true; -+ } -+ } -+ -+ if (ai->eof_found) -+ err = UBI_IO_FF_BITFLIPS; -+ - switch (err) { - case 0: - break; ---- a/drivers/mtd/ubi/ubi.h -+++ b/drivers/mtd/ubi/ubi.h -@@ -745,6 +745,7 @@ struct ubi_attach_info { - int mean_ec; - uint64_t ec_sum; - int ec_count; -+ bool eof_found; - struct kmem_cache *aeb_slab_cache; - }; - -- cgit v1.2.3