uboot-mediatek: update to 2021.04-rc3 with MediaTek's patches
authorDaniel Golle <daniel@makrotopia.org>
Sat, 20 Feb 2021 13:04:38 +0000 (13:04 +0000)
committerDaniel Golle <daniel@makrotopia.org>
Thu, 11 Mar 2021 16:24:53 +0000 (16:24 +0000)
MediaTek published their current U-Boot patchset on github:
https://github.com/mtk-openwrt/u-boot/commits/mtksoc

Import the platform patches from there (`00-mtk-*.patch`), arrange,
them nicely, drop no longer needed local patches and rebase on top of
U-Boot 2021.04-rc3.

Tested and works well on Linksys E8450 (snand-1ddr) as well as
Bananapi BPi-R64 (sdmmc-2ddr, emmc-2ddr).

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
48 files changed:
package/boot/uboot-mediatek/Makefile
package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-02-configs-RPi2-Disable-EFI-Grub-workaround.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch [deleted file]
package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch [deleted file]
package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch [deleted file]
package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch [deleted file]
package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch [deleted file]
package/boot/uboot-mediatek/patches/007-env-readmem.patch [deleted file]
package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch [deleted file]
package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch [deleted file]
package/boot/uboot-mediatek/patches/010-no-binman.patch [deleted file]
package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch [deleted file]
package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch [deleted file]
package/boot/uboot-mediatek/patches/016-fit-totalsize.patch [deleted file]
package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch [deleted file]
package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch [deleted file]
package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch [deleted file]
package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch [new file with mode: 0644]

index 6904b39938796c215bfd47f7477bdbd90744ee9e..efdc25a19521ad75d3807447e80f508b77c62e5c 100644 (file)
@@ -1,8 +1,8 @@
 include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
-PKG_VERSION:=2020.10
-PKG_HASH:=0d481bbdc05c0ee74908ec2f56a6daa53166cc6a78a0e4fac2ac5d025770a622
+PKG_VERSION:=2021.04-rc3
+PKG_HASH:=7c418e07f6065c8761eb2df890bb524d7109864325d8850ddb0c93eb345734f9
 PKG_BUILD_DEPENDS:=arm-trusted-firmware-tools/host
 
 include $(INCLUDE_DIR)/u-boot.mk
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
new file mode 100644 (file)
index 0000000..c398ae4
--- /dev/null
@@ -0,0 +1,69 @@
+From 5efb7855a9d33ac897d6e2a7117e4e3d35d433a5 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 11 Mar 2021 10:28:53 +0000
+Subject: [PATCH 01/21] 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
+@@ -84,7 +84,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;
+@@ -97,15 +97,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,
+@@ -124,7 +123,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;
+       }
+@@ -472,7 +471,6 @@ int 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))
+@@ -482,11 +480,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-02-configs-RPi2-Disable-EFI-Grub-workaround.patch b/package/boot/uboot-mediatek/patches/000-mtk-02-configs-RPi2-Disable-EFI-Grub-workaround.patch
new file mode 100644 (file)
index 0000000..7b54489
--- /dev/null
@@ -0,0 +1,21 @@
+From 04815ef5a49a9996acacfcb5e18924569f5e1bf5 Mon Sep 17 00:00:00 2001
+From: Matthias Brugger <mbrugger@suse.com>
+Date: Tue, 16 Feb 2021 20:54:08 +0100
+Subject: [PATCH 02/21] configs: RPi2: Disable EFI Grub workaround
+
+The EFI Grub workaround isn't needed with Grub version 2.04 or higher.
+This version was published over a year ago, so disable the workaround
+to reduce boot time.
+
+Signed-off-by: Matthias Brugger <mbrugger@suse.com>
+---
+ configs/rpi_2_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/configs/rpi_2_defconfig
++++ b/configs/rpi_2_defconfig
+@@ -42,3 +42,4 @@ CONFIG_SYS_WHITE_ON_BLACK=y
+ CONFIG_CONSOLE_SCROLL_LINES=10
+ CONFIG_PHYS_TO_BUS=y
+ CONFIG_OF_LIBFDT_OVERLAY=y
++# CONFIG_EFI_GRUB_ARM32_WORKAROUND is not set
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch b/package/boot/uboot-mediatek/patches/000-mtk-03-pinctrl-mediatek-fix-wrong-assignment-in-mtk_get_pin.patch
new file mode 100644 (file)
index 0000000..3aa6b61
--- /dev/null
@@ -0,0 +1,25 @@
+From 6f18e581a7e98db3675b4c111701263647b20781 Mon Sep 17 00:00:00 2001
+From: Sam Shih <sam.shih@mediatek.com>
+Date: Thu, 17 Dec 2020 19:29:56 +0800
+Subject: [PATCH 03/21] pinctrl: mediatek: fix wrong assignment in
+ mtk_get_pin_name
+
+This is a bug fix for mtk pinctrl common part. Appearently pins should be
+used instead of grps in mtk_get_pin_name().
+
+Signed-off-by: Sam Shih <sam.shih@mediatek.com>
+---
+ drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+@@ -219,7 +219,7 @@ static const char *mtk_get_pin_name(stru
+ {
+       struct mtk_pinctrl_priv *priv = dev_get_priv(dev);
+-      if (!priv->soc->grps[selector].name)
++      if (!priv->soc->pins[selector].name)
+               return mtk_pinctrl_dummy_name;
+       return priv->soc->pins[selector].name;
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch b/package/boot/uboot-mediatek/patches/000-mtk-04-pinctrl-mediatek-add-get_pin_muxing-ops-for-mediatek.patch
new file mode 100644 (file)
index 0000000..89b51f7
--- /dev/null
@@ -0,0 +1,43 @@
+From ca73da39ff0c9f599f75d7ccac0196030aa946b9 Mon Sep 17 00:00:00 2001
+From: Sam Shih <sam.shih@mediatek.com>
+Date: Thu, 17 Dec 2020 19:32:48 +0800
+Subject: [PATCH 04/21] pinctrl: mediatek: add get_pin_muxing ops for mediatek
+ pinctrl
+
+This patch add get_pin_muxing support for mediatek pinctrl drivers
+
+Signed-off-by: Sam Shih <sam.shih@mediatek.com>
+---
+ drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+@@ -232,6 +232,19 @@ static int mtk_get_pins_count(struct ude
+       return priv->soc->npins;
+ }
++static int mtk_get_pin_muxing(struct udevice *dev,
++                                unsigned int selector,
++                                char *buf, int size)
++{
++      int val, err;
++      err = mtk_hw_get_value(dev, selector, PINCTRL_PIN_REG_MODE, &val);
++      if (err)
++              return err;
++
++      snprintf(buf, size, "Aux Func.%d", val);
++      return 0;
++}
++
+ static const char *mtk_get_group_name(struct udevice *dev,
+                                     unsigned int selector)
+ {
+@@ -512,6 +525,7 @@ static int mtk_pinconf_group_set(struct
+ const struct pinctrl_ops mtk_pinctrl_ops = {
+       .get_pins_count = mtk_get_pins_count,
+       .get_pin_name = mtk_get_pin_name,
++      .get_pin_muxing = mtk_get_pin_muxing,
+       .get_groups_count = mtk_get_groups_count,
+       .get_group_name = mtk_get_group_name,
+       .get_functions_count = mtk_get_functions_count,
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch b/package/boot/uboot-mediatek/patches/000-mtk-05-pinctrl-mediatek-do-not-probe-gpio-driver-if-not-ena.patch
new file mode 100644 (file)
index 0000000..7a98e88
--- /dev/null
@@ -0,0 +1,58 @@
+From d3fbbef13853a695cdea75a980a3d6bd150a68c1 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 11 Jan 2021 10:17:15 +0800
+Subject: [PATCH 05/21] pinctrl: mediatek: do not probe gpio driver if not
+ enabled
+
+The mtk pinctrl driver is a combination driver with support for both
+pinctrl and gpio. When this driver is used in SPL, gpio support may not be
+enabled, and this will result in a compilation error.
+
+To fix this, macros are added to make sure gpio related code will only be
+compiled when gpio support is enabled.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+@@ -540,6 +540,8 @@ const struct pinctrl_ops mtk_pinctrl_ops
+       .set_state = pinctrl_generic_set_state,
+ };
++#if CONFIG_IS_ENABLED(DM_GPIO) || \
++    (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT))
+ static int mtk_gpio_get(struct udevice *dev, unsigned int off)
+ {
+       int val, err;
+@@ -647,12 +649,13 @@ static int mtk_gpiochip_register(struct
+       return 0;
+ }
++#endif
+ int mtk_pinctrl_common_probe(struct udevice *dev,
+                            struct mtk_pinctrl_soc *soc)
+ {
+       struct mtk_pinctrl_priv *priv = dev_get_priv(dev);
+-      int ret;
++      int ret = 0;
+       priv->base = dev_read_addr_ptr(dev);
+       if (!priv->base)
+@@ -660,9 +663,10 @@ int mtk_pinctrl_common_probe(struct udev
+       priv->soc = soc;
++#if CONFIG_IS_ENABLED(DM_GPIO) || \
++    (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT))
+       ret = mtk_gpiochip_register(dev);
+-      if (ret)
+-              return ret;
++#endif
+-      return 0;
++      return ret;
+ }
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch b/package/boot/uboot-mediatek/patches/000-mtk-06-pinctrl-mt7629-add-jtag-function-and-pin-group.patch
new file mode 100644 (file)
index 0000000..f4cc1eb
--- /dev/null
@@ -0,0 +1,50 @@
+From 1c6d07abf7fc79bf3950dc9ac56e3b566c334d3d Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 13 Jan 2021 16:29:23 +0800
+Subject: [PATCH 06/21] pinctrl: mt7629: add jtag function and pin group
+
+The EPHY LEDs of mt7629 can be used as JTAG. This patch adds the jtag pin
+group to the pinctrl driver.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/pinctrl/mediatek/pinctrl-mt7629.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/pinctrl/mediatek/pinctrl-mt7629.c
++++ b/drivers/pinctrl/mediatek/pinctrl-mt7629.c
+@@ -201,6 +201,10 @@ static int mt7629_wf2g_led_funcs[] = { 1
+ static int mt7629_wf5g_led_pins[] = { 18, };
+ static int mt7629_wf5g_led_funcs[] = { 1, };
++/* LED for EPHY used as JTAG */
++static int mt7629_ephy_leds_jtag_pins[] = { 12, 13, 14, 15, 16, };
++static int mt7629_ephy_leds_jtag_funcs[] = { 7, 7, 7, 7, 7, };
++
+ /* Watchdog */
+ static int mt7629_watchdog_pins[] = { 11, };
+ static int mt7629_watchdog_funcs[] = { 1, };
+@@ -297,6 +301,7 @@ static const struct mtk_group_desc mt762
+       PINCTRL_PIN_GROUP("ephy_led2", mt7629_ephy_led2),
+       PINCTRL_PIN_GROUP("ephy_led3", mt7629_ephy_led3),
+       PINCTRL_PIN_GROUP("ephy_led4", mt7629_ephy_led4),
++      PINCTRL_PIN_GROUP("ephy_leds_jtag", mt7629_ephy_leds_jtag),
+       PINCTRL_PIN_GROUP("wf2g_led", mt7629_wf2g_led),
+       PINCTRL_PIN_GROUP("wf5g_led", mt7629_wf5g_led),
+       PINCTRL_PIN_GROUP("watchdog", mt7629_watchdog),
+@@ -364,6 +369,7 @@ static const char *const mt7629_uart_gro
+ static const char *const mt7629_wdt_groups[] = { "watchdog", };
+ static const char *const mt7629_wifi_groups[] = { "wf0_5g", "wf0_2g", };
+ static const char *const mt7629_flash_groups[] = { "snfi", "spi_nor" };
++static const char *const mt7629_jtag_groups[] = { "ephy_leds_jtag" };
+ static const struct mtk_function_desc mt7629_functions[] = {
+       {"eth", mt7629_ethernet_groups, ARRAY_SIZE(mt7629_ethernet_groups)},
+@@ -376,6 +382,7 @@ static const struct mtk_function_desc mt
+       {"watchdog", mt7629_wdt_groups, ARRAY_SIZE(mt7629_wdt_groups)},
+       {"wifi", mt7629_wifi_groups, ARRAY_SIZE(mt7629_wifi_groups)},
+       {"flash", mt7629_flash_groups, ARRAY_SIZE(mt7629_flash_groups)},
++      {"jtag", mt7629_jtag_groups, ARRAY_SIZE(mt7629_jtag_groups)},
+ };
+ static struct mtk_pinctrl_soc mt7629_data = {
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch b/package/boot/uboot-mediatek/patches/000-mtk-07-configs-mt7622-use-ARMv8-Generic-Timer-instead-of-mt.patch
new file mode 100644 (file)
index 0000000..060c9aa
--- /dev/null
@@ -0,0 +1,25 @@
+From c47a5b927787a463eff8f67339d91e60fe0381c4 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 2 Mar 2021 15:02:50 +0800
+Subject: [PATCH 07/21] configs: mt7622: use ARMv8 Generic Timer instead of
+ mtk_timer
+
+It's better to use the generic timer which is correctly initialized by
+the ATF. The generic timer has higher resolution than the mtk_timer.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ configs/mt7622_rfb_defconfig | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/configs/mt7622_rfb_defconfig
++++ b/configs/mt7622_rfb_defconfig
+@@ -51,8 +51,6 @@ CONFIG_SPI=y
+ CONFIG_DM_SPI=y
+ CONFIG_MTK_SNOR=y
+ CONFIG_SYSRESET_WATCHDOG=y
+-CONFIG_TIMER=y
+-CONFIG_MTK_TIMER=y
+ CONFIG_WDT_MTK=y
+ CONFIG_LZO=y
+ CONFIG_HEXDUMP=y
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch b/package/boot/uboot-mediatek/patches/000-mtk-08-dts-mt7629-enable-JTAG-pins-by-default.patch
new file mode 100644 (file)
index 0000000..f9f783e
--- /dev/null
@@ -0,0 +1,50 @@
+From 4bee3f9e285007ccf77ca9916fff3d93fc4d8a80 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 2 Mar 2021 15:43:27 +0800
+Subject: [PATCH 08/21] dts: mt7629: enable JTAG pins by default
+
+The EPHY LEDs belongs to the built-in FE switch of MT7629, which is barely
+used. These LED pins on reference boards are used as JTAG socket. So it's
+a good idea to change the default state to JTAG, and this will make it
+convenience for debugging.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ arch/arm/dts/mt7629-rfb.dts | 10 ++++++++++
+ arch/arm/dts/mt7629.dtsi    |  6 ++++++
+ 2 files changed, 16 insertions(+)
+
+--- a/arch/arm/dts/mt7629-rfb.dts
++++ b/arch/arm/dts/mt7629-rfb.dts
+@@ -36,6 +36,16 @@
+ };
+ &pinctrl {
++      state_default: pinmux_conf {
++              u-boot,dm-pre-reloc;
++
++              mux {
++                      function = "jtag";
++                      groups = "ephy_leds_jtag";
++                      u-boot,dm-pre-reloc;
++              };
++      };
++
+       snfi_pins: snfi-pins {
+               mux {
+                       function = "flash";
+--- a/arch/arm/dts/mt7629.dtsi
++++ b/arch/arm/dts/mt7629.dtsi
+@@ -152,6 +152,12 @@
+               compatible = "mediatek,mt7629-pinctrl";
+               reg = <0x10217000 0x8000>;
++              pinctrl-names = "default";
++              pinctrl-0 = <&state_default>;
++
++              state_default: pinmux_conf {
++              };
++
+               gpio: gpio-controller {
+                       gpio-controller;
+                       #gpio-cells = <2>;
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
new file mode 100644 (file)
index 0000000..56a40ca
--- /dev/null
@@ -0,0 +1,44 @@
+From f3f320af7078a8c5647d870a31c1d3695dacd7cf Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 2 Mar 2021 15:47:45 +0800
+Subject: [PATCH 09/21] board: mediatek: add more network configurations
+
+Make the network configurations uniform for mediatek boards
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ 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
+@@ -36,6 +36,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
+@@ -54,6 +54,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
+@@ -52,5 +52,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-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch b/package/boot/uboot-mediatek/patches/000-mtk-10-mmc-mtk-sd-increase-the-minimum-bus-frequency.patch
new file mode 100644 (file)
index 0000000..658cbc1
--- /dev/null
@@ -0,0 +1,38 @@
+From ed880b7572e1135e3bd8382d4670a375f7d9c91b Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 2 Mar 2021 15:56:17 +0800
+Subject: [PATCH 10/21] mmc: mtk-sd: increase the minimum bus frequency
+
+With a 48MHz input clock, the lowest bus frequency can be as low as
+48000000 / (4 * 4095) = 2930Hz. Such an extremely low frequency will cause
+the mmc framework take seconds to finish the initialization.
+
+Limiting the minimum bus frequency to a slightly higher value can solve the
+issue without any side effects.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mmc/mtk-sd.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/mmc/mtk-sd.c
++++ b/drivers/mmc/mtk-sd.c
+@@ -232,6 +232,8 @@
+ #define SCLK_CYCLES_SHIFT             20
++#define MIN_BUS_CLK                   260000
++
+ #define CMD_INTS_MASK \
+       (MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO)
+@@ -1639,6 +1641,9 @@ static int msdc_drv_probe(struct udevice
+       else
+               cfg->f_min = host->src_clk_freq / (4 * 4095);
++      if (cfg->f_min < MIN_BUS_CLK)
++              cfg->f_min = MIN_BUS_CLK;
++
+       cfg->f_max = host->src_clk_freq;
+       cfg->b_max = 1024;
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch b/package/boot/uboot-mediatek/patches/000-mtk-11-serial-serial-mtk-rewrite-the-setbrg-function.patch
new file mode 100644 (file)
index 0000000..2ce7733
--- /dev/null
@@ -0,0 +1,141 @@
+From d8bde59186dafdea5bbe8d29d3a6ae7cac98e9d0 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jan 2021 11:19:08 +0800
+Subject: [PATCH 11/21] serial: serial-mtk: rewrite the setbrg function
+
+Currently the setbrg logic of serial-mtk is messy, and should be rewritten.
+Also an option is added to make it possible to use highspeed=3 mode for all
+bauds.
+
+The new logic is:
+1. If baud clock > 12MHz
+   a) If baud <= 115200, highspeed=0 mode will be used (ns16550 compatible)
+   b) If baud <= 576000, highspeed=2 mode will be used
+   c) any baud > 576000, highspeed=3 mode will be used
+2. If baud clock <= 12MHz
+   Always uses highspeed=3 mode
+   a) If baud <= 115200, calculates the divisor using DIV_ROUND_CLOSEST
+   b) any baud > 115200, the same as 1. c)
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/serial/serial_mtk.c | 74 +++++++++++++++++--------------------
+ 1 file changed, 33 insertions(+), 41 deletions(-)
+
+--- a/drivers/serial/serial_mtk.c
++++ b/drivers/serial/serial_mtk.c
+@@ -73,74 +73,64 @@ struct mtk_serial_regs {
+ struct mtk_serial_priv {
+       struct mtk_serial_regs __iomem *regs;
+       u32 clock;
++      bool force_highspeed;
+ };
+ static void _mtk_serial_setbrg(struct mtk_serial_priv *priv, int baud)
+ {
+-      bool support_clk12m_baud115200;
+-      u32 quot, samplecount, realbaud;
++      u32 quot, realbaud, samplecount = 1;
+-      if ((baud <= 115200) && (priv->clock == 12000000))
+-              support_clk12m_baud115200 = true;
+-      else
+-              support_clk12m_baud115200 = false;
++      /* Special case for low baud clock */
++      if ((baud <= 115200) && (priv->clock == 12000000)) {
++              writel(3, &priv->regs->highspeed);
++
++              quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud);
++              if (quot == 0)
++                      quot = 1;
++
++              samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud);
++
++              realbaud = priv->clock / samplecount / quot;
++              if ((realbaud > BAUD_ALLOW_MAX(baud)) ||
++                  (realbaud < BAUD_ALLOW_MIX(baud))) {
++                      pr_info("baud %d can't be handled\n", baud);
++              }
++
++              goto set_baud;
++      }
++
++      if (priv->force_highspeed)
++              goto use_hs3;
+       if (baud <= 115200) {
+               writel(0, &priv->regs->highspeed);
+               quot = DIV_ROUND_CLOSEST(priv->clock, 16 * baud);
+-
+-              if (support_clk12m_baud115200) {
+-                      writel(3, &priv->regs->highspeed);
+-                      quot = DIV_ROUND_CLOSEST(priv->clock, 256 * baud);
+-                      if (quot == 0)
+-                              quot = 1;
+-
+-                      samplecount = DIV_ROUND_CLOSEST(priv->clock,
+-                                                      quot * baud);
+-                      if (samplecount != 0) {
+-                              realbaud = priv->clock / samplecount / quot;
+-                              if ((realbaud > BAUD_ALLOW_MAX(baud)) ||
+-                                  (realbaud < BAUD_ALLOW_MIX(baud))) {
+-                                      pr_info("baud %d can't be handled\n",
+-                                              baud);
+-                              }
+-                      } else {
+-                              pr_info("samplecount is 0\n");
+-                      }
+-              }
+       } else if (baud <= 576000) {
+               writel(2, &priv->regs->highspeed);
+               /* Set to next lower baudrate supported */
+               if ((baud == 500000) || (baud == 576000))
+                       baud = 460800;
++
+               quot = DIV_ROUND_UP(priv->clock, 4 * baud);
+       } else {
++use_hs3:
+               writel(3, &priv->regs->highspeed);
++
+               quot = DIV_ROUND_UP(priv->clock, 256 * baud);
++              samplecount = DIV_ROUND_CLOSEST(priv->clock, quot * baud);
+       }
++set_baud:
+       /* set divisor */
+       writel(UART_LCR_WLS_8 | UART_LCR_DLAB, &priv->regs->lcr);
+       writel(quot & 0xff, &priv->regs->dll);
+       writel((quot >> 8) & 0xff, &priv->regs->dlm);
+       writel(UART_LCR_WLS_8, &priv->regs->lcr);
+-      if (baud > 460800) {
+-              u32 tmp;
+-
+-              tmp = DIV_ROUND_CLOSEST(priv->clock, quot * baud);
+-              writel(tmp - 1, &priv->regs->sample_count);
+-              writel((tmp - 2) >> 1, &priv->regs->sample_point);
+-      } else {
+-              writel(0, &priv->regs->sample_count);
+-              writel(0xff, &priv->regs->sample_point);
+-      }
+-
+-      if (support_clk12m_baud115200) {
+-              writel(samplecount - 1, &priv->regs->sample_count);
+-              writel((samplecount - 2) >> 1, &priv->regs->sample_point);
+-      }
++      /* set highspeed mode sample count & point */
++      writel(samplecount - 1, &priv->regs->sample_count);
++      writel((samplecount - 2) >> 1, &priv->regs->sample_point);
+ }
+ static int _mtk_serial_putc(struct mtk_serial_priv *priv, const char ch)
+@@ -248,6 +238,8 @@ static int mtk_serial_of_to_plat(struct
+               return -EINVAL;
+       }
++      priv->force_highspeed = dev_read_bool(dev, "mediatek,force-highspeed");
++
+       return 0;
+ }
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch b/package/boot/uboot-mediatek/patches/000-mtk-12-board-mt7629-enable-compression-of-u-boot-to-reduce-.patch
new file mode 100644 (file)
index 0000000..3b5545d
--- /dev/null
@@ -0,0 +1,94 @@
+From a80ef99cb308904b82662deb66c5ed7a6ff59928 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 11:13:36 +0800
+Subject: [PATCH 12/21] board: mt7629: enable compression of u-boot to reduce
+ the size of final image
+
+This patch makes use of the decompression mechanism implemented for mt7628
+previously to reduce the total image size. Binman will be also removed.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ Makefile                            |  3 +++
+ arch/arm/dts/mt7629-rfb-u-boot.dtsi | 18 ------------------
+ arch/arm/mach-mediatek/Kconfig      |  1 -
+ configs/mt7629_rfb_defconfig        |  6 ++++++
+ 4 files changed, 9 insertions(+), 19 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -1728,6 +1728,9 @@ u-boot-elf.lds: arch/u-boot-elf.lds prep
+ ifeq ($(CONFIG_SPL),y)
+ spl/u-boot-spl-mtk.bin: spl/u-boot-spl
++
++u-boot-mtk.bin: u-boot-with-spl.bin
++      $(call if_changed,copy)
+ else
+ MKIMAGEFLAGS_u-boot-mtk.bin = -T mtk_image \
+       -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \
+--- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi
++++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi
+@@ -5,24 +5,6 @@
+  * Author: Weijie Gao <weijie.gao@mediatek.com>
+  */
+-#include <config.h>
+-/ {
+-      binman {
+-              filename = "u-boot-mtk.bin";
+-              pad-byte = <0xff>;
+-
+-#ifdef CONFIG_SPL
+-              blob {
+-                      filename = "spl/u-boot-spl-mtk.bin";
+-                      size = <CONFIG_SPL_PAD_TO>;
+-              };
+-
+-              u-boot-img {
+-              };
+-#endif
+-      };
+-};
+-
+ &infracfg {
+       u-boot,dm-pre-reloc;
+ };
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -36,7 +36,6 @@ config TARGET_MT7629
+       bool "MediaTek MT7629 SoC"
+       select CPU_V7A
+       select SPL
+-      select BINMAN
+       help
+         The MediaTek MT7629 is a ARM-based SoC with a dual-core Cortex-A7
+         including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet,
+--- a/configs/mt7629_rfb_defconfig
++++ b/configs/mt7629_rfb_defconfig
+@@ -10,7 +10,11 @@ CONFIG_SPL_TEXT_BASE=0x201000
+ CONFIG_TARGET_MT7629=y
+ CONFIG_SPL_SERIAL_SUPPORT=y
+ CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
++CONFIG_SPL_STACK_R_ADDR=0x40800000
++CONFIG_SPL_PAYLOAD="u-boot-lzma.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
+@@ -18,6 +22,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7629-rfb"
+ CONFIG_SYS_CONSOLE_IS_IN_ENV=y
+ # CONFIG_DISPLAY_BOARDINFO is not set
+ CONFIG_SPL_SYS_MALLOC_SIMPLE=y
++CONFIG_SPL_STACK_R=y
+ CONFIG_SPL_NOR_SUPPORT=y
+ CONFIG_SPL_WATCHDOG_SUPPORT=y
+ CONFIG_HUSH_PARSER=y
+@@ -87,4 +92,5 @@ CONFIG_USB_STORAGE=y
+ CONFIG_USB_KEYBOARD=y
+ CONFIG_WDT_MTK=y
+ CONFIG_LZMA=y
++CONFIG_SPL_LZMA=y
+ # CONFIG_EFI_LOADER is not set
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch b/package/boot/uboot-mediatek/patches/000-mtk-13-configs-mt7622-enable-debug-uart-for-mt7622_rfb_defc.patch
new file mode 100644 (file)
index 0000000..6b83214
--- /dev/null
@@ -0,0 +1,26 @@
+From acd49b1549ff52286aace5e841420aa750325f8b Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 10:53:14 +0800
+Subject: [PATCH 13/21] configs: mt7622: enable debug uart for
+ mt7622_rfb_defconfig
+
+Enable debug uart for mt7622_rfb_defconfig
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ configs/mt7622_rfb_defconfig | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/configs/mt7622_rfb_defconfig
++++ b/configs/mt7622_rfb_defconfig
+@@ -4,7 +4,10 @@ CONFIG_ARCH_MEDIATEK=y
+ CONFIG_SYS_TEXT_BASE=0x41e00000
+ CONFIG_SYS_MALLOC_F_LEN=0x4000
+ CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=25000000
+ CONFIG_DEFAULT_DEVICE_TREE="mt7622-rfb"
++CONFIG_DEBUG_UART=y
+ CONFIG_FIT=y
+ CONFIG_DEFAULT_FDT_FILE="mt7622-rfb"
+ CONFIG_LOGLEVEL=7
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
new file mode 100644 (file)
index 0000000..a01d9e2
--- /dev/null
@@ -0,0 +1,3698 @@
+From f22a055a9f589f1ec614045eba3cb0c5fd887feb Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 2 Mar 2021 16:58:01 +0800
+Subject: [PATCH 14/21] 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 <weijie.gao@mediatek.com>
+---
+ 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
+@@ -108,6 +108,8 @@ config HBMC_AM654
+        This is the driver for HyperBus controller on TI's AM65x and
+        other SoCs
++source "drivers/mtd/mtk-snand/Kconfig"
++
+ source "drivers/mtd/nand/Kconfig"
+ source "drivers/mtd/spi/Kconfig"
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -40,3 +40,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 <weijie.gao@mediatek.com>
++#
++# 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 <weijie.gao@mediatek.com>
++#
++# 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 <weijie.gao@mediatek.com>
++ */
++
++#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 <mtk-snand.h>
++#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 <weijie.gao@mediatek.com>
++ */
++
++#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 <weijie.gao@mediatek.com>
++ */
++
++#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,526 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <common.h>
++#include <dm.h>
++#include <malloc.h>
++#include <mapmem.h>
++#include <linux/mtd/mtd.h>
++#include <watchdog.h>
++
++#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)
++              mtd_erase_callback(instr);
++      else
++              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 <weijie.gao@mediatek.com>
++ */
++
++#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 <weijie.gao@mediatek.com>
++ */
++
++#ifndef _MTK_SNAND_OS_H_
++#define _MTK_SNAND_OS_H_
++
++#include <common.h>
++#include <cpu_func.h>
++#include <errno.h>
++#include <div64.h>
++#include <malloc.h>
++#include <stdbool.h>
++#include <stdarg.h>
++#include <linux/types.h>
++#include <asm/io.h>
++#include <linux/bitops.h>
++#include <linux/sizes.h>
++#include <linux/iopoll.h>
++
++#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 <weijie.gao@mediatek.com>
++ */
++
++#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 <weijie.gao@mediatek.com>
++ */
++
++#ifndef _MTK_SNAND_H_
++#define _MTK_SNAND_H_
++
++#ifndef PRIVATE_MTK_SNAND_HEADER
++#include <stddef.h>
++#include <stdint.h>
++#include <stdbool.h>
++#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
new file mode 100644 (file)
index 0000000..0ec20eb
--- /dev/null
@@ -0,0 +1,174 @@
+From 0c857d4c9cd9dc8e8ebba18cf9e9b10513ccb35d Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 08:57:29 +0800
+Subject: [PATCH 15/21] 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 <weijie.gao@mediatek.com>
+---
+ 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 <weijie.gao@mediatek.com>
++ */
++
++#include <common.h>
++#include <dm.h>
++#include <dm/uclass.h>
++#include <malloc.h>
++#include <mapmem.h>
++#include <mtd.h>
++#include <watchdog.h>
++
++#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
new file mode 100644 (file)
index 0000000..932c4b7
--- /dev/null
@@ -0,0 +1,409 @@
+From eaa9bc597e0bf8bcd1486ea49c8c7c070a37a8aa Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 10:11:32 +0800
+Subject: [PATCH 16/21] env: add support for generic MTD device
+
+Add an env driver for generic MTD device.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ 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
+@@ -50,6 +50,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)       || \
+@@ -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|\
++# 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
+@@ -19,7 +19,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
+@@ -207,6 +207,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
+@@ -513,10 +534,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 0x88000 if ARCH_SUNXI
+@@ -560,6 +587,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 <weijie.gao@mediatek.com>
++ */
++
++#include <command.h>
++#include <env.h>
++#include <env_internal.h>
++#include <errno.h>
++#include <linux/kernel.h>
++#include <linux/stddef.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <malloc.h>
++#include <memalign.h>
++#include <mtd.h>
++#include <search.h>
++
++#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
+@@ -131,6 +131,7 @@ enum env_location {
+       ENVL_FAT,
+       ENVL_FLASH,
+       ENVL_MMC,
++      ENVL_MTD,
+       ENVL_NAND,
+       ENVL_NVRAM,
+       ENVL_ONENAND,
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -22,6 +22,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
new file mode 100644 (file)
index 0000000..aa38b27
--- /dev/null
@@ -0,0 +1,266 @@
+From 47b386259625061b376f538055a4f3fbd0ab7fef Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 10:48:53 +0800
+Subject: [PATCH 17/21] 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 <weijie.gao@mediatek.com>
+---
+ 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>;
+--- a/board/mediatek/mt7629/Kconfig
++++ b/board/mediatek/mt7629/Kconfig
+@@ -12,6 +12,39 @@ config MTK_SPL_PAD_SIZE
+ config MTK_BROM_HEADER_INFO
+       string
+-      default "media=nor"
++      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
+--- /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
+@@ -30,12 +30,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
new file mode 100644 (file)
index 0000000..2202911
--- /dev/null
@@ -0,0 +1,76 @@
+From ec0d1899b035700a657721761ff6370b940450ab Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 10:51:43 +0800
+Subject: [PATCH 18/21] board: mt7622: use new spi-nand driver
+
+Enable new spi-nand driver support for mt7622_rfb_defconfig
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ 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
+@@ -15,6 +15,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_CLK=y
+ CONFIG_DM_MMC=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-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch b/package/boot/uboot-mediatek/patches/000-mtk-19-configs-mt7629-remove-unused-options-and-add-dm-comm.patch
new file mode 100644 (file)
index 0000000..fadb274
--- /dev/null
@@ -0,0 +1,31 @@
+From 2f7aaf3c2c127bd53d5e8bfe39e808fdd6eb99be Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 3 Mar 2021 12:12:39 +0800
+Subject: [PATCH 19/21] configs: mt7629: remove unused options and add dm
+ command
+
+Remove unused bootm options
+Add dm command
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ configs/mt7629_rfb_defconfig | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/configs/mt7629_rfb_defconfig
++++ b/configs/mt7629_rfb_defconfig
+@@ -28,9 +28,14 @@ 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_SF_TEST=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
new file mode 100644 (file)
index 0000000..228c335
--- /dev/null
@@ -0,0 +1,33 @@
+From e5a71a0eebadfb3d75d8619a8b317eec58b2bca2 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Sat, 6 Mar 2021 16:29:33 +0800
+Subject: [PATCH 20/21] configs: mt7622: enable environment for mt7622_rfb
+
+Enable environment vairables for mt7622_rfb
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ configs/mt7622_rfb_defconfig | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/configs/mt7622_rfb_defconfig
++++ b/configs/mt7622_rfb_defconfig
+@@ -4,6 +4,8 @@ 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=0x280000
+ CONFIG_DEBUG_UART_BASE=0x11002000
+ CONFIG_DEBUG_UART_CLOCK=25000000
+ CONFIG_DEFAULT_DEVICE_TREE="mt7622-rfb"
+@@ -21,6 +23,9 @@ CONFIG_CMD_SF_TEST=y
+ CONFIG_CMD_PING=y
+ CONFIG_CMD_SMC=y
+ CONFIG_ENV_OVERWRITE=y
++CONFIG_ENV_IS_IN_MTD=y
++CONFIG_ENV_MTD_NAME="spi-nand0"
++CONFIG_ENV_SIZE_REDUND=0x40000
+ CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+ CONFIG_NET_RANDOM_ETHADDR=y
+ CONFIG_REGMAP=y
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch b/package/boot/uboot-mediatek/patches/000-mtk-21-mmc-mtk-sd-don-t-ignore-max-frequency-from-device-tr.patch
new file mode 100644 (file)
index 0000000..37810f9
--- /dev/null
@@ -0,0 +1,31 @@
+From 4bdab0ea008113dda4e001ab8d6863945000c1b2 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Thu, 11 Mar 2021 14:58:26 +0000
+Subject: [PATCH 21/21] mmc: mtk-sd: don't ignore max-frequency from device
+ tree
+
+commit e58e68d9 ("mmc: mtk-sd: assign plat->cfg.f_max with a correct value")
+wrongly assumed that plat->cfg.f_max is always unset at the time
+mscd_drv_probe() is run. This is not true in case max-frequency being
+defined in device tree, as it is then already set by mmc_of_parser()
+in msdc_of_to_plat().
+Only set plat->cfg.f_max to the default maximum value in case it is
+not already set to a sane value.
+
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/mmc/mtk-sd.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/mmc/mtk-sd.c
++++ b/drivers/mmc/mtk-sd.c
+@@ -1644,7 +1644,8 @@ static int msdc_drv_probe(struct udevice
+       if (cfg->f_min < MIN_BUS_CLK)
+               cfg->f_min = MIN_BUS_CLK;
+-      cfg->f_max = host->src_clk_freq;
++      if (cfg->f_max < cfg->f_min || cfg->f_max > host->src_clk_freq)
++              cfg->f_max = host->src_clk_freq;
+       cfg->b_max = 1024;
+       cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
diff --git a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch
deleted file mode 100644 (file)
index 5d3e94a..0000000
+++ /dev/null
@@ -1,8540 +0,0 @@
-From de8b6cf615be20b25d0f3c817866de2c0d46a704 Mon Sep 17 00:00:00 2001
-From: Sam Shih <sam.shih@mediatek.com>
-Date: Mon, 20 Apr 2020 17:10:05 +0800
-Subject: [PATCH 1/3] nand: add spi nand driver
-
-Add spi nand driver support for mt7622 based on nfi controller
-
-Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
----
- drivers/mtd/Kconfig                           |    7 +
- drivers/mtd/Makefile                          |    4 +
- drivers/mtd/nand/raw/nand.c                   |    2 +
- drivers/mtd/nandx/NOTICE                      |   52 +
- drivers/mtd/nandx/Nandx.config                |   17 +
- drivers/mtd/nandx/Nandx.mk                    |   91 ++
- drivers/mtd/nandx/README                      |   31 +
- drivers/mtd/nandx/core/Nandx.mk               |   38 +
- drivers/mtd/nandx/core/core_io.c              |  735 +++++++++
- drivers/mtd/nandx/core/core_io.h              |   39 +
- drivers/mtd/nandx/core/nand/device_spi.c      |  200 +++
- drivers/mtd/nandx/core/nand/device_spi.h      |  132 ++
- drivers/mtd/nandx/core/nand/nand_spi.c        |  526 +++++++
- drivers/mtd/nandx/core/nand/nand_spi.h        |   35 +
- drivers/mtd/nandx/core/nand_base.c            |  304 ++++
- drivers/mtd/nandx/core/nand_base.h            |   71 +
- drivers/mtd/nandx/core/nand_chip.c            |  272 ++++
- drivers/mtd/nandx/core/nand_chip.h            |  103 ++
- drivers/mtd/nandx/core/nand_device.c          |  285 ++++
- drivers/mtd/nandx/core/nand_device.h          |  608 ++++++++
- drivers/mtd/nandx/core/nfi.h                  |   51 +
- drivers/mtd/nandx/core/nfi/nfi_base.c         | 1357 +++++++++++++++++
- drivers/mtd/nandx/core/nfi/nfi_base.h         |   95 ++
- drivers/mtd/nandx/core/nfi/nfi_regs.h         |  114 ++
- drivers/mtd/nandx/core/nfi/nfi_spi.c          |  689 +++++++++
- drivers/mtd/nandx/core/nfi/nfi_spi.h          |   44 +
- drivers/mtd/nandx/core/nfi/nfi_spi_regs.h     |   64 +
- drivers/mtd/nandx/core/nfi/nfiecc.c           |  510 +++++++
- drivers/mtd/nandx/core/nfi/nfiecc.h           |   90 ++
- drivers/mtd/nandx/core/nfi/nfiecc_regs.h      |   51 +
- drivers/mtd/nandx/driver/Nandx.mk             |   18 +
- drivers/mtd/nandx/driver/bbt/bbt.c            |  408 +++++
- drivers/mtd/nandx/driver/uboot/driver.c       |  574 +++++++
- drivers/mtd/nandx/include/Nandx.mk            |   16 +
- drivers/mtd/nandx/include/internal/bbt.h      |   62 +
- .../mtd/nandx/include/internal/nandx_core.h   |  250 +++
- .../mtd/nandx/include/internal/nandx_errno.h  |   40 +
- .../mtd/nandx/include/internal/nandx_util.h   |  221 +++
- drivers/mtd/nandx/include/uboot/nandx_os.h    |   78 +
- include/configs/mt7622.h                      |   25 +
- 40 files changed, 8309 insertions(+)
- create mode 100644 drivers/mtd/nandx/NOTICE
- create mode 100644 drivers/mtd/nandx/Nandx.config
- create mode 100644 drivers/mtd/nandx/Nandx.mk
- create mode 100644 drivers/mtd/nandx/README
- create mode 100644 drivers/mtd/nandx/core/Nandx.mk
- create mode 100644 drivers/mtd/nandx/core/core_io.c
- create mode 100644 drivers/mtd/nandx/core/core_io.h
- create mode 100644 drivers/mtd/nandx/core/nand/device_spi.c
- create mode 100644 drivers/mtd/nandx/core/nand/device_spi.h
- create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.c
- create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.h
- create mode 100644 drivers/mtd/nandx/core/nand_base.c
- create mode 100644 drivers/mtd/nandx/core/nand_base.h
- create mode 100644 drivers/mtd/nandx/core/nand_chip.c
- create mode 100644 drivers/mtd/nandx/core/nand_chip.h
- create mode 100644 drivers/mtd/nandx/core/nand_device.c
- create mode 100644 drivers/mtd/nandx/core/nand_device.h
- create mode 100644 drivers/mtd/nandx/core/nfi.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.c
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_regs.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.c
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.c
- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.h
- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc_regs.h
- create mode 100644 drivers/mtd/nandx/driver/Nandx.mk
- create mode 100644 drivers/mtd/nandx/driver/bbt/bbt.c
- create mode 100644 drivers/mtd/nandx/driver/uboot/driver.c
- create mode 100644 drivers/mtd/nandx/include/Nandx.mk
- create mode 100644 drivers/mtd/nandx/include/internal/bbt.h
- create mode 100644 drivers/mtd/nandx/include/internal/nandx_core.h
- create mode 100644 drivers/mtd/nandx/include/internal/nandx_errno.h
- create mode 100644 drivers/mtd/nandx/include/internal/nandx_util.h
- create mode 100644 drivers/mtd/nandx/include/uboot/nandx_os.h
-
---- a/drivers/mtd/Kconfig
-+++ b/drivers/mtd/Kconfig
-@@ -108,6 +108,13 @@ config HBMC_AM654
-        This is the driver for HyperBus controller on TI's AM65x and
-        other SoCs
-+config MTK_SPI_NAND
-+      tristate "Mediatek SPI Nand"
-+      depends on DM_MTD
-+      help
-+        This option will support SPI Nand device via Mediatek
-+        NFI controller.
-+
- source "drivers/mtd/nand/Kconfig"
- source "drivers/mtd/spi/Kconfig"
---- a/drivers/mtd/Makefile
-+++ b/drivers/mtd/Makefile
-@@ -41,3 +41,7 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR
- obj-$(CONFIG_SPL_UBI) += ubispl/
- endif
-+
-+ifeq ($(CONFIG_MTK_SPI_NAND), y)
-+include $(srctree)/drivers/mtd/nandx/Nandx.mk
-+endif
---- a/drivers/mtd/nand/raw/nand.c
-+++ b/drivers/mtd/nand/raw/nand.c
-@@ -91,8 +91,10 @@ static void nand_init_chip(int i)
-       if (board_nand_init(nand))
-               return;
-+#ifndef CONFIG_MTK_SPI_NAND
-       if (nand_scan(mtd, maxchips))
-               return;
-+#endif
-       nand_register(i, mtd);
- }
---- /dev/null
-+++ b/drivers/mtd/nandx/NOTICE
-@@ -0,0 +1,52 @@
-+
-+/*
-+ * Nandx - Mediatek Common Nand Driver
-+ * Copyright (C) 2017 MediaTek Inc.
-+ *
-+ * Nandx is dual licensed: you can use it either under the terms of
-+ * the GPL, or the BSD license, at your option.
-+ *
-+ *  a) This program is free software; you can redistribute it and/or modify
-+ *     it under the terms of the GNU General Public License version 2 as
-+ *     published by the Free Software Foundation.
-+ *
-+ *     This library is distributed in the hope that it will be useful,
-+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ *     GNU General Public License for more details.
-+ *
-+ *     This program is distributed in the hope that it will be useful,
-+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-+ *     See http://www.gnu.org/licenses/gpl-2.0.html for more details.
-+ *
-+ * Alternatively,
-+ *
-+ *  b) Redistribution and use in source and binary forms, with or
-+ *     without modification, are permitted provided that the following
-+ *     conditions are met:
-+ *
-+ *     1. Redistributions of source code must retain the above
-+ *        copyright notice, this list of conditions and the following
-+ *        disclaimer.
-+ *     2. Redistributions in binary form must reproduce the above
-+ *        copyright notice, this list of conditions and the following
-+ *        disclaimer in the documentation and/or other materials
-+ *        provided with the distribution.
-+ *
-+ *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-+ *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
-+ *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-+ *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-+ *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-+ *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-+ *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-+ *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-+ *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-+ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-+ *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-+ *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
-+ *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-+ */
-+
-+####################################################################################################
-\ No newline at end of file
---- /dev/null
-+++ b/drivers/mtd/nandx/Nandx.config
-@@ -0,0 +1,17 @@
-+NANDX_SIMULATOR_SUPPORT := n
-+NANDX_CTP_SUPPORT := n
-+NANDX_DA_SUPPORT := n
-+NANDX_PRELOADER_SUPPORT := n
-+NANDX_LK_SUPPORT := n
-+NANDX_KERNEL_SUPPORT := n
-+NANDX_BROM_SUPPORT := n
-+NANDX_UBOOT_SUPPORT := y
-+NANDX_BBT_SUPPORT := y
-+
-+NANDX_NAND_SPI := y
-+NANDX_NAND_SLC := n
-+NANDX_NAND_MLC := n
-+NANDX_NAND_TLC := n
-+NANDX_NFI_BASE := y
-+NANDX_NFI_ECC := y
-+NANDX_NFI_SPI := y
---- /dev/null
-+++ b/drivers/mtd/nandx/Nandx.mk
-@@ -0,0 +1,91 @@
-+#
-+# Copyright (C) 2017 MediaTek Inc.
-+# Licensed under either
-+#     BSD Licence, (see NOTICE for more details)
-+#     GNU General Public License, version 2.0, (see NOTICE for more details)
-+#
-+
-+nandx_dir := $(shell dirname $(lastword $(MAKEFILE_LIST)))
-+include $(nandx_dir)/Nandx.config
-+
-+ifeq ($(NANDX_SIMULATOR_SUPPORT), y)
-+sim-obj :=
-+sim-inc :=
-+nandx-obj := sim-obj
-+nandx-prefix := .
-+nandx-postfix := %.o
-+sim-inc += -I$(nandx-prefix)/include/internal
-+sim-inc += -I$(nandx-prefix)/include/simulator
-+endif
-+
-+ifeq ($(NANDX_CTP_SUPPORT), y)
-+nandx-obj := C_SRC_FILES
-+nandx-prefix := $(nandx_dir)
-+nandx-postfix := %.c
-+INC_DIRS += $(nandx_dir)/include/internal
-+INC_DIRS += $(nandx_dir)/include/ctp
-+endif
-+
-+ifeq ($(NANDX_DA_SUPPORT), y)
-+nandx-obj := obj-y
-+nandx-prefix := $(nandx_dir)
-+nandx-postfix := %.o
-+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/internal
-+INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/da
-+endif
-+
-+ifeq ($(NANDX_PRELOADER_SUPPORT), y)
-+nandx-obj := MOD_SRC
-+nandx-prefix := $(nandx_dir)
-+nandx-postfix := %.c
-+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/internal
-+C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/preloader
-+endif
-+
-+ifeq ($(NANDX_LK_SUPPORT), y)
-+nandx-obj := MODULE_SRCS
-+nandx-prefix := $(nandx_dir)
-+nandx-postfix := %.c
-+GLOBAL_INCLUDES += $(nandx_dir)/include/internal
-+GLOBAL_INCLUDES += $(nandx_dir)/include/lk
-+endif
-+
-+ifeq ($(NANDX_KERNEL_SUPPORT), y)
-+nandx-obj := obj-y
-+nandx-prefix := nandx
-+nandx-postfix := %.o
-+ccflags-y += -I$(nandx_dir)/include/internal
-+ccflags-y += -I$(nandx_dir)/include/kernel
-+endif
-+
-+ifeq ($(NANDX_UBOOT_SUPPORT), y)
-+nandx-obj := obj-y
-+nandx-prefix := nandx
-+nandx-postfix := %.o
-+ccflags-y += -I$(nandx_dir)/include/internal
-+ccflags-y += -I$(nandx_dir)/include/uboot
-+endif
-+
-+nandx-y :=
-+include $(nandx_dir)/core/Nandx.mk
-+nandx-target := $(nandx-prefix)/core/$(nandx-postfix)
-+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y))
-+
-+
-+nandx-y :=
-+include $(nandx_dir)/driver/Nandx.mk
-+nandx-target := $(nandx-prefix)/driver/$(nandx-postfix)
-+$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y))
-+
-+ifeq ($(NANDX_SIMULATOR_SUPPORT), y)
-+cc := gcc
-+CFLAGS += $(sim-inc)
-+
-+.PHONY:nandx
-+nandx: $(sim-obj)
-+      $(cc)  $(sim-obj) -o nandx
-+
-+.PHONY:clean
-+clean:
-+      rm -rf $(sim-obj) nandx
-+endif
---- /dev/null
-+++ b/drivers/mtd/nandx/README
-@@ -0,0 +1,31 @@
-+
-+                          NAND2.0
-+                ===============================
-+
-+    NAND2.0 is a common nand driver which designed for accessing
-+different type of NANDs(SLC, SPI-NAND, MLC, TLC) on various OS. This
-+driver can work on mostly SoCs of Mediatek.
-+
-+    Although there already has a common nand driver, it doesn't cover
-+SPI-NAND, and not match our IC-Verification's reqirement. We need
-+a driver that can be exten or cut easily.
-+
-+    This driver is base on NANDX & SLC. We try to refactor structures,
-+and make them inheritable. We also refactor some operations' flow
-+principally for adding SPI-NAND support.
-+
-+    This driver's architecture is like:
-+
-+          Driver @LK/Uboot/DA...           |IC verify/other purposes
-+    ----------------------------------------------------------------
-+      partition       |        BBM         |
-+    -------------------------------------- |       extend_core
-+             nandx_core/core_io            |
-+    ----------------------------------------------------------------
-+             nand_chip/nand_base           |
-+    -------------------------------------- |        extend_nfi
-+      nand_device     |    nfi/nfi_base    |
-+
-+    Any block of above graph can be extended at your will, if you
-+want add new feature into this code, please make sure that your code
-+would follow the framework, and we will be appreciated about it.
---- /dev/null
-+++ b/drivers/mtd/nandx/core/Nandx.mk
-@@ -0,0 +1,38 @@
-+#
-+# Copyright (C) 2017 MediaTek Inc.
-+# Licensed under either
-+#     BSD Licence, (see NOTICE for more details)
-+#     GNU General Public License, version 2.0, (see NOTICE for more details)
-+#
-+
-+nandx-y += nand_device.c
-+nandx-y += nand_base.c
-+nandx-y += nand_chip.c
-+nandx-y += core_io.c
-+
-+nandx-header-y += nand_device.h
-+nandx-header-y += nand_base.h
-+nandx-header-y += nand_chip.h
-+nandx-header-y += core_io.h
-+nandx-header-y += nfi.h
-+
-+nandx-$(NANDX_NAND_SPI) += nand/device_spi.c
-+nandx-$(NANDX_NAND_SPI) += nand/nand_spi.c
-+nandx-$(NANDX_NAND_SLC) += nand/device_slc.c
-+nandx-$(NANDX_NAND_SLC) += nand/nand_slc.c
-+
-+nandx-header-$(NANDX_NAND_SPI) += nand/device_spi.h
-+nandx-header-$(NANDX_NAND_SPI) += nand/nand_spi.h
-+nandx-header-$(NANDX_NAND_SLC) += nand/device_slc.h
-+nandx-header-$(NANDX_NAND_SLC) += nand/nand_slc.h
-+
-+nandx-$(NANDX_NFI_BASE) += nfi/nfi_base.c
-+nandx-$(NANDX_NFI_ECC) += nfi/nfiecc.c
-+nandx-$(NANDX_NFI_SPI) += nfi/nfi_spi.c
-+
-+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_base.h
-+nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_regs.h
-+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc.h
-+nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc_regs.h
-+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi.h
-+nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi_regs.h
---- /dev/null
-+++ b/drivers/mtd/nandx/core/core_io.c
-@@ -0,0 +1,735 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+/*NOTE: switch cache/multi*/
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "nand_chip.h"
-+#include "core_io.h"
-+
-+static struct nandx_desc *g_nandx;
-+
-+static inline bool is_sector_align(u64 val)
-+{
-+      return reminder(val, g_nandx->chip->sector_size) ? false : true;
-+}
-+
-+static inline bool is_page_align(u64 val)
-+{
-+      return reminder(val, g_nandx->chip->page_size) ? false : true;
-+}
-+
-+static inline bool is_block_align(u64 val)
-+{
-+      return reminder(val, g_nandx->chip->block_size) ? false : true;
-+}
-+
-+static inline u32 page_sectors(void)
-+{
-+      return div_down(g_nandx->chip->page_size, g_nandx->chip->sector_size);
-+}
-+
-+static inline u32 sector_oob(void)
-+{
-+      return div_down(g_nandx->chip->oob_size, page_sectors());
-+}
-+
-+static inline u32 sector_padded_size(void)
-+{
-+      return g_nandx->chip->sector_size + g_nandx->chip->sector_spare_size;
-+}
-+
-+static inline u32 page_padded_size(void)
-+{
-+      return page_sectors() * sector_padded_size();
-+}
-+
-+static inline u32 offset_to_padded_col(u64 offset)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      u32 col, sectors;
-+
-+      col = reminder(offset, nandx->chip->page_size);
-+      sectors = div_down(col, nandx->chip->sector_size);
-+
-+      return col + sectors * nandx->chip->sector_spare_size;
-+}
-+
-+static inline u32 offset_to_row(u64 offset)
-+{
-+      return div_down(offset, g_nandx->chip->page_size);
-+}
-+
-+static inline u32 offset_to_col(u64 offset)
-+{
-+      return reminder(offset, g_nandx->chip->page_size);
-+}
-+
-+static inline u32 oob_upper_size(void)
-+{
-+      return g_nandx->ecc_en ? g_nandx->chip->oob_size :
-+             g_nandx->chip->sector_spare_size * page_sectors();
-+}
-+
-+static inline bool is_upper_oob_align(u64 val)
-+{
-+      return reminder(val, oob_upper_size()) ? false : true;
-+}
-+
-+#define prepare_op(_op, _row, _col, _len, _data, _oob) \
-+      do { \
-+              (_op).row = (_row); \
-+              (_op).col = (_col); \
-+              (_op).len = (_len); \
-+              (_op).data = (_data); \
-+              (_op).oob = (_oob); \
-+      } while (0)
-+
-+static int operation_multi(enum nandx_op_mode mode, u8 *data, u8 *oob,
-+                         u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      u32 row = offset_to_row(offset);
-+      u32 col = offset_to_padded_col(offset);
-+
-+      if (nandx->mode == NANDX_IDLE) {
-+              nandx->mode = mode;
-+              nandx->ops_current = 0;
-+      } else if (nandx->mode != mode) {
-+              pr_info("forbid mixed operations.\n");
-+              return -EOPNOTSUPP;
-+      }
-+
-+      prepare_op(nandx->ops[nandx->ops_current], row, col, len, data, oob);
-+      nandx->ops_current++;
-+
-+      if (nandx->ops_current == nandx->ops_multi_len)
-+              return nandx_sync();
-+
-+      return nandx->ops_multi_len - nandx->ops_current;
-+}
-+
-+static int operation_sequent(enum nandx_op_mode mode, u8 *data, u8 *oob,
-+                           u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      struct nand_chip *chip = nandx->chip;
-+      u32 row = offset_to_row(offset);
-+      func_chip_ops chip_ops;
-+      u8 *ref_data = data, *ref_oob = oob;
-+      int align, ops, row_step;
-+      int i, rem;
-+
-+      align = data ? chip->page_size : oob_upper_size();
-+      ops = data ? div_down(len, align) : div_down(len, oob_upper_size());
-+      row_step = 1;
-+
-+      switch (mode) {
-+      case NANDX_ERASE:
-+              chip_ops = chip->erase_block;
-+              align = chip->block_size;
-+              ops = div_down(len, align);
-+              row_step = chip->block_pages;
-+              break;
-+
-+      case NANDX_READ:
-+              chip_ops = chip->read_page;
-+              break;
-+
-+      case NANDX_WRITE:
-+              chip_ops = chip->write_page;
-+              break;
-+
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      if (!data) {
-+              ref_data = nandx->head_buf;
-+              memset(ref_data, 0xff, chip->page_size);
-+      }
-+
-+      if (!oob) {
-+              ref_oob = nandx->head_buf + chip->page_size;
-+              memset(ref_oob, 0xff, oob_upper_size());
-+      }
-+
-+      for (i = 0; i < ops; i++) {
-+              prepare_op(nandx->ops[nandx->ops_current],
-+                         row + i * row_step, 0, align, ref_data, ref_oob);
-+              nandx->ops_current++;
-+              /* if data or oob is null, nandx->head_buf or
-+               * nandx->head_buf + chip->page_size should not been used
-+               * so, here it is safe to use the buf.
-+               */
-+              ref_data = data ? ref_data + chip->page_size : nandx->head_buf;
-+              ref_oob = oob ? ref_oob + oob_upper_size() :
-+                        nandx->head_buf + chip->page_size;
-+      }
-+
-+      if (nandx->mode == NANDX_WRITE) {
-+              rem = reminder(nandx->ops_current, nandx->min_write_pages);
-+              if (rem)
-+                      return nandx->min_write_pages - rem;
-+      }
-+
-+      nandx->ops_current = 0;
-+      return chip_ops(chip, nandx->ops, ops);
-+}
-+
-+static int read_pages(u8 *data, u8 *oob, u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      struct nand_chip *chip = nandx->chip;
-+      struct nandx_split64 split = {0};
-+      u8 *ref_data = data, *ref_oob;
-+      u32 row, col;
-+      int ret = 0, i, ops;
-+      u32 head_offset = 0;
-+      u64 val;
-+
-+      if (!data)
-+              return operation_sequent(NANDX_READ, NULL, oob, offset, len);
-+
-+      ref_oob = oob ? oob : nandx->head_buf + chip->page_size;
-+
-+      nandx_split(&split, offset, len, val, chip->page_size);
-+
-+      if (split.head_len) {
-+              row = offset_to_row(split.head);
-+              col = offset_to_col(split.head);
-+              prepare_op(nandx->ops[nandx->ops_current], row, 0,
-+                         chip->page_size,
-+                         nandx->head_buf, ref_oob);
-+              nandx->ops_current++;
-+
-+              head_offset = col;
-+
-+              ref_data += split.head_len;
-+              ref_oob = oob ? ref_oob + oob_upper_size() :
-+                        nandx->head_buf + chip->page_size;
-+      }
-+
-+      if (split.body_len) {
-+              ops = div_down(split.body_len, chip->page_size);
-+              row = offset_to_row(split.body);
-+              for (i = 0; i < ops; i++) {
-+                      prepare_op(nandx->ops[nandx->ops_current],
-+                                 row + i, 0, chip->page_size,
-+                                 ref_data, ref_oob);
-+                      nandx->ops_current++;
-+                      ref_data += chip->page_size;
-+                      ref_oob = oob ? ref_oob + oob_upper_size() :
-+                                nandx->head_buf + chip->page_size;
-+              }
-+      }
-+
-+      if (split.tail_len) {
-+              row = offset_to_row(split.tail);
-+              prepare_op(nandx->ops[nandx->ops_current], row, 0,
-+                         chip->page_size, nandx->tail_buf, ref_oob);
-+              nandx->ops_current++;
-+      }
-+
-+      ret = chip->read_page(chip, nandx->ops, nandx->ops_current);
-+
-+      if (split.head_len)
-+              memcpy(data, nandx->head_buf + head_offset, split.head_len);
-+      if (split.tail_len)
-+              memcpy(ref_data, nandx->tail_buf, split.tail_len);
-+
-+      nandx->ops_current = 0;
-+      return ret;
-+}
-+
-+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+
-+      if (!len || len > nandx->info.total_size)
-+              return -EINVAL;
-+      if (div_up(len, nandx->chip->page_size) > nandx->ops_len)
-+              return -EINVAL;
-+      if (!data && !oob)
-+              return -EINVAL;
-+      /**
-+       * as design, oob not support partial read
-+       * and, the length of oob buf should be oob size aligned
-+       */
-+      if (!data && !is_upper_oob_align(len))
-+              return -EINVAL;
-+
-+      if (g_nandx->multi_en) {
-+              /* as design, there only 2 buf for partial read,
-+               * if partial read allowed for multi read,
-+               * there are not enough buf
-+               */
-+              if (!is_sector_align(offset))
-+                      return -EINVAL;
-+              if (data && !is_sector_align(len))
-+                      return -EINVAL;
-+              return operation_multi(NANDX_READ, data, oob, offset, len);
-+      }
-+
-+      nandx->ops_current = 0;
-+      nandx->mode = NANDX_IDLE;
-+      return read_pages(data, oob, offset, len);
-+}
-+
-+static int write_pages(u8 *data, u8 *oob, u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      struct nand_chip *chip = nandx->chip;
-+      struct nandx_split64 split = {0};
-+      int ret, rem, i, ops;
-+      u32 row, col;
-+      u8 *ref_oob = oob;
-+      u64 val;
-+
-+      nandx->mode = NANDX_WRITE;
-+
-+      if (!data)
-+              return operation_sequent(NANDX_WRITE, NULL, oob, offset, len);
-+
-+      if (!oob) {
-+              ref_oob = nandx->head_buf + chip->page_size;
-+              memset(ref_oob, 0xff, oob_upper_size());
-+      }
-+
-+      nandx_split(&split, offset, len, val, chip->page_size);
-+
-+      /*NOTE: slc can support sector write, here copy too many data.*/
-+      if (split.head_len) {
-+              row = offset_to_row(split.head);
-+              col = offset_to_col(split.head);
-+              memset(nandx->head_buf, 0xff, page_padded_size());
-+              memcpy(nandx->head_buf + col, data, split.head_len);
-+              prepare_op(nandx->ops[nandx->ops_current], row, 0,
-+                         chip->page_size, nandx->head_buf, ref_oob);
-+              nandx->ops_current++;
-+
-+              data += split.head_len;
-+              ref_oob = oob ? ref_oob + oob_upper_size() :
-+                        nandx->head_buf + chip->page_size;
-+      }
-+
-+      if (split.body_len) {
-+              row = offset_to_row(split.body);
-+              ops = div_down(split.body_len, chip->page_size);
-+              for (i = 0; i < ops; i++) {
-+                      prepare_op(nandx->ops[nandx->ops_current],
-+                                 row + i, 0, chip->page_size, data, ref_oob);
-+                      nandx->ops_current++;
-+                      data += chip->page_size;
-+                      ref_oob = oob ? ref_oob + oob_upper_size() :
-+                                nandx->head_buf + chip->page_size;
-+              }
-+      }
-+
-+      if (split.tail_len) {
-+              row = offset_to_row(split.tail);
-+              memset(nandx->tail_buf, 0xff, page_padded_size());
-+              memcpy(nandx->tail_buf, data, split.tail_len);
-+              prepare_op(nandx->ops[nandx->ops_current], row, 0,
-+                         chip->page_size, nandx->tail_buf, ref_oob);
-+              nandx->ops_current++;
-+      }
-+
-+      rem = reminder(nandx->ops_current, nandx->min_write_pages);
-+      if (rem)
-+              return nandx->min_write_pages - rem;
-+
-+      ret = chip->write_page(chip, nandx->ops, nandx->ops_current);
-+
-+      nandx->ops_current = 0;
-+      nandx->mode = NANDX_IDLE;
-+      return ret;
-+}
-+
-+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+
-+      if (!len || len > nandx->info.total_size)
-+              return -EINVAL;
-+      if (div_up(len, nandx->chip->page_size) > nandx->ops_len)
-+              return -EINVAL;
-+      if (!data && !oob)
-+              return -EINVAL;
-+      if (!data && !is_upper_oob_align(len))
-+              return -EINVAL;
-+
-+      if (nandx->multi_en) {
-+              if (!is_page_align(offset))
-+                      return -EINVAL;
-+              if (data && !is_page_align(len))
-+                      return -EINVAL;
-+
-+              return operation_multi(NANDX_WRITE, data, oob, offset, len);
-+      }
-+
-+      return write_pages(data, oob, offset, len);
-+}
-+
-+int nandx_erase(u64 offset, size_t len)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+
-+      if (!len || len > nandx->info.total_size)
-+              return -EINVAL;
-+      if (div_down(len, nandx->chip->block_size) > nandx->ops_len)
-+              return -EINVAL;
-+      if (!is_block_align(offset) || !is_block_align(len))
-+              return -EINVAL;
-+
-+      if (g_nandx->multi_en)
-+              return operation_multi(NANDX_ERASE, NULL, NULL, offset, len);
-+
-+      nandx->ops_current = 0;
-+      nandx->mode = NANDX_IDLE;
-+      return operation_sequent(NANDX_ERASE, NULL, NULL, offset, len);
-+}
-+
-+int nandx_sync(void)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      struct nand_chip *chip = nandx->chip;
-+      func_chip_ops chip_ops;
-+      int ret, i, rem;
-+
-+      if (!nandx->ops_current)
-+              return 0;
-+
-+      rem = reminder(nandx->ops_current, nandx->ops_multi_len);
-+      if (nandx->multi_en && rem) {
-+              ret = -EIO;
-+              goto error;
-+      }
-+
-+      switch (nandx->mode) {
-+      case NANDX_IDLE:
-+              return 0;
-+      case NANDX_ERASE:
-+              chip_ops = chip->erase_block;
-+              break;
-+      case NANDX_READ:
-+              chip_ops = chip->read_page;
-+              break;
-+      case NANDX_WRITE:
-+              chip_ops = chip->write_page;
-+              break;
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      rem = reminder(nandx->ops_current, nandx->min_write_pages);
-+      if (!nandx->multi_en && nandx->mode == NANDX_WRITE && rem) {
-+              /* in one process of program, only allow 2 pages to do partial
-+               * write, here we supposed 1st buf would be used, and 2nd
-+               * buf should be not used.
-+               */
-+              memset(nandx->tail_buf, 0xff,
-+                     chip->page_size + oob_upper_size());
-+              for (i = 0; i < rem; i++) {
-+                      prepare_op(nandx->ops[nandx->ops_current],
-+                                 nandx->ops[nandx->ops_current - 1].row + 1,
-+                                 0, chip->page_size, nandx->tail_buf,
-+                                 nandx->tail_buf + chip->page_size);
-+                      nandx->ops_current++;
-+              }
-+      }
-+
-+      ret = chip_ops(nandx->chip, nandx->ops, nandx->ops_current);
-+
-+error:
-+      nandx->mode = NANDX_IDLE;
-+      nandx->ops_current = 0;
-+
-+      return ret;
-+}
-+
-+int nandx_ioctl(int cmd, void *arg)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+      struct nand_chip *chip = nandx->chip;
-+      int ret = 0;
-+
-+      switch (cmd) {
-+      case CORE_CTRL_NAND_INFO:
-+              *(struct nandx_info *)arg = nandx->info;
-+              break;
-+
-+      case CHIP_CTRL_OPS_MULTI:
-+              ret = chip->chip_ctrl(chip, cmd, arg);
-+              if (!ret)
-+                      nandx->multi_en = *(bool *)arg;
-+              break;
-+
-+      case NFI_CTRL_ECC:
-+              ret = chip->chip_ctrl(chip, cmd, arg);
-+              if (!ret)
-+                      nandx->ecc_en = *(bool *)arg;
-+              break;
-+
-+      default:
-+              ret = chip->chip_ctrl(chip, cmd, arg);
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+bool nandx_is_bad_block(u64 offset)
-+{
-+      struct nandx_desc *nandx = g_nandx;
-+
-+      prepare_op(nandx->ops[0], offset_to_row(offset), 0,
-+                 nandx->chip->page_size, nandx->head_buf,
-+                 nandx->head_buf + nandx->chip->page_size);
-+
-+      return nandx->chip->is_bad_block(nandx->chip, nandx->ops, 1);
-+}
-+
-+int nandx_suspend(void)
-+{
-+      return g_nandx->chip->suspend(g_nandx->chip);
-+}
-+
-+int nandx_resume(void)
-+{
-+      return g_nandx->chip->resume(g_nandx->chip);
-+}
-+
-+int nandx_init(struct nfi_resource *res)
-+{
-+      struct nand_chip *chip;
-+      struct nandx_desc *nandx;
-+      int ret = 0;
-+
-+      if (!res)
-+              return -EINVAL;
-+
-+      chip = nand_chip_init(res);
-+      if (!chip) {
-+              pr_info("nand chip init fail.\n");
-+              return -EFAULT;
-+      }
-+
-+      nandx = (struct nandx_desc *)mem_alloc(1, sizeof(struct nandx_desc));
-+      if (!nandx)
-+              return -ENOMEM;
-+
-+      g_nandx = nandx;
-+
-+      nandx->chip = chip;
-+      nandx->min_write_pages = chip->min_program_pages;
-+      nandx->ops_multi_len = nandx->min_write_pages * chip->plane_num;
-+      nandx->ops_len = chip->block_pages * chip->plane_num;
-+      nandx->ops = mem_alloc(1, sizeof(struct nand_ops) * nandx->ops_len);
-+      if (!nandx->ops) {
-+              ret = -ENOMEM;
-+              goto ops_error;
-+      }
-+
-+#if NANDX_BULK_IO_USE_DRAM
-+      nandx->head_buf = NANDX_CORE_BUF_ADDR;
-+#else
-+      nandx->head_buf = mem_alloc(2, page_padded_size());
-+#endif
-+      if (!nandx->head_buf) {
-+              ret = -ENOMEM;
-+              goto buf_error;
-+      }
-+      nandx->tail_buf = nandx->head_buf + page_padded_size();
-+      memset(nandx->head_buf, 0xff, 2 * page_padded_size());
-+      nandx->multi_en = false;
-+      nandx->ecc_en = false;
-+      nandx->ops_current = 0;
-+      nandx->mode = NANDX_IDLE;
-+
-+      nandx->info.max_io_count = nandx->ops_len;
-+      nandx->info.min_write_pages = nandx->min_write_pages;
-+      nandx->info.plane_num = chip->plane_num;
-+      nandx->info.oob_size = chip->oob_size;
-+      nandx->info.page_parity_size = chip->sector_spare_size * page_sectors();
-+      nandx->info.page_size = chip->page_size;
-+      nandx->info.block_size = chip->block_size;
-+      nandx->info.total_size = chip->block_size * chip->block_num;
-+      nandx->info.fdm_ecc_size = chip->fdm_ecc_size;
-+      nandx->info.fdm_reg_size = chip->fdm_reg_size;
-+      nandx->info.ecc_strength = chip->ecc_strength;
-+      nandx->info.sector_size = chip->sector_size;
-+
-+      return 0;
-+
-+buf_error:
-+#if !NANDX_BULK_IO_USE_DRAM
-+      mem_free(nandx->head_buf);
-+#endif
-+ops_error:
-+      mem_free(nandx);
-+
-+      return ret;
-+}
-+
-+void nandx_exit(void)
-+{
-+      nand_chip_exit(g_nandx->chip);
-+#if !NANDX_BULK_IO_USE_DRAM
-+      mem_free(g_nandx->head_buf);
-+#endif
-+      mem_free(g_nandx->ops);
-+      mem_free(g_nandx);
-+}
-+
-+#ifdef NANDX_UNIT_TEST
-+static void dump_buf(u8 *buf, u32 len)
-+{
-+      u32 i;
-+
-+      pr_info("dump buf@0x%X start", (u32)buf);
-+      for (i = 0; i < len; i++) {
-+              if (!reminder(i, 16))
-+                      pr_info("\n0x");
-+              pr_info("%x ", buf[i]);
-+      }
-+      pr_info("\ndump buf done.\n");
-+}
-+
-+int nandx_unit_test(u64 offset, size_t len)
-+{
-+      u8 *src_buf, *dst_buf;
-+      u32 i, j;
-+      int ret;
-+
-+      if (!len || len > g_nandx->chip->block_size)
-+              return -EINVAL;
-+
-+#if NANDX_BULK_IO_USE_DRAM
-+      src_buf = NANDX_UT_SRC_ADDR;
-+      dst_buf = NANDX_UT_DST_ADDR;
-+
-+#else
-+      src_buf = mem_alloc(1, g_nandx->chip->page_size);
-+      if (!src_buf)
-+              return -ENOMEM;
-+      dst_buf = mem_alloc(1, g_nandx->chip->page_size);
-+      if (!dst_buf) {
-+              mem_free(src_buf);
-+              return -ENOMEM;
-+      }
-+#endif
-+
-+      pr_info("%s: src_buf address 0x%x, dst_buf address 0x%x\n",
-+               __func__, (int)((unsigned long)src_buf),
-+               (int)((unsigned long)dst_buf));
-+
-+      memset(dst_buf, 0, g_nandx->chip->page_size);
-+      pr_info("read page 0 data...!\n");
-+      ret = nandx_read(dst_buf, NULL, 0, g_nandx->chip->page_size);
-+      if (ret < 0) {
-+              pr_info("read fail with ret %d\n", ret);
-+      } else {
-+              pr_info("read page success!\n");
-+      }
-+
-+      for (i = 0; i < g_nandx->chip->page_size; i++) {
-+              src_buf[i] = 0x5a;
-+      }
-+
-+      ret = nandx_erase(offset, g_nandx->chip->block_size);
-+      if (ret < 0) {
-+              pr_info("erase fail with ret %d\n", ret);
-+              goto error;
-+      }
-+
-+      for (j = 0; j < g_nandx->chip->block_pages; j++) {
-+              memset(dst_buf, 0, g_nandx->chip->page_size);
-+              pr_info("check data after erase...!\n");
-+              ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
-+              if (ret < 0) {
-+                      pr_info("read fail with ret %d\n", ret);
-+                      goto error;
-+              }
-+
-+              for (i = 0; i < g_nandx->chip->page_size; i++) {
-+                      if (dst_buf[i] != 0xff) {
-+                              pr_info("read after erase, check fail @%d\n", i);
-+                              pr_info("all data should be 0xff\n");
-+                              ret = -ENANDERASE;
-+                              dump_buf(dst_buf, 128);
-+                              //goto error;
-+                              break;
-+                      }
-+              }
-+
-+              pr_info("write data...!\n");
-+              ret = nandx_write(src_buf, NULL, offset, g_nandx->chip->page_size);
-+              if (ret < 0) {
-+                      pr_info("write fail with ret %d\n", ret);
-+                      goto error;
-+              }
-+
-+              memset(dst_buf, 0, g_nandx->chip->page_size);
-+              pr_info("read data...!\n");
-+              ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
-+              if (ret < 0) {
-+                      pr_info("read fail with ret %d\n", ret);
-+                      goto error;
-+              }
-+
-+              for (i = 0; i < g_nandx->chip->page_size; i++) {
-+                      if (dst_buf[i] != src_buf[i]) {
-+                              pr_info("read after write, check fail @%d\n", i);
-+                              pr_info("dst_buf should be same as src_buf\n");
-+                              ret = -EIO;
-+                              dump_buf(src_buf + i, 128);
-+                              dump_buf(dst_buf + i, 128);
-+                              break;
-+                      }
-+              }
-+
-+              pr_err("%s %d %s@%d\n", __func__, __LINE__, ret?"Failed":"OK", j);
-+              if (ret)
-+                      break;
-+
-+              offset += g_nandx->chip->page_size;
-+      }
-+
-+      ret = nandx_erase(offset, g_nandx->chip->block_size);
-+      if (ret < 0) {
-+              pr_info("erase fail with ret %d\n", ret);
-+              goto error;
-+      }
-+
-+      memset(dst_buf, 0, g_nandx->chip->page_size);
-+      ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
-+      if (ret < 0) {
-+              pr_info("read fail with ret %d\n", ret);
-+              goto error;
-+      }
-+
-+      for (i = 0; i < g_nandx->chip->page_size; i++) {
-+              if (dst_buf[i] != 0xff) {
-+                      pr_info("read after erase, check fail\n");
-+                      pr_info("all data should be 0xff\n");
-+                      ret = -ENANDERASE;
-+                      dump_buf(dst_buf, 128);
-+                      goto error;
-+              }
-+      }
-+
-+      return 0;
-+
-+error:
-+#if !NANDX_BULK_IO_USE_DRAM
-+      mem_free(src_buf);
-+      mem_free(dst_buf);
-+#endif
-+      return ret;
-+}
-+#endif
---- /dev/null
-+++ b/drivers/mtd/nandx/core/core_io.h
-@@ -0,0 +1,39 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __CORE_IO_H__
-+#define __CORE_IO_H__
-+
-+typedef int (*func_chip_ops)(struct nand_chip *, struct nand_ops *,
-+                           int);
-+
-+enum nandx_op_mode {
-+      NANDX_IDLE,
-+      NANDX_WRITE,
-+      NANDX_READ,
-+      NANDX_ERASE
-+};
-+
-+struct nandx_desc {
-+      struct nand_chip *chip;
-+      struct nandx_info info;
-+      enum nandx_op_mode mode;
-+
-+      bool multi_en;
-+      bool ecc_en;
-+
-+      struct nand_ops *ops;
-+      int ops_len;
-+      int ops_multi_len;
-+      int ops_current;
-+      int min_write_pages;
-+
-+      u8 *head_buf;
-+      u8 *tail_buf;
-+};
-+
-+#endif /* __CORE_IO_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand/device_spi.c
-@@ -0,0 +1,200 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "../nand_device.h"
-+#include "device_spi.h"
-+
-+/* spi nand basic commands */
-+static struct nand_cmds spi_cmds = {
-+      .reset = 0xff,
-+      .read_id = 0x9f,
-+      .read_status = 0x0f,
-+      .read_param_page = 0x03,
-+      .set_feature = 0x1f,
-+      .get_feature = 0x0f,
-+      .read_1st = 0x13,
-+      .read_2nd = -1,
-+      .random_out_1st = 0x03,
-+      .random_out_2nd = -1,
-+      .program_1st = 0x02,
-+      .program_2nd = 0x10,
-+      .erase_1st = 0xd8,
-+      .erase_2nd = -1,
-+      .read_cache = 0x30,
-+      .read_cache_last = 0x3f,
-+      .program_cache = 0x02
-+};
-+
-+/* spi nand extend commands */
-+static struct spi_extend_cmds spi_extend_cmds = {
-+      .die_select = 0xc2,
-+      .write_enable = 0x06
-+};
-+
-+/* means the start bit of addressing type */
-+static struct nand_addressing spi_addressing = {
-+      .row_bit_start = 0,
-+      .block_bit_start = 0,
-+      .plane_bit_start = 12,
-+      .lun_bit_start = 0,
-+};
-+
-+/* spi nand endurance */
-+static struct nand_endurance spi_endurance = {
-+      .pe_cycle = 100000,
-+      .ecc_req = 1,
-+      .max_bitflips = 1
-+};
-+
-+/* array_busy, write_protect, erase_fail, program_fail */
-+static struct nand_status spi_status[] = {
-+      {.array_busy = BIT(0),
-+      .write_protect = BIT(1),
-+      .erase_fail = BIT(2),
-+      .program_fail = BIT(3)}
-+};
-+
-+/* measure time by the us */
-+static struct nand_array_timing spi_array_timing = {
-+      .tRST = 500,
-+      .tWHR = 1,
-+      .tR = 25,
-+      .tRCBSY = 25,
-+      .tFEAT = 1,
-+      .tPROG = 600,
-+      .tPCBSY = 600,
-+      .tBERS = 10000,
-+      .tDBSY = 1
-+};
-+
-+/* spi nand device table */
-+static struct device_spi spi_nand[] = {
-+      {
-+              NAND_DEVICE("W25N01GV",
-+                          NAND_PACK_ID(0xef, 0xaa, 0x21, 0, 0, 0, 0, 0),
-+                          3, 0, 3, 3,
-+                          1, 1, 1, 1024, KB(128), KB(2), 64, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 0),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-+              NAND_DEVICE("MX35LF1G",
-+                          NAND_PACK_ID(0xc2, 0x12, 0x21, 0, 0, 0, 0, 0),
-+                          2, 0, 3, 3,
-+                          1, 1, 1, 1024, KB(128), KB(2), 64, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-+              NAND_DEVICE("MT29F4G01ABAFDWB",
-+                          NAND_PACK_ID(0x2c, 0x34, 0, 0, 0, 0, 0, 0),
-+                          2, 0, 3, 3,
-+                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-+              NAND_DEVICE("GD5F4GQ4UB",
-+                          NAND_PACK_ID(0xc8, 0xd4, 0, 0, 0, 0, 0, 0),
-+                          2, 0, 3, 3,
-+                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-+              NAND_DEVICE("TC58CVG2S0HRAIJ",
-+                          NAND_PACK_ID(0x98, 0xED, 0x51, 0, 0, 0, 0, 0),
-+                          3, 0, 3, 3,
-+                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-+              NAND_DEVICE("NO-DEVICE",
-+                          NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0,
-+                          0, 0, 0, 0, 0, 0, 0, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 0),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      }
-+};
-+
-+u8 spi_replace_rx_cmds(u8 mode)
-+{
-+      u8 rx_replace_cmds[] = {0x03, 0x3b, 0x6b, 0xbb, 0xeb};
-+
-+      return rx_replace_cmds[mode];
-+}
-+
-+u8 spi_replace_tx_cmds(u8 mode)
-+{
-+      u8 tx_replace_cmds[] = {0x02, 0x32};
-+
-+      return tx_replace_cmds[mode];
-+}
-+
-+u8 spi_replace_rx_col_cycle(u8 mode)
-+{
-+      u8 rx_replace_col_cycle[] = {3, 3, 3, 3, 4};
-+
-+      return rx_replace_col_cycle[mode];
-+}
-+
-+u8 spi_replace_tx_col_cycle(u8 mode)
-+{
-+      u8 tx_replace_col_cycle[] = {2, 2};
-+
-+      return tx_replace_col_cycle[mode];
-+}
-+
-+struct nand_device *nand_get_device(int index)
-+{
-+      return &spi_nand[index].dev;
-+}
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand/device_spi.h
-@@ -0,0 +1,132 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __DEVICE_SPI_H__
-+#define __DEVICE_SPI_H__
-+
-+/*
-+ * extend commands
-+ * @die_select: select nand device die command
-+ * @write_enable: enable write command before write data to spi nand
-+ *    spi nand device will auto to be disable after write done
-+ */
-+struct spi_extend_cmds {
-+      short die_select;
-+      short write_enable;
-+};
-+
-+/*
-+ * protection feature register
-+ * @addr: register address
-+ * @wp_en_bit: write protection enable bit
-+ * @bp_start_bit: block protection mask start bit
-+ * @bp_end_bit: block protection mask end bit
-+ */
-+struct feature_protect {
-+      u8 addr;
-+      u8 wp_en_bit;
-+      u8 bp_start_bit;
-+      u8 bp_end_bit;
-+};
-+
-+/*
-+ * configuration feature register
-+ * @addr: register address
-+ * @ecc_en_bit: in-die ecc enable bit
-+ * @otp_en_bit: enter otp access mode bit
-+ * @need_qe: quad io enable bit
-+ */
-+struct feature_config {
-+      u8 addr;
-+      u8 ecc_en_bit;
-+      u8 otp_en_bit;
-+      u8 need_qe;
-+};
-+
-+/*
-+ * status feature register
-+ * @addr: register address
-+ * @ecc_start_bit: ecc status mask start bit for error bits number
-+ * @ecc_end_bit: ecc status mask end bit for error bits number
-+ * note that:
-+ *   operations status (ex. array busy status) could see on struct nand_status
-+ */
-+struct feature_status {
-+      u8 addr;
-+      u8 ecc_start_bit;
-+      u8 ecc_end_bit;
-+};
-+
-+/*
-+ * character feature register
-+ * @addr: register address
-+ * @die_sel_bit: die select bit
-+ * @drive_start_bit: drive strength mask start bit
-+ * @drive_end_bit: drive strength mask end bit
-+ */
-+struct feature_character {
-+      u8 addr;
-+      u8 die_sel_bit;
-+      u8 drive_start_bit;
-+      u8 drive_end_bit;
-+};
-+
-+/*
-+ * spi features
-+ * @protect: protection feature register
-+ * @config: configuration feature register
-+ * @status: status feature register
-+ * @character: character feature register
-+ */
-+struct spi_features {
-+      struct feature_protect protect;
-+      struct feature_config config;
-+      struct feature_status status;
-+      struct feature_character character;
-+};
-+
-+/*
-+ * device_spi
-+ *    configurations of spi nand device table
-+ * @dev: base information of nand device
-+ * @feature: feature information for spi nand
-+ * @extend_cmds: extended the nand base commands
-+ * @tx_mode_mask: tx mode mask for chip read
-+ * @rx_mode_mask: rx mode mask for chip write
-+ */
-+struct device_spi {
-+      struct nand_device dev;
-+      struct spi_features feature;
-+      struct spi_extend_cmds *extend_cmds;
-+
-+      u8 tx_mode_mask;
-+      u8 rx_mode_mask;
-+};
-+
-+#define NAND_SPI_PROTECT(addr, wp_en_bit, bp_start_bit, bp_end_bit) \
-+      {addr, wp_en_bit, bp_start_bit, bp_end_bit}
-+
-+#define NAND_SPI_CONFIG(addr, ecc_en_bit, otp_en_bit, need_qe) \
-+      {addr, ecc_en_bit, otp_en_bit, need_qe}
-+
-+#define NAND_SPI_STATUS(addr, ecc_start_bit, ecc_end_bit) \
-+      {addr, ecc_start_bit, ecc_end_bit}
-+
-+#define NAND_SPI_CHARACTER(addr, die_sel_bit, drive_start_bit, drive_end_bit) \
-+      {addr, die_sel_bit, drive_start_bit, drive_end_bit}
-+
-+static inline struct device_spi *device_to_spi(struct nand_device *dev)
-+{
-+      return container_of(dev, struct device_spi, dev);
-+}
-+
-+u8 spi_replace_rx_cmds(u8 mode);
-+u8 spi_replace_tx_cmds(u8 mode);
-+u8 spi_replace_rx_col_cycle(u8 mode);
-+u8 spi_replace_tx_col_cycle(u8 mode);
-+
-+#endif /* __DEVICE_SPI_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand/nand_spi.c
-@@ -0,0 +1,526 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "../nand_chip.h"
-+#include "../nand_device.h"
-+#include "../nfi.h"
-+#include "../nand_base.h"
-+#include "device_spi.h"
-+#include "nand_spi.h"
-+
-+#define READY_TIMEOUT   500000 /* us */
-+
-+static int nand_spi_read_status(struct nand_base *nand)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      u8 status;
-+
-+      nand->get_feature(nand, dev->feature.status.addr, &status, 1);
-+
-+      return status;
-+}
-+
-+static int nand_spi_wait_ready(struct nand_base *nand, u32 timeout)
-+{
-+      u64 now, end;
-+      int status;
-+
-+      end = get_current_time_us() + timeout;
-+
-+      do {
-+              status = nand_spi_read_status(nand);
-+              status &= nand->dev->status->array_busy;
-+              now = get_current_time_us();
-+
-+              if (now > end)
-+                      break;
-+      } while (status);
-+
-+      return status ? -EBUSY : 0;
-+}
-+
-+static int nand_spi_set_op_mode(struct nand_base *nand, u8 mode)
-+{
-+      struct nand_spi *spi_nand = base_to_spi(nand);
-+      struct nfi *nfi = nand->nfi;
-+      int ret = 0;
-+
-+      if (spi_nand->op_mode != mode) {
-+              ret = nfi->nfi_ctrl(nfi, SNFI_CTRL_OP_MODE, (void *)&mode);
-+              spi_nand->op_mode = mode;
-+      }
-+
-+      return ret;
-+}
-+
-+static int nand_spi_set_config(struct nand_base *nand, u8 addr, u8 mask,
-+                             bool en)
-+{
-+      u8 configs = 0;
-+
-+      nand->get_feature(nand, addr, &configs, 1);
-+
-+      if (en)
-+              configs |= mask;
-+      else
-+              configs &= ~mask;
-+
-+      nand->set_feature(nand, addr, &configs, 1);
-+
-+      configs = 0;
-+      nand->get_feature(nand, addr, &configs, 1);
-+
-+      return (configs & mask) == en ? 0 : -EFAULT;
-+}
-+
-+static int nand_spi_die_select(struct nand_base *nand, int *row)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nfi *nfi = nand->nfi;
-+      int lun_blocks, block_pages, lun, blocks;
-+      int page = *row, ret = 0;
-+      u8 param = 0, die_sel;
-+
-+      if (nand->dev->lun_num < 2)
-+              return 0;
-+
-+      block_pages = nand_block_pages(nand->dev);
-+      lun_blocks = nand_lun_blocks(nand->dev);
-+      blocks = div_down(page, block_pages);
-+      lun = div_down(blocks, lun_blocks);
-+
-+      if (dev->extend_cmds->die_select == -1) {
-+              die_sel = (u8)(lun << dev->feature.character.die_sel_bit);
-+              nand->get_feature(nand, dev->feature.character.addr, &param, 1);
-+              param |= die_sel;
-+              nand->set_feature(nand, dev->feature.character.addr, &param, 1);
-+              param = 0;
-+              nand->get_feature(nand, dev->feature.character.addr, &param, 1);
-+              ret = (param & die_sel) ? 0 : -EFAULT;
-+      } else {
-+              nfi->reset(nfi);
-+              nfi->send_cmd(nfi, dev->extend_cmds->die_select);
-+              nfi->send_addr(nfi, lun, 0, 1, 0);
-+              nfi->trigger(nfi);
-+      }
-+
-+      *row =  page - (lun_blocks * block_pages) * lun;
-+
-+      return ret;
-+}
-+
-+static int nand_spi_select_device(struct nand_base *nand, int cs)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      return parent->select_device(nand, cs);
-+}
-+
-+static int nand_spi_reset(struct nand_base *nand)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      parent->reset(nand);
-+
-+      return nand_spi_wait_ready(nand, READY_TIMEOUT);
-+}
-+
-+static int nand_spi_read_id(struct nand_base *nand, u8 *id, int count)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      return parent->read_id(nand, id, count);
-+}
-+
-+static int nand_spi_read_param_page(struct nand_base *nand, u8 *data,
-+                                  int count)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nfi *nfi = nand->nfi;
-+      int sectors, value;
-+      u8 param = 0;
-+
-+      sectors = div_round_up(count, nfi->sector_size);
-+
-+      nand->get_feature(nand, dev->feature.config.addr, &param, 1);
-+      param |= BIT(dev->feature.config.otp_en_bit);
-+      nand->set_feature(nand, dev->feature.config.addr, &param, 1);
-+
-+      param = 0;
-+      nand->get_feature(nand, dev->feature.config.addr, &param, 1);
-+      if (param & BIT(dev->feature.config.otp_en_bit)) {
-+              value = 0;
-+              nfi->nfi_ctrl(nfi, NFI_CTRL_ECC, &value);
-+              nand->dev->col_cycle  = spi_replace_rx_col_cycle(spi->rx_mode);
-+              nand->read_page(nand, 0x01);
-+              nand->read_data(nand, 0x01, 0, sectors, data, NULL);
-+      }
-+
-+      param &= ~BIT(dev->feature.config.otp_en_bit);
-+      nand->set_feature(nand, dev->feature.config.addr, &param, 1);
-+
-+      return 0;
-+}
-+
-+static int nand_spi_set_feature(struct nand_base *nand, u8 addr,
-+                              u8 *param,
-+                              int count)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand->write_enable(nand);
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      return parent->set_feature(nand, addr, param, count);
-+}
-+
-+static int nand_spi_get_feature(struct nand_base *nand, u8 addr,
-+                              u8 *param,
-+                              int count)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      return parent->get_feature(nand, addr, param, count);
-+}
-+
-+static int nand_spi_addressing(struct nand_base *nand, int *row,
-+                             int *col)
-+{
-+      struct nand_device *dev = nand->dev;
-+      int plane, block, block_pages;
-+      int ret;
-+
-+      ret = nand_spi_die_select(nand, row);
-+      if (ret)
-+              return ret;
-+
-+      block_pages = nand_block_pages(dev);
-+      block = div_down(*row, block_pages);
-+
-+      plane = block % dev->plane_num;
-+      *col |= (plane << dev->addressing->plane_bit_start);
-+
-+      return 0;
-+}
-+
-+static int nand_spi_read_page(struct nand_base *nand, int row)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      if (spi->op_mode == SNFI_AUTO_MODE)
-+              nand_spi_set_op_mode(nand, SNFI_AUTO_MODE);
-+      else
-+              nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      parent->read_page(nand, row);
-+
-+      return nand_spi_wait_ready(nand, READY_TIMEOUT);
-+}
-+
-+static int nand_spi_read_data(struct nand_base *nand, int row, int col,
-+                            int sectors, u8 *data, u8 *oob)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+      int ret;
-+
-+      if ((spi->rx_mode == SNFI_RX_114 || spi->rx_mode == SNFI_RX_144) &&
-+          dev->feature.config.need_qe)
-+              nand_spi_set_config(nand, dev->feature.config.addr,
-+                                  BIT(0), true);
-+
-+      nand->dev->col_cycle  = spi_replace_rx_col_cycle(spi->rx_mode);
-+
-+      nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE);
-+
-+      ret = parent->read_data(nand, row, col, sectors, data, oob);
-+      if (ret)
-+              return -ENANDREAD;
-+
-+      if (spi->ondie_ecc) {
-+              ret = nand_spi_read_status(nand);
-+              ret &= GENMASK(dev->feature.status.ecc_end_bit,
-+                             dev->feature.status.ecc_start_bit);
-+              ret >>= dev->feature.status.ecc_start_bit;
-+              if (ret > nand->dev->endurance->ecc_req)
-+                      return -ENANDREAD;
-+              else if (ret > nand->dev->endurance->max_bitflips)
-+                      return -ENANDFLIPS;
-+      }
-+
-+      return 0;
-+}
-+
-+static int nand_spi_write_enable(struct nand_base *nand)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nfi *nfi = nand->nfi;
-+      int status;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->extend_cmds->write_enable);
-+
-+      nfi->trigger(nfi);
-+
-+      status = nand_spi_read_status(nand);
-+      status &= nand->dev->status->write_protect;
-+
-+      return !status;
-+}
-+
-+static int nand_spi_program_data(struct nand_base *nand, int row,
-+                               int col,
-+                               u8 *data, u8 *oob)
-+{
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nand_spi *spi = base_to_spi(nand);
-+
-+      if (spi->tx_mode == SNFI_TX_114 && dev->feature.config.need_qe)
-+              nand_spi_set_config(nand, dev->feature.config.addr,
-+                                  BIT(0), true);
-+
-+      nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE);
-+
-+      nand->dev->col_cycle  = spi_replace_tx_col_cycle(spi->tx_mode);
-+
-+      return spi->parent->program_data(nand, row, col, data, oob);
-+}
-+
-+static int nand_spi_program_page(struct nand_base *nand, int row)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_device *dev = nand->dev;
-+      struct nfi *nfi = nand->nfi;
-+
-+      if (spi->op_mode == SNFI_AUTO_MODE)
-+              nand_spi_set_op_mode(nand, SNFI_AUTO_MODE);
-+      else
-+              nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->program_2nd);
-+      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
-+      nfi->trigger(nfi);
-+
-+      return nand_spi_wait_ready(nand, READY_TIMEOUT);
-+}
-+
-+static int nand_spi_erase_block(struct nand_base *nand, int row)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nand_base *parent = spi->parent;
-+
-+      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
-+
-+      parent->erase_block(nand, row);
-+
-+      return nand_spi_wait_ready(nand, READY_TIMEOUT);
-+}
-+
-+static int nand_chip_spi_ctrl(struct nand_chip *chip, int cmd,
-+                            void *args)
-+{
-+      struct nand_base *nand = chip->nand;
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct nfi *nfi = nand->nfi;
-+      int ret = 0, value = *(int *)args;
-+
-+      switch (cmd) {
-+      case CHIP_CTRL_ONDIE_ECC:
-+              spi->ondie_ecc = (bool)value;
-+              ret = nand_spi_set_config(nand, dev->feature.config.addr,
-+                                        BIT(dev->feature.config.ecc_en_bit),
-+                                        spi->ondie_ecc);
-+              break;
-+
-+      case SNFI_CTRL_TX_MODE:
-+              if (value < 0 || value > SNFI_TX_114)
-+                      return -EOPNOTSUPP;
-+
-+              if (dev->tx_mode_mask & BIT(value)) {
-+                      spi->tx_mode = value;
-+                      nand->dev->cmds->random_out_1st = spi_replace_tx_cmds(
-+                                                                spi->tx_mode);
-+                      ret = nfi->nfi_ctrl(nfi, cmd, args);
-+              }
-+
-+              break;
-+
-+      case SNFI_CTRL_RX_MODE:
-+              if (value < 0 || value > SNFI_RX_144)
-+                      return -EOPNOTSUPP;
-+
-+              if (dev->rx_mode_mask & BIT(value)) {
-+                      spi->rx_mode = value;
-+                      nand->dev->cmds->program_1st = spi_replace_rx_cmds(
-+                                                             spi->rx_mode);
-+                      ret = nfi->nfi_ctrl(nfi, cmd, args);
-+              }
-+
-+              break;
-+
-+      case CHIP_CTRL_OPS_CACHE:
-+      case CHIP_CTRL_OPS_MULTI:
-+      case CHIP_CTRL_PSLC_MODE:
-+      case CHIP_CTRL_DDR_MODE:
-+      case CHIP_CTRL_DRIVE_STRENGTH:
-+      case CHIP_CTRL_TIMING_MODE:
-+              ret = -EOPNOTSUPP;
-+              break;
-+
-+      default:
-+              ret = nfi->nfi_ctrl(nfi, cmd, args);
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+int nand_chip_spi_resume(struct nand_chip *chip)
-+{
-+      struct nand_base *nand = chip->nand;
-+      struct nand_spi *spi = base_to_spi(nand);
-+      struct device_spi *dev = device_to_spi(nand->dev);
-+      struct nfi *nfi = nand->nfi;
-+      struct nfi_format format;
-+      u8 mask;
-+
-+      nand->reset(nand);
-+
-+      mask = GENMASK(dev->feature.protect.bp_end_bit,
-+                     dev->feature.protect.bp_start_bit);
-+      nand_spi_set_config(nand, dev->feature.config.addr, mask, false);
-+      mask =  BIT(dev->feature.config.ecc_en_bit);
-+      nand_spi_set_config(nand, dev->feature.config.addr, mask,
-+                          spi->ondie_ecc);
-+
-+      format.page_size = nand->dev->page_size;
-+      format.spare_size = nand->dev->spare_size;
-+      format.ecc_req = nand->dev->endurance->ecc_req;
-+
-+      return nfi->set_format(nfi, &format);
-+}
-+
-+static int nand_spi_set_format(struct nand_base *nand)
-+{
-+      struct nfi_format format = {
-+              nand->dev->page_size,
-+              nand->dev->spare_size,
-+              nand->dev->endurance->ecc_req
-+      };
-+
-+      return nand->nfi->set_format(nand->nfi, &format);
-+}
-+
-+struct nand_base *nand_device_init(struct nand_chip *chip)
-+{
-+      struct nand_base *nand;
-+      struct nand_spi *spi;
-+      struct device_spi *dev;
-+      int ret;
-+      u8 mask;
-+
-+      spi = mem_alloc(1, sizeof(struct nand_spi));
-+      if (!spi) {
-+              pr_info("alloc nand_spi fail\n");
-+              return NULL;
-+      }
-+
-+      spi->ondie_ecc = false;
-+      spi->op_mode = SNFI_CUSTOM_MODE;
-+      spi->rx_mode = SNFI_RX_114;
-+      spi->tx_mode = SNFI_TX_114;
-+
-+      spi->parent = chip->nand;
-+      nand = &spi->base;
-+      nand->dev = spi->parent->dev;
-+      nand->nfi = spi->parent->nfi;
-+
-+      nand->select_device = nand_spi_select_device;
-+      nand->reset = nand_spi_reset;
-+      nand->read_id = nand_spi_read_id;
-+      nand->read_param_page = nand_spi_read_param_page;
-+      nand->set_feature = nand_spi_set_feature;
-+      nand->get_feature = nand_spi_get_feature;
-+      nand->read_status = nand_spi_read_status;
-+      nand->addressing = nand_spi_addressing;
-+      nand->read_page = nand_spi_read_page;
-+      nand->read_data = nand_spi_read_data;
-+      nand->write_enable = nand_spi_write_enable;
-+      nand->program_data = nand_spi_program_data;
-+      nand->program_page = nand_spi_program_page;
-+      nand->erase_block = nand_spi_erase_block;
-+
-+      chip->chip_ctrl = nand_chip_spi_ctrl;
-+      chip->nand_type = NAND_SPI;
-+      chip->resume = nand_chip_spi_resume;
-+
-+      ret = nand_detect_device(nand);
-+      if (ret)
-+              goto err;
-+
-+      nand->select_device(nand, 0);
-+
-+      ret = nand_spi_set_format(nand);
-+      if (ret)
-+              goto err;
-+
-+      dev = (struct device_spi *)nand->dev;
-+
-+      nand->dev->cmds->random_out_1st =
-+              spi_replace_rx_cmds(spi->rx_mode);
-+      nand->dev->cmds->program_1st =
-+              spi_replace_tx_cmds(spi->tx_mode);
-+
-+      mask = GENMASK(dev->feature.protect.bp_end_bit,
-+                     dev->feature.protect.bp_start_bit);
-+      ret = nand_spi_set_config(nand, dev->feature.protect.addr, mask, false);
-+      if (ret)
-+              goto err;
-+
-+      mask =  BIT(dev->feature.config.ecc_en_bit);
-+      ret = nand_spi_set_config(nand, dev->feature.config.addr, mask,
-+                                spi->ondie_ecc);
-+      if (ret)
-+              goto err;
-+
-+      return nand;
-+
-+err:
-+      mem_free(spi);
-+      return NULL;
-+}
-+
-+void nand_exit(struct nand_base *nand)
-+{
-+      struct nand_spi *spi = base_to_spi(nand);
-+
-+      nand_base_exit(spi->parent);
-+      mem_free(spi);
-+}
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand/nand_spi.h
-@@ -0,0 +1,35 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NAND_SPI_H__
-+#define __NAND_SPI_H__
-+
-+/*
-+ * spi nand handler
-+ * @base: spi nand base functions
-+ * @parent: common parent nand base functions
-+ * @tx_mode: spi bus width of transfer to device
-+ * @rx_mode: spi bus width of transfer from device
-+ * @op_mode: spi nand controller (NFI) operation mode
-+ * @ondie_ecc: spi nand on-die ecc flag
-+ */
-+
-+struct nand_spi {
-+      struct nand_base base;
-+      struct nand_base *parent;
-+      u8 tx_mode;
-+      u8 rx_mode;
-+      u8 op_mode;
-+      bool ondie_ecc;
-+};
-+
-+static inline struct nand_spi *base_to_spi(struct nand_base *base)
-+{
-+      return container_of(base, struct nand_spi, base);
-+}
-+
-+#endif /* __NAND_SPI_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_base.c
-@@ -0,0 +1,304 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "nand_chip.h"
-+#include "nand_device.h"
-+#include "nfi.h"
-+#include "nand_base.h"
-+
-+static int nand_base_select_device(struct nand_base *nand, int cs)
-+{
-+      struct nfi *nfi = nand->nfi;
-+
-+      nfi->reset(nfi);
-+
-+      return nfi->select_chip(nfi, cs);
-+}
-+
-+static int nand_base_reset(struct nand_base *nand)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->reset);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRST);
-+}
-+
-+static int nand_base_read_id(struct nand_base *nand, u8 *id, int count)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_id);
-+      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR);
-+      nfi->send_addr(nfi, 0, 0, 1, 0);
-+
-+      return nfi->read_bytes(nfi, id, count);
-+}
-+
-+static int nand_base_read_param_page(struct nand_base *nand, u8 *data,
-+                                   int count)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_param_page);
-+      nfi->send_addr(nfi, 0, 0, 1, 0);
-+
-+      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR);
-+
-+      return nfi->read_bytes(nfi, data, count);
-+}
-+
-+static int nand_base_set_feature(struct nand_base *nand, u8 addr,
-+                               u8 *param,
-+                               int count)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->set_feature);
-+      nfi->send_addr(nfi, addr, 0, 1, 0);
-+
-+      nfi->write_bytes(nfi, param, count);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tFEAT);
-+}
-+
-+static int nand_base_get_feature(struct nand_base *nand, u8 addr,
-+                               u8 *param,
-+                               int count)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->get_feature);
-+      nfi->send_addr(nfi, addr, 0, 1, 0);
-+      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tFEAT);
-+
-+      return nfi->read_bytes(nfi, param, count);
-+}
-+
-+static int nand_base_read_status(struct nand_base *nand)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+      u8 status = 0;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_status);
-+      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR);
-+      nfi->read_bytes(nfi, &status, 1);
-+
-+      return status;
-+}
-+
-+static int nand_base_addressing(struct nand_base *nand, int *row,
-+                              int *col)
-+{
-+      struct nand_device *dev = nand->dev;
-+      int lun, plane, block, page, cs = 0;
-+      int block_pages, target_blocks, wl = 0;
-+      int icol = *col;
-+
-+      if (dev->target_num > 1) {
-+              block_pages = nand_block_pages(dev);
-+              target_blocks = nand_target_blocks(dev);
-+              cs = div_down(*row, block_pages * target_blocks);
-+              *row -= cs * block_pages * target_blocks;
-+      }
-+
-+      nand->select_device(nand, cs);
-+
-+      block_pages = nand_block_pages(dev);
-+      block = div_down(*row, block_pages);
-+      page = *row - block * block_pages;
-+      plane = reminder(block, dev->plane_num);
-+      lun = div_down(block, nand_lun_blocks(dev));
-+
-+      wl |= (page << dev->addressing->row_bit_start);
-+      wl |= (block << dev->addressing->block_bit_start);
-+      wl |= (plane << dev->addressing->plane_bit_start);
-+      wl |= (lun << dev->addressing->lun_bit_start);
-+
-+      *row = wl;
-+      *col = icol;
-+
-+      return 0;
-+}
-+
-+static int nand_base_read_page(struct nand_base *nand, int row)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_1st);
-+      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
-+      nfi->send_cmd(nfi, dev->cmds->read_2nd);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR);
-+}
-+
-+static int nand_base_read_data(struct nand_base *nand, int row, int col,
-+                             int sectors, u8 *data, u8 *oob)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->random_out_1st);
-+      nfi->send_addr(nfi, col, row, dev->col_cycle, dev->row_cycle);
-+      nfi->send_cmd(nfi, dev->cmds->random_out_2nd);
-+      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRCBSY);
-+
-+      return nfi->read_sectors(nfi, data, oob, sectors);
-+}
-+
-+static int nand_base_write_enable(struct nand_base *nand)
-+{
-+      struct nand_device *dev = nand->dev;
-+      int status;
-+
-+      status = nand_base_read_status(nand);
-+      if (status & dev->status->write_protect)
-+              return 0;
-+
-+      return -ENANDWP;
-+}
-+
-+static int nand_base_program_data(struct nand_base *nand, int row,
-+                                int col,
-+                                u8 *data, u8 *oob)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->program_1st);
-+      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
-+
-+      return nfi->write_page(nfi, data, oob);
-+}
-+
-+static int nand_base_program_page(struct nand_base *nand, int row)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->program_2nd);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tPROG);
-+}
-+
-+static int nand_base_erase_block(struct nand_base *nand, int row)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->erase_1st);
-+      nfi->send_addr(nfi, 0, row, 0, dev->row_cycle);
-+      nfi->send_cmd(nfi, dev->cmds->erase_2nd);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tBERS);
-+}
-+
-+static int nand_base_read_cache(struct nand_base *nand, int row)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_1st);
-+      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
-+      nfi->send_cmd(nfi, dev->cmds->read_cache);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tRCBSY);
-+}
-+
-+static int nand_base_read_last(struct nand_base *nand)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->read_cache_last);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tRCBSY);
-+}
-+
-+static int nand_base_program_cache(struct nand_base *nand)
-+{
-+      struct nfi *nfi = nand->nfi;
-+      struct nand_device *dev = nand->dev;
-+
-+      nfi->reset(nfi);
-+      nfi->send_cmd(nfi, dev->cmds->program_cache);
-+      nfi->trigger(nfi);
-+
-+      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
-+                             dev->array_timing->tPCBSY);
-+}
-+
-+struct nand_base *nand_base_init(struct nand_device *dev,
-+                               struct nfi *nfi)
-+{
-+      struct nand_base *nand;
-+
-+      nand = mem_alloc(1, sizeof(struct nand_base));
-+      if (!nand)
-+              return NULL;
-+
-+      nand->dev = dev;
-+      nand->nfi = nfi;
-+      nand->select_device = nand_base_select_device;
-+      nand->reset = nand_base_reset;
-+      nand->read_id = nand_base_read_id;
-+      nand->read_param_page = nand_base_read_param_page;
-+      nand->set_feature = nand_base_set_feature;
-+      nand->get_feature = nand_base_get_feature;
-+      nand->read_status = nand_base_read_status;
-+      nand->addressing = nand_base_addressing;
-+      nand->read_page = nand_base_read_page;
-+      nand->read_data = nand_base_read_data;
-+      nand->read_cache = nand_base_read_cache;
-+      nand->read_last = nand_base_read_last;
-+      nand->write_enable = nand_base_write_enable;
-+      nand->program_data = nand_base_program_data;
-+      nand->program_page = nand_base_program_page;
-+      nand->program_cache = nand_base_program_cache;
-+      nand->erase_block = nand_base_erase_block;
-+
-+      return nand;
-+}
-+
-+void nand_base_exit(struct nand_base *base)
-+{
-+      nfi_exit(base->nfi);
-+      mem_free(base);
-+}
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_base.h
-@@ -0,0 +1,71 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NAND_BASE_H__
-+#define __NAND_BASE_H__
-+
-+/*
-+ * nand base functions
-+ * @dev: nand device infomations
-+ * @nfi: nand host controller
-+ * @select_device: select one nand device of multi nand on chip
-+ * @reset: reset current nand device
-+ * @read_id: read current nand id
-+ * @read_param_page: read current nand parameters page
-+ * @set_feature: configurate the nand device feature
-+ * @get_feature: get the nand device feature
-+ * @read_status: read nand device status
-+ * @addressing: addressing the address to nand device physical address
-+ * @read_page: read page data to device cache register
-+ * @read_data: read data from device cache register by bus protocol
-+ * @read_cache: nand cache read operation for data output
-+ * @read_last: nand cache read operation for last page output
-+ * @write_enable: enable program/erase for nand, especially spi nand
-+ * @program_data: program data to nand device cache register
-+ * @program_page: program page data from nand device cache register to array
-+ * @program_cache: nand cache program operation for data input
-+ * @erase_block: erase nand block operation
-+ */
-+struct nand_base {
-+      struct nand_device *dev;
-+      struct nfi *nfi;
-+      int (*select_device)(struct nand_base *nand, int cs);
-+      int (*reset)(struct nand_base *nand);
-+      int (*read_id)(struct nand_base *nand, u8 *id, int count);
-+      int (*read_param_page)(struct nand_base *nand, u8 *data, int count);
-+      int (*set_feature)(struct nand_base *nand, u8 addr, u8 *param,
-+                         int count);
-+      int (*get_feature)(struct nand_base *nand, u8 addr, u8 *param,
-+                         int count);
-+      int (*read_status)(struct nand_base *nand);
-+      int (*addressing)(struct nand_base *nand, int *row, int *col);
-+
-+      int (*read_page)(struct nand_base *nand, int row);
-+      int (*read_data)(struct nand_base *nand, int row, int col, int sectors,
-+                       u8 *data, u8 *oob);
-+      int (*read_cache)(struct nand_base *nand, int row);
-+      int (*read_last)(struct nand_base *nand);
-+
-+      int (*write_enable)(struct nand_base *nand);
-+      int (*program_data)(struct nand_base *nand, int row, int col, u8 *data,
-+                          u8 *oob);
-+      int (*program_page)(struct nand_base *nand, int row);
-+      int (*program_cache)(struct nand_base *nand);
-+
-+      int (*erase_block)(struct nand_base *nand, int row);
-+};
-+
-+struct nand_base *nand_base_init(struct nand_device *device,
-+                               struct nfi *nfi);
-+void nand_base_exit(struct nand_base *base);
-+
-+struct nand_base *nand_device_init(struct nand_chip *nand);
-+void nand_exit(struct nand_base *nand);
-+
-+int nand_detect_device(struct nand_base *nand);
-+
-+#endif /* __NAND_BASE_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_chip.c
-@@ -0,0 +1,272 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "nand_chip.h"
-+#include "nand_device.h"
-+#include "nfi.h"
-+#include "nand_base.h"
-+
-+static int nand_chip_read_page(struct nand_chip *chip,
-+                             struct nand_ops *ops,
-+                             int count)
-+{
-+      struct nand_base *nand = chip->nand;
-+      struct nand_device *dev = nand->dev;
-+      int i, ret = 0;
-+      int row, col, sectors;
-+      u8 *data, *oob;
-+
-+      for (i = 0; i < count; i++) {
-+              row = ops[i].row;
-+              col = ops[i].col;
-+
-+              nand->addressing(nand, &row, &col);
-+              ops[i].status = nand->read_page(nand, row);
-+              if (ops[i].status < 0) {
-+                      ret = ops[i].status;
-+                      continue;
-+              }
-+
-+              data = ops[i].data;
-+              oob = ops[i].oob;
-+              sectors = ops[i].len / chip->sector_size;
-+              ops[i].status = nand->read_data(nand, row, col,
-+                                              sectors, data, oob);
-+              if (ops[i].status > 0)
-+                      ops[i].status = ops[i].status >=
-+                                      dev->endurance->max_bitflips ?
-+                                      -ENANDFLIPS : 0;
-+
-+              ret = min_t(int, ret, ops[i].status);
-+      }
-+
-+      return ret;
-+}
-+
-+static int nand_chip_write_page(struct nand_chip *chip,
-+                              struct nand_ops *ops,
-+                              int count)
-+{
-+      struct nand_base *nand = chip->nand;
-+      struct nand_device *dev = nand->dev;
-+      int i, ret = 0;
-+      int row, col;
-+      u8 *data, *oob;
-+
-+      for (i = 0; i < count; i++) {
-+              row = ops[i].row;
-+              col = ops[i].col;
-+
-+              nand->addressing(nand, &row, &col);
-+
-+              ops[i].status = nand->write_enable(nand);
-+              if (ops[i].status) {
-+                      pr_debug("Write Protect at %x!\n", row);
-+                      ops[i].status = -ENANDWP;
-+                      return -ENANDWP;
-+              }
-+
-+              data = ops[i].data;
-+              oob = ops[i].oob;
-+              ops[i].status = nand->program_data(nand, row, col, data, oob);
-+              if (ops[i].status < 0) {
-+                      ret = ops[i].status;
-+                      continue;
-+              }
-+
-+              ops[i].status = nand->program_page(nand, row);
-+              if (ops[i].status < 0) {
-+                      ret = ops[i].status;
-+                      continue;
-+              }
-+
-+              ops[i].status = nand->read_status(nand);
-+              if (ops[i].status & dev->status->program_fail)
-+                      ops[i].status = -ENANDWRITE;
-+
-+              ret = min_t(int, ret, ops[i].status);
-+      }
-+
-+      return ret;
-+}
-+
-+static int nand_chip_erase_block(struct nand_chip *chip,
-+                               struct nand_ops *ops,
-+                               int count)
-+{
-+      struct nand_base *nand = chip->nand;
-+      struct nand_device *dev = nand->dev;
-+      int i, ret = 0;
-+      int row, col;
-+
-+      for (i = 0; i < count; i++) {
-+              row = ops[i].row;
-+              col = ops[i].col;
-+
-+              nand->addressing(nand, &row, &col);
-+
-+              ops[i].status = nand->write_enable(nand);
-+              if (ops[i].status) {
-+                      pr_debug("Write Protect at %x!\n", row);
-+                      ops[i].status = -ENANDWP;
-+                      return -ENANDWP;
-+              }
-+
-+              ops[i].status = nand->erase_block(nand, row);
-+              if (ops[i].status < 0) {
-+                      ret = ops[i].status;
-+                      continue;
-+              }
-+
-+              ops[i].status = nand->read_status(nand);
-+              if (ops[i].status & dev->status->erase_fail)
-+                      ops[i].status = -ENANDERASE;
-+
-+              ret = min_t(int, ret, ops[i].status);
-+      }
-+
-+      return ret;
-+}
-+
-+/* read first bad mark on spare */
-+static int nand_chip_is_bad_block(struct nand_chip *chip,
-+                                struct nand_ops *ops,
-+                                int count)
-+{
-+      int i, ret, value;
-+      int status = 0;
-+      u8 *data, *tmp_buf;
-+
-+      tmp_buf = mem_alloc(1, chip->page_size);
-+      if (!tmp_buf)
-+              return -ENOMEM;
-+
-+      memset(tmp_buf, 0x00, chip->page_size);
-+
-+      /* Disable ECC */
-+      value = 0;
-+      ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value);
-+      if (ret)
-+              goto out;
-+
-+      ret = chip->read_page(chip, ops, count);
-+      if (ret)
-+              goto out;
-+
-+      for (i = 0; i < count; i++) {
-+              data = ops[i].data;
-+
-+              /* temp solution for mt7622, because of no bad mark swap */
-+              if (!memcmp(data, tmp_buf, chip->page_size)) {
-+                      ops[i].status = -ENANDBAD;
-+                      status = -ENANDBAD;
-+                      
-+              } else {
-+                      ops[i].status = 0;
-+              }
-+      }
-+
-+      /* Enable ECC */
-+      value = 1;
-+      ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value);
-+      if (ret)
-+              goto out;
-+
-+      mem_free(tmp_buf);
-+      return status;
-+
-+out:
-+      mem_free(tmp_buf);
-+      return ret;
-+}
-+
-+static int nand_chip_ctrl(struct nand_chip *chip, int cmd, void *args)
-+{
-+      return -EOPNOTSUPP;
-+}
-+
-+static int nand_chip_suspend(struct nand_chip *chip)
-+{
-+      return 0;
-+}
-+
-+static int nand_chip_resume(struct nand_chip *chip)
-+{
-+      return 0;
-+}
-+
-+struct nand_chip *nand_chip_init(struct nfi_resource *res)
-+{
-+      struct nand_chip *chip;
-+      struct nand_base *nand;
-+      struct nfi *nfi;
-+
-+      chip = mem_alloc(1, sizeof(struct nand_chip));
-+      if (!chip) {
-+              pr_info("nand chip alloc fail!\n");
-+              return NULL;
-+      }
-+
-+      nfi = nfi_init(res);
-+      if (!nfi) {
-+              pr_info("nfi init fail!\n");
-+              goto nfi_err;
-+      }
-+
-+      nand = nand_base_init(NULL, nfi);
-+      if (!nand) {
-+              pr_info("nand base init fail!\n");
-+              goto base_err;
-+      }
-+
-+      chip->nand = (void *)nand;
-+      chip->read_page = nand_chip_read_page;
-+      chip->write_page = nand_chip_write_page;
-+      chip->erase_block = nand_chip_erase_block;
-+      chip->is_bad_block = nand_chip_is_bad_block;
-+      chip->chip_ctrl = nand_chip_ctrl;
-+      chip->suspend = nand_chip_suspend;
-+      chip->resume = nand_chip_resume;
-+
-+      nand = nand_device_init(chip);
-+      if (!nand)
-+              goto nand_err;
-+
-+      chip->nand = (void *)nand;
-+      chip->plane_num = nand->dev->plane_num;
-+      chip->block_num = nand_total_blocks(nand->dev);
-+      chip->block_size = nand->dev->block_size;
-+      chip->block_pages = nand_block_pages(nand->dev);
-+      chip->page_size = nand->dev->page_size;
-+      chip->oob_size = nfi->fdm_size * div_down(chip->page_size,
-+                                                nfi->sector_size);
-+      chip->sector_size = nfi->sector_size;
-+      chip->sector_spare_size = nfi->sector_spare_size;
-+      chip->min_program_pages = nand->dev->min_program_pages;
-+      chip->ecc_strength = nfi->ecc_strength;
-+      chip->ecc_parity_size = nfi->ecc_parity_size;
-+      chip->fdm_ecc_size = nfi->fdm_ecc_size;
-+      chip->fdm_reg_size = nfi->fdm_size;
-+
-+      return chip;
-+
-+nand_err:
-+      mem_free(nand);
-+base_err:
-+      nfi_exit(nfi);
-+nfi_err:
-+      mem_free(chip);
-+      return NULL;
-+}
-+
-+void nand_chip_exit(struct nand_chip *chip)
-+{
-+      nand_exit(chip->nand);
-+      mem_free(chip);
-+}
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_chip.h
-@@ -0,0 +1,103 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NAND_CHIP_H__
-+#define __NAND_CHIP_H__
-+
-+enum nand_type {
-+      NAND_SPI,
-+      NAND_SLC,
-+      NAND_MLC,
-+      NAND_TLC
-+};
-+
-+/*
-+ * nand chip operation unit
-+ *    one nand_ops indicates one row operation
-+ * @row: nand chip row address, like as nand row
-+ * @col: nand chip column address, like as nand column
-+ * @len: operate data length, min is sector_size,
-+ *    max is page_size and sector_size aligned
-+ * @status: one operation result status
-+ * @data: data buffer for operation
-+ * @oob: oob buffer for operation, like as nand spare area
-+ */
-+struct nand_ops {
-+      int row;
-+      int col;
-+      int len;
-+      int status;
-+      void *data;
-+      void *oob;
-+};
-+
-+/*
-+ * nand chip descriptions
-+ *    nand chip includes nand controller and the several same nand devices
-+ * @nand_type: the nand type on this chip,
-+ *    the chip maybe have several nand device and the type must be same
-+ * @plane_num: the whole plane number on the chip
-+ * @block_num: the whole block number on the chip
-+ * @block_size: nand device block size
-+ * @block_pages: nand device block has page number
-+ * @page_size: nand device page size
-+ * @oob_size: chip out of band size, like as nand spare szie,
-+ *    but restricts this:
-+ *    the size is provied by nand controller(NFI),
-+ *    because NFI would use some nand spare size
-+ * @min_program_pages: chip needs min pages per program operations
-+ *    one page as one nand_ops
-+ * @sector_size: chip min read size
-+ * @sector_spare_size: spare size for sector, is spare_size/page_sectors
-+ * @ecc_strength: ecc stregth per sector_size, it would be for calculated ecc
-+ * @ecc_parity_size: ecc parity size for one  sector_size data
-+ * @nand: pointer to inherited struct nand_base
-+ * @read_page: read %count pages on chip
-+ * @write_page: write %count pages on chip
-+ * @erase_block: erase %count blocks on chip, one block is one nand_ops
-+ *    it is better to set nand_ops.row to block start row
-+ * @is_bad_block: judge the %count blocks on chip if they are bad
-+ *    by vendor specification
-+ * @chip_ctrl: control the chip features by nandx_ctrl_cmd
-+ * @suspend: suspend nand chip
-+ * @resume: resume nand chip
-+ */
-+struct nand_chip {
-+      int nand_type;
-+      int plane_num;
-+      int block_num;
-+      int block_size;
-+      int block_pages;
-+      int page_size;
-+      int oob_size;
-+
-+      int min_program_pages;
-+      int sector_size;
-+      int sector_spare_size;
-+      int ecc_strength;
-+      int ecc_parity_size;
-+      u32 fdm_ecc_size;
-+      u32 fdm_reg_size;
-+
-+      void *nand;
-+
-+      int (*read_page)(struct nand_chip *chip, struct nand_ops *ops,
-+                       int count);
-+      int (*write_page)(struct nand_chip *chip, struct nand_ops *ops,
-+                        int count);
-+      int (*erase_block)(struct nand_chip *chip, struct nand_ops *ops,
-+                         int count);
-+      int (*is_bad_block)(struct nand_chip *chip, struct nand_ops *ops,
-+                          int count);
-+      int (*chip_ctrl)(struct nand_chip *chip, int cmd, void *args);
-+      int (*suspend)(struct nand_chip *chip);
-+      int (*resume)(struct nand_chip *chip);
-+};
-+
-+struct nand_chip *nand_chip_init(struct nfi_resource *res);
-+void nand_chip_exit(struct nand_chip *chip);
-+#endif /* __NAND_CHIP_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_device.c
-@@ -0,0 +1,285 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "nand_chip.h"
-+#include "nand_device.h"
-+#include "nand_base.h"
-+
-+#define MAX_CHIP_DEVICE 4
-+#define PARAM_PAGE_LEN  2048
-+#define ONFI_CRC_BASE   0x4f4e
-+
-+static u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len)
-+{
-+      int i;
-+
-+      while (len--) {
-+              crc ^= *p++ << 8;
-+
-+              for (i = 0; i < 8; i++)
-+                      crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
-+      }
-+
-+      return crc;
-+}
-+
-+static inline void decode_addr_cycle(u8 addr_cycle, u8 *row_cycle,
-+                                   u8 *col_cycle)
-+{
-+      *row_cycle = addr_cycle & 0xf;
-+      *col_cycle = (addr_cycle >> 4) & 0xf;
-+}
-+
-+static int detect_onfi(struct nand_device *dev,
-+                     struct nand_onfi_params *onfi)
-+{
-+      struct nand_endurance *endurance = dev->endurance;
-+      u16 size, i, crc16;
-+      u8 *id;
-+
-+      size = sizeof(struct nand_onfi_params) - sizeof(u16);
-+
-+      for (i = 0; i < 3; i++) {
-+              crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&onfi[i], size);
-+
-+              if (onfi[i].signature[0] == 'O' &&
-+                  onfi[i].signature[1] == 'N' &&
-+                  onfi[i].signature[2] == 'F' &&
-+                  onfi[i].signature[3] == 'I' &&
-+                  onfi[i].crc16 == crc16)
-+                      break;
-+
-+              /* in some spi nand, onfi signature maybe "NAND" */
-+              if (onfi[i].signature[0] == 'N' &&
-+                  onfi[i].signature[1] == 'A' &&
-+                  onfi[i].signature[2] == 'N' &&
-+                  onfi[i].signature[3] == 'D' &&
-+                  onfi[i].crc16 == crc16)
-+                      break;
-+      }
-+
-+      if (i == 3)
-+              return -ENODEV;
-+
-+      memcpy(dev->name, onfi[i].model, 20);
-+      id = onfi[i].manufacturer;
-+      dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6],
-+                             id[7]);
-+      dev->id_len = MAX_ID_NUM;
-+      dev->io_width = (onfi[i].features & 1) ? NAND_IO16 : NAND_IO8;
-+      decode_addr_cycle(onfi[i].addr_cycle, &dev->row_cycle,
-+                        &dev->col_cycle);
-+      dev->target_num = 1;
-+      dev->lun_num = onfi[i].lun_num;
-+      dev->plane_num = BIT(onfi[i].plane_address_bits);
-+      dev->block_num = onfi[i].lun_blocks / dev->plane_num;
-+      dev->block_size = onfi[i].block_pages * onfi[i].page_size;
-+      dev->page_size = onfi[i].page_size;
-+      dev->spare_size = onfi[i].spare_size;
-+
-+      endurance->ecc_req = onfi[i].ecc_req;
-+      endurance->pe_cycle = onfi[i].valid_block_endurance;
-+      endurance->max_bitflips = endurance->ecc_req >> 1;
-+
-+      return 0;
-+}
-+
-+static int detect_jedec(struct nand_device *dev,
-+                      struct nand_jedec_params *jedec)
-+{
-+      struct nand_endurance *endurance = dev->endurance;
-+      u16 size, i, crc16;
-+      u8 *id;
-+
-+      size = sizeof(struct nand_jedec_params) - sizeof(u16);
-+
-+      for (i = 0; i < 3; i++) {
-+              crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&jedec[i], size);
-+
-+              if (jedec[i].signature[0] == 'J' &&
-+                  jedec[i].signature[1] == 'E' &&
-+                  jedec[i].signature[2] == 'S' &&
-+                  jedec[i].signature[3] == 'D' &&
-+                  jedec[i].crc16 == crc16)
-+                      break;
-+      }
-+
-+      if (i == 3)
-+              return -ENODEV;
-+
-+      memcpy(dev->name, jedec[i].model, 20);
-+      id = jedec[i].manufacturer;
-+      dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6],
-+                             id[7]);
-+      dev->id_len = MAX_ID_NUM;
-+      dev->io_width = (jedec[i].features & 1) ? NAND_IO16 : NAND_IO8;
-+      decode_addr_cycle(jedec[i].addr_cycle, &dev->row_cycle,
-+                        &dev->col_cycle);
-+      dev->target_num = 1;
-+      dev->lun_num = jedec[i].lun_num;
-+      dev->plane_num = BIT(jedec[i].plane_address_bits);
-+      dev->block_num = jedec[i].lun_blocks / dev->plane_num;
-+      dev->block_size = jedec[i].block_pages * jedec[i].page_size;
-+      dev->page_size = jedec[i].page_size;
-+      dev->spare_size = jedec[i].spare_size;
-+
-+      endurance->ecc_req = jedec[i].endurance_block0[0];
-+      endurance->pe_cycle = jedec[i].valid_block_endurance;
-+      endurance->max_bitflips = endurance->ecc_req >> 1;
-+
-+      return 0;
-+}
-+
-+static struct nand_device *detect_parameters_page(struct nand_base
-+                                                *nand)
-+{
-+      struct nand_device *dev = nand->dev;
-+      void *params;
-+      int ret;
-+
-+      params = mem_alloc(1, PARAM_PAGE_LEN);
-+      if (!params)
-+              return NULL;
-+
-+      memset(params, 0, PARAM_PAGE_LEN);
-+      ret = nand->read_param_page(nand, params, PARAM_PAGE_LEN);
-+      if (ret < 0) {
-+              pr_info("read parameters page fail!\n");
-+              goto error;
-+      }
-+
-+      ret = detect_onfi(dev, params);
-+      if (ret) {
-+              pr_info("detect onfi device fail! try to detect jedec\n");
-+              ret = detect_jedec(dev, params);
-+              if (ret) {
-+                      pr_info("detect jedec device fail!\n");
-+                      goto error;
-+              }
-+      }
-+
-+      mem_free(params);
-+      return dev;
-+
-+error:
-+      mem_free(params);
-+      return NULL;
-+}
-+
-+static int read_device_id(struct nand_base *nand, int cs, u8 *id)
-+{
-+      int i;
-+
-+      nand->select_device(nand, cs);
-+      nand->reset(nand);
-+      nand->read_id(nand, id, MAX_ID_NUM);
-+      pr_info("device %d ID: ", cs);
-+
-+      for (i = 0; i < MAX_ID_NUM; i++)
-+              pr_info("%x ", id[i]);
-+
-+      pr_info("\n");
-+
-+      return 0;
-+}
-+
-+static int detect_more_device(struct nand_base *nand, u8 *id)
-+{
-+      u8 id_ext[MAX_ID_NUM];
-+      int i, j, target_num = 0;
-+
-+      for (i = 1; i < MAX_CHIP_DEVICE; i++) {
-+              memset(id_ext, 0xff, MAX_ID_NUM);
-+              read_device_id(nand, i, id_ext);
-+
-+              for (j = 0; j < MAX_ID_NUM; j++) {
-+                      if (id_ext[j] != id[j])
-+                              goto out;
-+              }
-+
-+              target_num += 1;
-+      }
-+
-+out:
-+      return target_num;
-+}
-+
-+static struct nand_device *scan_device_table(const u8 *id, int id_len)
-+{
-+      struct nand_device *dev;
-+      int i = 0, j;
-+      u8 ids[MAX_ID_NUM] = {0};
-+
-+      while (1) {
-+              dev = nand_get_device(i);
-+
-+              if (!strcmp(dev->name, "NO-DEVICE"))
-+                      break;
-+
-+              if (id_len < dev->id_len) {
-+                      i += 1;
-+                      continue;
-+              }
-+
-+              NAND_UNPACK_ID(dev->id, ids, MAX_ID_NUM);
-+              for (j = 0; j < dev->id_len; j++) {
-+                      if (ids[j] != id[j])
-+                              break;
-+              }
-+
-+              if (j == dev->id_len)
-+                      break;
-+
-+              i += 1;
-+      }
-+
-+      return dev;
-+}
-+
-+int nand_detect_device(struct nand_base *nand)
-+{
-+      struct nand_device *dev;
-+      u8 id[MAX_ID_NUM] = { 0 };
-+      int target_num = 0;
-+
-+      /* Get nand device default setting for reset/read_id */
-+      nand->dev = scan_device_table(NULL, -1);
-+
-+      read_device_id(nand, 0, id);
-+      dev = scan_device_table(id, MAX_ID_NUM);
-+
-+      if (!strcmp(dev->name, "NO-DEVICE")) {
-+              pr_info("device scan fail\n");
-+              return -ENODEV;
-+      }
-+
-+      /* TobeFix: has null pointer issue in this funciton */
-+      if (!strcmp(dev->name, "NO-DEVICE")) {
-+              pr_info("device scan fail, detect parameters page\n");
-+              dev = detect_parameters_page(nand);
-+              if (!dev) {
-+                      pr_info("detect parameters fail\n");
-+                      return -ENODEV;
-+              }
-+      }
-+
-+      if (dev->target_num > 1)
-+              target_num = detect_more_device(nand, id);
-+
-+      target_num += 1;
-+      pr_debug("chip has target device num: %d\n", target_num);
-+
-+      if (dev->target_num != target_num)
-+              dev->target_num = target_num;
-+
-+      nand->dev = dev;
-+
-+      return 0;
-+}
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nand_device.h
-@@ -0,0 +1,608 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NAND_DEVICE_H__
-+#define __NAND_DEVICE_H__
-+
-+/* onfi 3.2 */
-+struct nand_onfi_params {
-+      /* Revision information and features block. 0 */
-+      /*
-+       * Byte 0: 4Fh,
-+       * Byte 1: 4Eh,
-+       * Byte 2: 46h,
-+       * Byte 3: 49h,
-+       */
-+      u8 signature[4];
-+      /*
-+       * 9-15 Reserved (0)
-+       * 8 1 = supports ONFI version 3.2
-+       * 7 1 = supports ONFI version 3.1
-+       * 6 1 = supports ONFI version 3.0
-+       * 5 1 = supports ONFI version 2.3
-+       * 4 1 = supports ONFI version 2.2
-+       * 3 1 = supports ONFI version 2.1
-+       * 2 1 = supports ONFI version 2.0
-+       * 1 1 = supports ONFI version 1.0
-+       * 0 Reserved (0)
-+       */
-+      u16 revision;
-+      /*
-+       * 13-15 Reserved (0)
-+       * 12 1 = supports external Vpp
-+       * 11 1 = supports Volume addressing
-+       * 10 1 = supports NV-DDR2
-+       * 9 1 = supports EZ NAND
-+       * 8 1 = supports program page register clear enhancement
-+       * 7 1 = supports extended parameter page
-+       * 6 1 = supports multi-plane read operations
-+       * 5 1 = supports NV-DDR
-+       * 4 1 = supports odd to even page Copyback
-+       * 3 1 = supports multi-plane program and erase operations
-+       * 2 1 = supports non-sequential page programming
-+       * 1 1 = supports multiple LUN operations
-+       * 0 1 = supports 16-bit data bus width
-+       */
-+      u16 features;
-+      /*
-+       * 13-15 Reserved (0)
-+       * 12 1 = supports LUN Get and LUN Set Features
-+       * 11 1 = supports ODT Configure
-+       * 10 1 = supports Volume Select
-+       * 9 1 = supports Reset LUN
-+       * 8 1 = supports Small Data Move
-+       * 7 1 = supports Change Row Address
-+       * 6 1 = supports Change Read Column Enhanced
-+       * 5 1 = supports Read Unique ID
-+       * 4 1 = supports Copyback
-+       * 3 1 = supports Read Status Enhanced
-+       * 2 1 = supports Get Features and Set Features
-+       * 1 1 = supports Read Cache commands
-+       * 0 1 = supports Page Cache Program command
-+       */
-+      u16 opt_cmds;
-+      /*
-+       * 4-7 Reserved (0)
-+       * 3 1 = supports Multi-plane Block Erase
-+       * 2 1 = supports Multi-plane Copyback Program
-+       * 1 1 = supports Multi-plane Page Program
-+       * 0 1 = supports Random Data Out
-+       */
-+      u8 advance_cmds;
-+      u8 reserved0[1];
-+      u16 extend_param_len;
-+      u8 param_page_num;
-+      u8 reserved1[17];
-+
-+      /* Manufacturer information block. 32 */
-+      u8 manufacturer[12];
-+      u8 model[20];
-+      u8 jedec_id;
-+      u16 data_code;
-+      u8 reserved2[13];
-+
-+      /* Memory organization block. 80 */
-+      u32 page_size;
-+      u16 spare_size;
-+      u32 partial_page_size; /* obsolete */
-+      u16 partial_spare_size; /* obsolete */
-+      u32 block_pages;
-+      u32 lun_blocks;
-+      u8 lun_num;
-+      /*
-+       * 4-7 Column address cycles
-+       * 0-3 Row address cycles
-+       */
-+      u8 addr_cycle;
-+      u8 cell_bits;
-+      u16 lun_max_bad_blocks;
-+      u16 block_endurance;
-+      u8 target_begin_valid_blocks;
-+      u16 valid_block_endurance;
-+      u8 page_program_num;
-+      u8 partial_program_attr; /* obsolete */
-+      u8 ecc_req;
-+      /*
-+       * 4-7 Reserved (0)
-+       * 0-3 Number of plane address bits
-+       */
-+      u8 plane_address_bits;
-+      /*
-+       * 6-7 Reserved (0)
-+       * 5 1 = lower bit XNOR block address restriction
-+       * 4 1 = read cache supported
-+       * 3 Address restrictions for cache operations
-+       * 2 1 = program cache supported
-+       * 1 1 = no block address restrictions
-+       * 0 Overlapped / concurrent multi-plane support
-+       */
-+      u8 multi_plane_attr;
-+      u8 ez_nand_support;
-+      u8 reserved3[12];
-+
-+      /* Electrical parameters block. 128 */
-+      u8 io_pin_max_capacitance;
-+      /*
-+       * 6-15 Reserved (0)
-+       * 5 1 = supports timing mode 5
-+       * 4 1 = supports timing mode 4
-+       * 3 1 = supports timing mode 3
-+       * 2 1 = supports timing mode 2
-+       * 1 1 = supports timing mode 1
-+       * 0 1 = supports timing mode 0, shall be 1
-+       */
-+      u16 sdr_timing_mode;
-+      u16 sdr_program_cache_timing_mode; /* obsolete */
-+      u16 tPROG;
-+      u16 tBERS;
-+      u16 tR;
-+      u16 tCCS;
-+      /*
-+       * 7 Reserved (0)
-+       * 6 1 = supports NV-DDR2 timing mode 8
-+       * 5 1 = supports NV-DDR timing mode 5
-+       * 4 1 = supports NV-DDR timing mode 4
-+       * 3 1 = supports NV-DDR timing mode 3
-+       * 2 1 = supports NV-DDR timing mode 2
-+       * 1 1 = supports NV-DDR timing mode 1
-+       * 0 1 = supports NV-DDR timing mode 0
-+       */
-+      u8 nvddr_timing_mode;
-+      /*
-+       * 7 1 = supports timing mode 7
-+       * 6 1 = supports timing mode 6
-+       * 5 1 = supports timing mode 5
-+       * 4 1 = supports timing mode 4
-+       * 3 1 = supports timing mode 3
-+       * 2 1 = supports timing mode 2
-+       * 1 1 = supports timing mode 1
-+       * 0 1 = supports timing mode 0
-+       */
-+      u8 nvddr2_timing_mode;
-+      /*
-+       * 4-7 Reserved (0)
-+       * 3 1 = device requires Vpp enablement sequence
-+       * 2 1 = device supports CLK stopped for data input
-+       * 1 1 = typical capacitance
-+       * 0 tCAD value to use
-+       */
-+      u8 nvddr_fetures;
-+      u16 clk_pin_capacitance;
-+      u16 io_pin_capacitance;
-+      u16 input_pin_capacitance;
-+      u8 input_pin_max_capacitance;
-+      /*
-+       * 3-7 Reserved (0)
-+       * 2 1 = supports 18 Ohm drive strength
-+       * 1 1 = supports 25 Ohm drive strength
-+       * 0 1 = supports driver strength settings
-+       */
-+      u8 drive_strength;
-+      u16 tR_multi_plane;
-+      u16 tADL;
-+      u16 tR_ez_nand;
-+      /*
-+       * 6-7 Reserved (0)
-+       * 5 1 = external VREFQ required for >= 200 MT/s
-+       * 4 1 = supports differential signaling for DQS
-+       * 3 1 = supports differential signaling for RE_n
-+       * 2 1 = supports ODT value of 30 Ohms
-+       * 1 1 = supports matrix termination ODT
-+       * 0 1 = supports self-termination ODT
-+       */
-+      u8 nvddr2_features;
-+      u8 nvddr2_warmup_cycles;
-+      u8 reserved4[4];
-+
-+      /* vendor block. 164 */
-+      u16 vendor_revision;
-+      u8      vendor_spec[88];
-+
-+      /* CRC for Parameter Page. 254 */
-+      u16 crc16;
-+} __packed;
-+
-+/* JESD230-B */
-+struct nand_jedec_params {
-+      /* Revision information and features block. 0 */
-+      /*
-+       * Byte 0:4Ah
-+       * Byte 1:45h
-+       * Byte 2:53h
-+       * Byte 3:44h
-+       */
-+      u8 signature[4];
-+      /*
-+       * 3-15: Reserved (0)
-+       * 2: 1 = supports parameter page revision 1.0 and standard revision 1.0
-+       * 1: 1 = supports vendor specific parameter page
-+       * 0: Reserved (0)
-+       */
-+      u16 revision;
-+      /*
-+       * 9-15 Reserved (0)
-+       * 8: 1 = supports program page register clear enhancement
-+       * 7: 1 = supports external Vpp
-+       * 6: 1 = supports Toggle Mode DDR
-+       * 5: 1 = supports Synchronous DDR
-+       * 4: 1 = supports multi-plane read operations
-+       * 3: 1 = supports multi-plane program and erase operations
-+       * 2: 1 = supports non-sequential page programming
-+       * 1: 1 = supports multiple LUN operations
-+       * 0: 1 = supports 16-bit data bus width
-+       */
-+      u16 features;
-+      /*
-+       * 11-23: Reserved (0)
-+       * 10: 1 = supports Synchronous Reset
-+       * 9: 1 = supports Reset LUN (Primary)
-+       * 8: 1 = supports Small Data Move
-+       * 7: 1 = supports Multi-plane Copyback Program (Primary)
-+       * 6: 1 = supports Random Data Out (Primary)
-+       * 5: 1 = supports Read Unique ID
-+       * 4: 1 = supports Copyback
-+       * 3: 1 = supports Read Status Enhanced (Primary)
-+       * 2: 1 = supports Get Features and Set Features
-+       * 1: 1 = supports Read Cache commands
-+       * 0: 1 = supports Page Cache Program command
-+       */
-+      u8 opt_cmds[3];
-+      /*
-+       * 8-15: Reserved (0)
-+       * 7: 1 = supports secondary Read Status Enhanced
-+       * 6: 1 = supports secondary Multi-plane Block Erase
-+       * 5: 1 = supports secondary Multi-plane Copyback Program
-+       * 4: 1 = supports secondary Multi-plane Program
-+       * 3: 1 = supports secondary Random Data Out
-+       * 2: 1 = supports secondary Multi-plane Copyback Read
-+       * 1: 1 = supports secondary Multi-plane Read Cache Random
-+       * 0: 1 = supports secondary Multi-plane Read
-+       */
-+      u16 secondary_cmds;
-+      u8 param_page_num;
-+      u8 reserved0[18];
-+
-+      /* Manufacturer information block. 32*/
-+      u8 manufacturer[12];
-+      u8 model[20];
-+      u8 jedec_id[6];
-+      u8 reserved1[10];
-+
-+      /* Memory organization block. 80 */
-+      u32 page_size;
-+      u16 spare_size;
-+      u8 reserved2[6];
-+      u32 block_pages;
-+      u32 lun_blocks;
-+      u8 lun_num;
-+      /*
-+       * 4-7 Column address cycles
-+       * 0-3 Row address cycles
-+       */
-+      u8 addr_cycle;
-+      u8 cell_bits;
-+      u8 page_program_num;
-+      /*
-+       * 4-7 Reserved (0)
-+       * 0-3 Number of plane address bits
-+       */
-+      u8 plane_address_bits;
-+      /*
-+       * 3-7: Reserved (0)
-+       * 2: 1= read cache supported
-+       * 1: 1 = program cache supported
-+       * 0: 1= No multi-plane block address restrictions
-+       */
-+      u8 multi_plane_attr;
-+      u8 reserved3[38];
-+
-+      /* Electrical parameters block. 144 */
-+      /*
-+       * 6-15: Reserved (0)
-+       * 5: 1 = supports 20 ns speed grade (50 MHz)
-+       * 4: 1 = supports 25 ns speed grade (40 MHz)
-+       * 3: 1 = supports 30 ns speed grade (~33 MHz)
-+       * 2: 1 = supports 35 ns speed grade (~28 MHz)
-+       * 1: 1 = supports 50 ns speed grade (20 MHz)
-+       * 0: 1 = supports 100 ns speed grade (10 MHz)
-+       */
-+      u16 sdr_speed;
-+      /*
-+       * 8-15: Reserved (0)
-+       * 7: 1 = supports 5 ns speed grade (200 MHz)
-+       * 6: 1 = supports 6 ns speed grade (~166 MHz)
-+       * 5: 1 = supports 7.5 ns speed grade (~133 MHz)
-+       * 4: 1 = supports 10 ns speed grade (100 MHz)
-+       * 3: 1 = supports 12 ns speed grade (~83 MHz)
-+       * 2: 1 = supports 15 ns speed grade (~66 MHz)
-+       * 1: 1 = supports 25 ns speed grade (40 MHz)
-+       * 0: 1 = supports 30 ns speed grade (~33 MHz)
-+       */
-+      u16 toggle_ddr_speed;
-+      /*
-+       * 6-15: Reserved (0)
-+       * 5: 1 = supports 10 ns speed grade (100 MHz)
-+       * 4: 1 = supports 12 ns speed grade (~83 MHz)
-+       * 3: 1 = supports 15 ns speed grade (~66 MHz)
-+       * 2: 1 = supports 20 ns speed grade (50 MHz)
-+       * 1: 1 = supports 30 ns speed grade (~33 MHz)
-+       * 0: 1 = supports 50 ns speed grade (20 MHz)
-+       */
-+      u16 sync_ddr_speed;
-+      u8 sdr_features;
-+      u8 toggle_ddr_features;
-+      /*
-+       * 2-7: Reserved (0)
-+       * 1: Device supports CK stopped for data input
-+       * 0: tCAD value to use
-+       */
-+      u8 sync_ddr_features;
-+      u16 tPROG;
-+      u16 tBERS;
-+      u16 tR;
-+      u16 tR_multi_plane;
-+      u16 tCCS;
-+      u16 io_pin_capacitance;
-+      u16 input_pin_capacitance;
-+      u16 ck_pin_capacitance;
-+      /*
-+       * 3-7: Reserved (0)
-+       * 2: 1 = supports 18 ohm drive strength
-+       * 1: 1 = supports 25 ohm drive strength
-+       * 0: 1 = supports 35ohm/50ohm drive strength
-+       */
-+      u8 drive_strength;
-+      u16 tADL;
-+      u8 reserved4[36];
-+
-+      /* ECC and endurance block. 208 */
-+      u8 target_begin_valid_blocks;
-+      u16 valid_block_endurance;
-+      /*
-+       * Byte 0: Number of bits ECC correctability
-+       * Byte 1: Codeword size
-+       * Byte 2-3: Bad blocks maximum per LUN
-+       * Byte 4-5: Block endurance
-+       * Byte 6-7: Reserved (0)
-+       */
-+      u8 endurance_block0[8];
-+      u8 endurance_block1[8];
-+      u8 endurance_block2[8];
-+      u8 endurance_block3[8];
-+      u8 reserved5[29];
-+
-+      /* Reserved. 272 */
-+      u8 reserved6[148];
-+
-+      /* Vendor specific block. 420  */
-+      u16 vendor_revision;
-+      u8      vendor_spec[88];
-+
-+      /* CRC for Parameter Page. 510 */
-+      u16 crc16;
-+} __packed;
-+
-+/* parallel nand io width */
-+enum nand_io_width {
-+      NAND_IO8,
-+      NAND_IO16
-+};
-+
-+/* all supported nand timming type */
-+enum nand_timing_type {
-+      NAND_TIMING_SDR,
-+      NAND_TIMING_SYNC_DDR,
-+      NAND_TIMING_TOGGLE_DDR,
-+      NAND_TIMING_NVDDR2
-+};
-+
-+/* nand basic commands */
-+struct nand_cmds {
-+      short reset;
-+      short read_id;
-+      short read_status;
-+      short read_param_page;
-+      short set_feature;
-+      short get_feature;
-+      short read_1st;
-+      short read_2nd;
-+      short random_out_1st;
-+      short random_out_2nd;
-+      short program_1st;
-+      short program_2nd;
-+      short erase_1st;
-+      short erase_2nd;
-+      short read_cache;
-+      short read_cache_last;
-+      short program_cache;
-+};
-+
-+/*
-+ * addressing for nand physical address
-+ * @row_bit_start: row address start bit
-+ * @block_bit_start: block address start bit
-+ * @plane_bit_start: plane address start bit
-+ * @lun_bit_start: lun address start bit
-+ */
-+struct nand_addressing {
-+      u8 row_bit_start;
-+      u8 block_bit_start;
-+      u8 plane_bit_start;
-+      u8 lun_bit_start;
-+};
-+
-+/*
-+ * nand operations status
-+ * @array_busy: indicates device array operation busy
-+ * @write_protect: indicates the device cannot be wrote or erased
-+ * @erase_fail: indicates erase operation fail
-+ * @program_fail: indicates program operation fail
-+ */
-+struct nand_status {
-+      u8 array_busy;
-+      u8 write_protect;
-+      u8 erase_fail;
-+      u8 program_fail;
-+};
-+
-+/*
-+ * nand endurance information
-+ * @pe_cycle: max program/erase cycle for nand stored data stability
-+ * @ecc_req: ecc strength required for the nand, measured per 1KB
-+ * @max_bitflips: bitflips is ecc corrected bits,
-+ *    max_bitflips is the threshold for nand stored data stability
-+ *    if corrected bits is over max_bitflips, stored data must be moved
-+ *    to another good block
-+ */
-+struct nand_endurance {
-+      int pe_cycle;
-+      int ecc_req;
-+      int max_bitflips;
-+};
-+
-+/* wait for nand busy type */
-+enum nand_wait_type {
-+      NAND_WAIT_IRQ,
-+      NAND_WAIT_POLLING,
-+      NAND_WAIT_TWHR2,
-+};
-+
-+/* each nand array operations time */
-+struct nand_array_timing {
-+      u16 tRST;
-+      u16 tWHR;
-+      u16 tR;
-+      u16 tRCBSY;
-+      u16 tFEAT;
-+      u16 tPROG;
-+      u16 tPCBSY;
-+      u16 tBERS;
-+      u16 tDBSY;
-+};
-+
-+/* nand sdr interface timing required */
-+struct nand_sdr_timing {
-+      u16 tREA;
-+      u16 tREH;
-+      u16 tCR;
-+      u16 tRP;
-+      u16 tWP;
-+      u16 tWH;
-+      u16 tWHR;
-+      u16 tCLS;
-+      u16 tALS;
-+      u16 tCLH;
-+      u16 tALH;
-+      u16 tWC;
-+      u16 tRC;
-+};
-+
-+/* nand onfi ddr (nvddr) interface timing required */
-+struct nand_onfi_timing {
-+      u16 tCAD;
-+      u16 tWPRE;
-+      u16 tWPST;
-+      u16 tWRCK;
-+      u16 tDQSCK;
-+      u16 tWHR;
-+};
-+
-+/* nand toggle ddr (toggle 1.0) interface timing required */
-+struct nand_toggle_timing {
-+      u16 tCS;
-+      u16 tCH;
-+      u16 tCAS;
-+      u16 tCAH;
-+      u16 tCALS;
-+      u16 tCALH;
-+      u16 tWP;
-+      u16 tWPRE;
-+      u16 tWPST;
-+      u16 tWPSTH;
-+      u16 tCR;
-+      u16 tRPRE;
-+      u16 tRPST;
-+      u16 tRPSTH;
-+      u16 tCDQSS;
-+      u16 tWHR;
-+};
-+
-+/* nand basic device information */
-+struct nand_device {
-+      u8 *name;
-+      u64 id;
-+      u8 id_len;
-+      u8 io_width;
-+      u8 row_cycle;
-+      u8 col_cycle;
-+      u8 target_num;
-+      u8 lun_num;
-+      u8 plane_num;
-+      int block_num;
-+      int block_size;
-+      int page_size;
-+      int spare_size;
-+      int min_program_pages;
-+      struct nand_cmds *cmds;
-+      struct nand_addressing *addressing;
-+      struct nand_status *status;
-+      struct nand_endurance *endurance;
-+      struct nand_array_timing *array_timing;
-+};
-+
-+#define NAND_DEVICE(_name, _id, _id_len, _io_width, _row_cycle, \
-+                  _col_cycle, _target_num, _lun_num, _plane_num, \
-+                  _block_num, _block_size, _page_size, _spare_size, \
-+                  _min_program_pages, _cmds, _addressing, _status, \
-+                  _endurance, _array_timing) \
-+{ \
-+      _name, _id, _id_len, _io_width, _row_cycle, \
-+      _col_cycle, _target_num, _lun_num, _plane_num, \
-+      _block_num, _block_size, _page_size, _spare_size, \
-+      _min_program_pages, _cmds, _addressing, _status, \
-+      _endurance, _array_timing \
-+}
-+
-+#define MAX_ID_NUM      sizeof(u64)
-+
-+#define NAND_PACK_ID(id0, id1, id2, id3, id4, id5, id6, id7) \
-+      ( \
-+        id0 | id1 << 8 | id2 << 16 | id3 << 24 | \
-+        (u64)id4 << 32 | (u64)id5 << 40 | \
-+        (u64)id6 << 48 | (u64)id7 << 56 \
-+      )
-+
-+#define NAND_UNPACK_ID(id, ids, len) \
-+      do { \
-+              int _i; \
-+              for (_i = 0; _i < len; _i++) \
-+                      ids[_i] = id >> (_i << 3) & 0xff; \
-+      } while (0)
-+
-+static inline int nand_block_pages(struct nand_device *device)
-+{
-+      return div_down(device->block_size, device->page_size);
-+}
-+
-+static inline int nand_lun_blocks(struct nand_device *device)
-+{
-+      return device->plane_num * device->block_num;
-+}
-+
-+static inline int nand_target_blocks(struct nand_device *device)
-+{
-+      return device->lun_num * device->plane_num * device->block_num;
-+}
-+
-+static inline int nand_total_blocks(struct nand_device *device)
-+{
-+      return device->target_num * device->lun_num * device->plane_num *
-+             device->block_num;
-+}
-+
-+struct nand_device *nand_get_device(int index);
-+#endif /* __NAND_DEVICE_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi.h
-@@ -0,0 +1,51 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFI_H__
-+#define __NFI_H__
-+
-+struct nfi_format {
-+      int page_size;
-+      int spare_size;
-+      int ecc_req;
-+};
-+
-+struct nfi {
-+      int sector_size;
-+      int sector_spare_size;
-+      int fdm_size; /*for sector*/
-+      int fdm_ecc_size;
-+      int ecc_strength;
-+      int ecc_parity_size; /*for sector*/
-+
-+      int (*select_chip)(struct nfi *nfi, int cs);
-+      int (*set_format)(struct nfi *nfi, struct nfi_format *format);
-+      int (*set_timing)(struct nfi *nfi, void *timing, int type);
-+      int (*nfi_ctrl)(struct nfi *nfi, int cmd, void *args);
-+
-+      int (*reset)(struct nfi *nfi);
-+      int (*send_cmd)(struct nfi *nfi, short cmd);
-+      int (*send_addr)(struct nfi *nfi, int col, int row,
-+                       int col_cycle, int row_cycle);
-+      int (*trigger)(struct nfi *nfi);
-+
-+      int (*write_page)(struct nfi *nfi, u8 *data, u8 *fdm);
-+      int (*write_bytes)(struct nfi *nfi, u8 *data, int count);
-+      int (*read_sectors)(struct nfi *nfi, u8 *data, u8 *fdm,
-+                          int sectors);
-+      int (*read_bytes)(struct nfi *nfi, u8 *data, int count);
-+
-+      int (*wait_ready)(struct nfi *nfi, int type, u32 timeout);
-+
-+      int (*enable_randomizer)(struct nfi *nfi, u32 row, bool encode);
-+      int (*disable_randomizer)(struct nfi *nfi);
-+};
-+
-+struct nfi *nfi_init(struct nfi_resource *res);
-+void nfi_exit(struct nfi *nfi);
-+
-+#endif /* __NFI_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_base.c
-@@ -0,0 +1,1357 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+/**
-+ * nfi_base.c - the base logic for nfi to access nand flash
-+ *
-+ * slc/mlc/tlc could use same code to access nand
-+ * of cause, there still some work need to do
-+ * even for spi nand, there should be a chance to integrate code together
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "../nfi.h"
-+#include "../nand_device.h"
-+#include "nfi_regs.h"
-+#include "nfiecc.h"
-+#include "nfi_base.h"
-+
-+static const int spare_size_mt7622[] = {
-+      16, 26, 27, 28
-+};
-+
-+#define RAND_SEED_SHIFT(op) \
-+      ((op) == RAND_ENCODE ? ENCODE_SEED_SHIFT : DECODE_SEED_SHIFT)
-+#define RAND_EN(op) \
-+      ((op) == RAND_ENCODE ? RAN_ENCODE_EN : RAN_DECODE_EN)
-+
-+#define SS_SEED_NUM     128
-+static u16 ss_randomizer_seed[SS_SEED_NUM] = {
-+      0x576A, 0x05E8, 0x629D, 0x45A3, 0x649C, 0x4BF0, 0x2342, 0x272E,
-+      0x7358, 0x4FF3, 0x73EC, 0x5F70, 0x7A60, 0x1AD8, 0x3472, 0x3612,
-+      0x224F, 0x0454, 0x030E, 0x70A5, 0x7809, 0x2521, 0x484F, 0x5A2D,
-+      0x492A, 0x043D, 0x7F61, 0x3969, 0x517A, 0x3B42, 0x769D, 0x0647,
-+      0x7E2A, 0x1383, 0x49D9, 0x07B8, 0x2578, 0x4EEC, 0x4423, 0x352F,
-+      0x5B22, 0x72B9, 0x367B, 0x24B6, 0x7E8E, 0x2318, 0x6BD0, 0x5519,
-+      0x1783, 0x18A7, 0x7B6E, 0x7602, 0x4B7F, 0x3648, 0x2C53, 0x6B99,
-+      0x0C23, 0x67CF, 0x7E0E, 0x4D8C, 0x5079, 0x209D, 0x244A, 0x747B,
-+      0x350B, 0x0E4D, 0x7004, 0x6AC3, 0x7F3E, 0x21F5, 0x7A15, 0x2379,
-+      0x1517, 0x1ABA, 0x4E77, 0x15A1, 0x04FA, 0x2D61, 0x253A, 0x1302,
-+      0x1F63, 0x5AB3, 0x049A, 0x5AE8, 0x1CD7, 0x4A00, 0x30C8, 0x3247,
-+      0x729C, 0x5034, 0x2B0E, 0x57F2, 0x00E4, 0x575B, 0x6192, 0x38F8,
-+      0x2F6A, 0x0C14, 0x45FC, 0x41DF, 0x38DA, 0x7AE1, 0x7322, 0x62DF,
-+      0x5E39, 0x0E64, 0x6D85, 0x5951, 0x5937, 0x6281, 0x33A1, 0x6A32,
-+      0x3A5A, 0x2BAC, 0x743A, 0x5E74, 0x3B2E, 0x7EC7, 0x4FD2, 0x5D28,
-+      0x751F, 0x3EF8, 0x39B1, 0x4E49, 0x746B, 0x6EF6, 0x44BE, 0x6DB7
-+};
-+
-+#if 0
-+static void dump_register(void *regs)
-+{
-+      int i;
-+
-+      pr_info("registers:\n");
-+      for (i = 0; i < 0x600; i += 0x10) {
-+              pr_info("    address 0x%X : %X %X %X %X\n",
-+                      (u32)((unsigned long)regs + i),
-+                      (u32)readl(regs + i),
-+                      (u32)readl(regs + i + 0x4),
-+                      (u32)readl(regs + i + 0x8),
-+                      (u32)readl(regs + i + 0xC));
-+      }
-+}
-+#endif
-+
-+static int nfi_enable_randomizer(struct nfi *nfi, u32 row, bool encode)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      enum randomizer_op op = RAND_ENCODE;
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      if (!encode)
-+              op = RAND_DECODE;
-+
-+      /* randomizer type and reseed type setup */
-+      val = readl(regs + NFI_CNFG);
-+      val |= CNFG_RAND_SEL | CNFG_RESEED_SEC_EN;
-+      writel(val, regs + NFI_CNFG);
-+
-+      /* randomizer seed and type setup */
-+      val = ss_randomizer_seed[row % SS_SEED_NUM] & RAN_SEED_MASK;
-+      val <<= RAND_SEED_SHIFT(op);
-+      val |= RAND_EN(op);
-+      writel(val, regs + NFI_RANDOM_CNFG);
-+
-+      return 0;
-+}
-+
-+static int nfi_disable_randomizer(struct nfi *nfi)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+
-+      writel(0, nb->res.nfi_regs + NFI_RANDOM_CNFG);
-+
-+      return 0;
-+}
-+
-+static int nfi_irq_handler(int irq, void *data)
-+{
-+      struct nfi_base *nb = (struct nfi_base *) data;
-+      void *regs = nb->res.nfi_regs;
-+      u16 status, en;
-+
-+      status = readw(regs + NFI_INTR_STA);
-+      en = readw(regs + NFI_INTR_EN);
-+
-+      if (!(status & en))
-+              return NAND_IRQ_NONE;
-+
-+      writew(~status & en, regs + NFI_INTR_EN);
-+
-+      nandx_event_complete(nb->done);
-+
-+      return NAND_IRQ_HANDLED;
-+}
-+
-+static int nfi_select_chip(struct nfi *nfi, int cs)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+
-+      writel(cs, nb->res.nfi_regs + NFI_CSEL);
-+
-+      return 0;
-+}
-+
-+static inline void set_op_mode(void *regs, u32 mode)
-+{
-+      u32 val = readl(regs + NFI_CNFG);
-+
-+      val &= ~CNFG_OP_MODE_MASK;
-+      val |= mode;
-+
-+      writel(val, regs + NFI_CNFG);
-+}
-+
-+static int nfi_reset(struct nfi *nfi)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      int ret, val;
-+
-+      /* The NFI reset to reset all registers and force the NFI
-+       * master be early terminated
-+       */
-+      writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON);
-+
-+      /* check state of NFI internal FSM and NAND interface FSM */
-+      ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, val,
-+                                      !(val & MASTER_BUS_BUSY),
-+                                      10, NFI_TIMEOUT);
-+      if (ret)
-+              pr_info("nfi reset timeout...\n");
-+
-+      writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON);
-+      writew(STAR_DE, regs + NFI_STRDATA);
-+
-+      return ret;
-+}
-+
-+static void bad_mark_swap(struct nfi *nfi, u8 *buf, u8 *fdm)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      u32 start_sector = div_down(nb->col, nfi->sector_size);
-+      u32 data_mark_pos;
-+      u8 temp;
-+
-+      /* raw access, no need to do swap. */
-+      if (!nb->ecc_en)
-+              return;
-+
-+      if (!buf || !fdm)
-+              return;
-+
-+      if (nb->bad_mark_ctrl.sector < start_sector ||
-+          nb->bad_mark_ctrl.sector > start_sector + nb->rw_sectors)
-+              return;
-+
-+      data_mark_pos = nb->bad_mark_ctrl.position +
-+                      (nb->bad_mark_ctrl.sector - start_sector) *
-+                      nfi->sector_size;
-+
-+      temp = *fdm;
-+      *fdm = *(buf + data_mark_pos);
-+      *(buf + data_mark_pos) = temp;
-+}
-+
-+static u8 *fdm_shift(struct nfi *nfi, u8 *fdm, int sector)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      u8 *pos;
-+
-+      if (!fdm)
-+              return NULL;
-+
-+      /* map the sector's FDM data to free oob:
-+       * the beginning of the oob area stores the FDM data of bad mark sectors
-+       */
-+      if (sector < nb->bad_mark_ctrl.sector)
-+              pos = fdm + (sector + 1) * nfi->fdm_size;
-+      else if (sector == nb->bad_mark_ctrl.sector)
-+              pos = fdm;
-+      else
-+              pos = fdm + sector * nfi->fdm_size;
-+
-+      return pos;
-+
-+}
-+
-+static void set_bad_mark_ctrl(struct nfi_base *nb)
-+{
-+      int temp, page_size = nb->format.page_size;
-+
-+      nb->bad_mark_ctrl.bad_mark_swap = bad_mark_swap;
-+      nb->bad_mark_ctrl.fdm_shift = fdm_shift;
-+
-+      temp = nb->nfi.sector_size + nb->nfi.sector_spare_size;
-+      nb->bad_mark_ctrl.sector = div_down(page_size, temp);
-+      nb->bad_mark_ctrl.position = reminder(page_size, temp);
-+}
-+
-+/* NOTE: check if page_size valid future */
-+static int setup_format(struct nfi_base *nb, int spare_idx)
-+{
-+      struct nfi *nfi = &nb->nfi;
-+      u32 page_size = nb->format.page_size;
-+      u32 val;
-+
-+      switch (page_size) {
-+      case 512:
-+              val = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
-+              break;
-+
-+      case KB(2):
-+              if (nfi->sector_size == 512)
-+                      val = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
-+              else
-+                      val = PAGEFMT_512_2K;
-+
-+              break;
-+
-+      case KB(4):
-+              if (nfi->sector_size == 512)
-+                      val = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
-+              else
-+                      val = PAGEFMT_2K_4K;
-+
-+              break;
-+
-+      case KB(8):
-+              if (nfi->sector_size == 512)
-+                      val = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
-+              else
-+                      val = PAGEFMT_4K_8K;
-+
-+              break;
-+
-+      case KB(16):
-+              val = PAGEFMT_8K_16K;
-+              break;
-+
-+      default:
-+              pr_info("invalid page len: %d\n", page_size);
-+              return -EINVAL;
-+      }
-+
-+      val |= spare_idx << PAGEFMT_SPARE_SHIFT;
-+      val |= nfi->fdm_size << PAGEFMT_FDM_SHIFT;
-+      val |= nfi->fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT;
-+      writel(val, nb->res.nfi_regs + NFI_PAGEFMT);
-+
-+      if (nb->custom_sector_en) {
-+              val = nfi->sector_spare_size + nfi->sector_size;
-+              val |= SECCUS_SIZE_EN;
-+              writel(val, nb->res.nfi_regs + NFI_SECCUS_SIZE);
-+      }
-+
-+      return 0;
-+}
-+
-+static int adjust_spare(struct nfi_base *nb, int *spare)
-+{
-+      int multi = nb->nfi.sector_size == 512 ? 1 : 2;
-+      int i, count = nb->caps->spare_size_num;
-+
-+      if (*spare >= nb->caps->spare_size[count - 1] * multi) {
-+              *spare = nb->caps->spare_size[count - 1] * multi;
-+              return count - 1;
-+      }
-+
-+      if (*spare < nb->caps->spare_size[0] * multi)
-+              return -EINVAL;
-+
-+      for (i = 1; i < count; i++) {
-+              if (*spare < nb->caps->spare_size[i] * multi) {
-+                      *spare = nb->caps->spare_size[i - 1] * multi;
-+                      return i - 1;
-+              }
-+      }
-+
-+      return -EINVAL;
-+}
-+
-+static int nfi_set_format(struct nfi *nfi, struct nfi_format *format)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfiecc *ecc = nb->ecc;
-+      int ecc_strength = format->ecc_req;
-+      int min_fdm, min_ecc, max_ecc;
-+      u32 temp, page_sectors;
-+      int spare_idx = 0;
-+
-+      if (!nb->buf) {
-+#if NANDX_BULK_IO_USE_DRAM
-+              nb->buf = NANDX_NFI_BUF_ADDR;
-+#else
-+              nb->buf = mem_alloc(1, format->page_size + format->spare_size);
-+#endif
-+              if (!nb->buf)
-+                      return -ENOMEM;
-+      }
-+
-+      nb->format = *format;
-+
-+      /* ToBeFixed: for spi nand, now sector size is 512,
-+       * it should be same with slc.
-+       */
-+      nfi->sector_size = 512;
-+      /* format->ecc_req is the requirement per 1KB */
-+      ecc_strength >>= 1;
-+
-+      page_sectors = div_down(format->page_size, nfi->sector_size);
-+      nfi->sector_spare_size = div_down(format->spare_size, page_sectors);
-+
-+      if (!nb->custom_sector_en) {
-+              spare_idx = adjust_spare(nb, &nfi->sector_spare_size);
-+              if (spare_idx < 0)
-+                      return -EINVAL;
-+      }
-+
-+      /* calculate ecc strength and fdm size */
-+      temp = (nfi->sector_spare_size - nb->caps->max_fdm_size) * 8;
-+      min_ecc = div_down(temp, nb->caps->ecc_parity_bits);
-+      min_ecc = ecc->adjust_strength(ecc, min_ecc);
-+      if (min_ecc < 0)
-+              return -EINVAL;
-+
-+      temp = div_up(nb->res.min_oob_req, page_sectors);
-+      temp = (nfi->sector_spare_size - temp) * 8;
-+      max_ecc = div_down(temp, nb->caps->ecc_parity_bits);
-+      max_ecc = ecc->adjust_strength(ecc, max_ecc);
-+      if (max_ecc < 0)
-+              return -EINVAL;
-+
-+      temp = div_up(temp * nb->caps->ecc_parity_bits, 8);
-+      temp = nfi->sector_spare_size - temp;
-+      min_fdm = min_t(u32, temp, (u32)nb->caps->max_fdm_size);
-+
-+      if (ecc_strength > max_ecc) {
-+              pr_info("required ecc strength %d, max supported %d\n",
-+                      ecc_strength, max_ecc);
-+              nfi->ecc_strength = max_ecc;
-+              nfi->fdm_size = min_fdm;
-+      } else if (format->ecc_req < min_ecc) {
-+              nfi->ecc_strength = min_ecc;
-+              nfi->fdm_size = nb->caps->max_fdm_size;
-+      } else {
-+              ecc_strength = ecc->adjust_strength(ecc, ecc_strength);
-+              if (ecc_strength < 0)
-+                      return -EINVAL;
-+
-+              nfi->ecc_strength = ecc_strength;
-+              temp = div_up(ecc_strength * nb->caps->ecc_parity_bits, 8);
-+              nfi->fdm_size = nfi->sector_spare_size - temp;
-+      }
-+
-+      nb->page_sectors = div_down(format->page_size, nfi->sector_size);
-+
-+      /* some IC has fixed fdm_ecc_size, if not assigend, set to fdm_size */
-+      nfi->fdm_ecc_size = nb->caps->fdm_ecc_size ? : nfi->fdm_size;
-+
-+      nfi->ecc_parity_size = div_up(nfi->ecc_strength *
-+                                    nb->caps->ecc_parity_bits,
-+                                    8);
-+      set_bad_mark_ctrl(nb);
-+
-+      pr_debug("sector_size: %d\n", nfi->sector_size);
-+      pr_debug("sector_spare_size: %d\n", nfi->sector_spare_size);
-+      pr_debug("fdm_size: %d\n", nfi->fdm_size);
-+      pr_debug("fdm_ecc_size: %d\n", nfi->fdm_ecc_size);
-+      pr_debug("ecc_strength: %d\n", nfi->ecc_strength);
-+      pr_debug("ecc_parity_size: %d\n", nfi->ecc_parity_size);
-+
-+      return setup_format(nb, spare_idx);
-+}
-+
-+static int nfi_ctrl(struct nfi *nfi, int cmd, void *args)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      int ret = 0;
-+
-+      switch (cmd) {
-+      case NFI_CTRL_DMA:
-+              nb->dma_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_AUTOFORMAT:
-+              nb->auto_format = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_NFI_IRQ:
-+              nb->nfi_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_PAGE_IRQ:
-+              nb->page_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_BAD_MARK_SWAP:
-+              nb->bad_mark_swap_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC:
-+              nb->ecc_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_MODE:
-+              nb->ecc_mode = *(enum nfiecc_mode *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_CLOCK:
-+              /* NOTE: it seems that there's nothing need to do
-+               * if new IC need, just add tht logic
-+               */
-+              nb->ecc_clk_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_IRQ:
-+              nb->ecc_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_DECODE_MODE:
-+              nb->ecc_deccon = *(enum nfiecc_deccon *)args;
-+              break;
-+
-+      default:
-+              pr_info("invalid arguments.\n");
-+              ret = -EOPNOTSUPP;
-+              break;
-+      }
-+
-+      pr_debug("%s: set cmd(%d) to %d\n", __func__, cmd, *(int *)args);
-+      return ret;
-+}
-+
-+static int nfi_send_cmd(struct nfi *nfi, short cmd)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+      u32 val;
-+
-+      pr_debug("%s: cmd 0x%x\n", __func__, cmd);
-+
-+      if (cmd < 0)
-+              return -EINVAL;
-+
-+      set_op_mode(regs, nb->op_mode);
-+
-+      writel(cmd, regs + NFI_CMD);
-+
-+      ret = readl_poll_timeout_atomic(regs + NFI_STA,
-+                                      val, !(val & STA_CMD),
-+                                      5, NFI_TIMEOUT);
-+      if (ret)
-+              pr_info("send cmd 0x%x timeout\n", cmd);
-+
-+      return ret;
-+}
-+
-+static int nfi_send_addr(struct nfi *nfi, int col, int row,
-+                       int col_cycle, int row_cycle)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+      u32 val;
-+
-+      pr_debug("%s: col 0x%x, row 0x%x, col_cycle 0x%x, row_cycle 0x%x\n",
-+               __func__, col, row, col_cycle, row_cycle);
-+
-+      nb->col = col;
-+      nb->row = row;
-+
-+      writel(col, regs + NFI_COLADDR);
-+      writel(row, regs + NFI_ROWADDR);
-+      writel(col_cycle | (row_cycle << ROW_SHIFT), regs + NFI_ADDRNOB);
-+
-+      ret = readl_poll_timeout_atomic(regs + NFI_STA,
-+                                      val, !(val & STA_ADDR),
-+                                      5, NFI_TIMEOUT);
-+      if (ret)
-+              pr_info("send address timeout\n");
-+
-+      return ret;
-+}
-+
-+static int nfi_trigger(struct nfi *nfi)
-+{
-+      /* Nothing need to do. */
-+      return 0;
-+}
-+
-+static inline int wait_io_ready(void *regs)
-+{
-+      u32 val;
-+      int ret;
-+
-+      ret = readl_poll_timeout_atomic(regs + NFI_PIO_DIRDY,
-+                                      val, val & PIO_DI_RDY,
-+                                      2, NFI_TIMEOUT);
-+      if (ret)
-+              pr_info("wait io ready timeout\n");
-+
-+      return ret;
-+}
-+
-+static int wait_ready_irq(struct nfi_base *nb, u32 timeout)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+      u32 val;
-+
-+      writel(0xf1, regs + NFI_CNRNB);
-+      nandx_event_init(nb->done);
-+
-+      writel(INTR_BUSY_RETURN_EN, (void *)(regs + NFI_INTR_EN));
-+
-+      /**
-+       * check if nand already bean ready,
-+       * avoid issue that casued by missing irq-event.
-+       */
-+      val = readl(regs + NFI_STA);
-+      if (val & STA_BUSY2READY) {
-+              readl(regs + NFI_INTR_STA);
-+              writel(0, (void *)(regs + NFI_INTR_EN));
-+              return 0;
-+      }
-+
-+      ret = nandx_event_wait_complete(nb->done, timeout);
-+
-+      writew(0, regs + NFI_CNRNB);
-+      return ret;
-+}
-+
-+static void wait_ready_twhr2(struct nfi_base *nb, u32 timeout)
-+{
-+      /* NOTE: this for tlc */
-+}
-+
-+static int wait_ready_poll(struct nfi_base *nb, u32 timeout)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+      u32 val;
-+
-+      writel(0x21, regs + NFI_CNRNB);
-+      ret = readl_poll_timeout_atomic(regs + NFI_STA, val,
-+                                      val & STA_BUSY2READY,
-+                                      2, timeout);
-+      writew(0, regs + NFI_CNRNB);
-+
-+      return ret;
-+}
-+
-+static int nfi_wait_ready(struct nfi *nfi, int type, u32 timeout)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      int ret;
-+
-+      switch (type) {
-+      case NAND_WAIT_IRQ:
-+              if (nb->nfi_irq_en)
-+                      ret = wait_ready_irq(nb, timeout);
-+              else
-+                      ret = -EINVAL;
-+
-+              break;
-+
-+      case NAND_WAIT_POLLING:
-+              ret = wait_ready_poll(nb, timeout);
-+              break;
-+
-+      case NAND_WAIT_TWHR2:
-+              wait_ready_twhr2(nb, timeout);
-+              ret = 0;
-+              break;
-+
-+      default:
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      if (ret)
-+              pr_info("%s: type 0x%x, timeout 0x%x\n",
-+                     __func__, type, timeout);
-+
-+      return ret;
-+}
-+
-+static int enable_ecc_decode(struct nfi_base *nb, int sectors)
-+{
-+      struct nfi *nfi = &nb->nfi;
-+      struct nfiecc *ecc = nb->ecc;
-+
-+      ecc->config.op = ECC_DECODE;
-+      ecc->config.mode = nb->ecc_mode;
-+      ecc->config.deccon = nb->ecc_deccon;
-+      ecc->config.sectors = sectors;
-+      ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size;
-+      ecc->config.strength = nfi->ecc_strength;
-+
-+      return ecc->enable(ecc);
-+}
-+
-+static int enable_ecc_encode(struct nfi_base *nb)
-+{
-+      struct nfiecc *ecc = nb->ecc;
-+      struct nfi *nfi = &nb->nfi;
-+
-+      ecc->config.op = ECC_ENCODE;
-+      ecc->config.mode = nb->ecc_mode;
-+      ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size;
-+      ecc->config.strength = nfi->ecc_strength;
-+
-+      return ecc->enable(ecc);
-+}
-+
-+static void read_fdm(struct nfi_base *nb, u8 *fdm, int start_sector,
-+                   int sectors)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      int j, i = start_sector;
-+      u32 vall, valm;
-+      u8 *buf = fdm;
-+
-+      for (; i < start_sector + sectors; i++) {
-+              if (nb->bad_mark_swap_en)
-+                      buf = nb->bad_mark_ctrl.fdm_shift(&nb->nfi, fdm, i);
-+
-+              vall = readl(regs + NFI_FDML(i));
-+              valm = readl(regs + NFI_FDMM(i));
-+
-+              for (j = 0; j < nb->nfi.fdm_size; j++)
-+                      *buf++ = (j >= 4 ? valm : vall) >> ((j & 3) << 3);
-+      }
-+}
-+
-+static void write_fdm(struct nfi_base *nb, u8 *fdm)
-+{
-+      struct nfi *nfi = &nb->nfi;
-+      void *regs = nb->res.nfi_regs;
-+      u32 vall, valm;
-+      int i, j;
-+      u8 *buf = fdm;
-+
-+      for (i = 0; i < nb->page_sectors; i++) {
-+              if (nb->bad_mark_swap_en)
-+                      buf = nb->bad_mark_ctrl.fdm_shift(nfi, fdm, i);
-+
-+              vall = 0;
-+              for (j = 0; j < 4; j++)
-+                      vall |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8);
-+              writel(vall, regs + NFI_FDML(i));
-+
-+              valm = 0;
-+              for (j = 0; j < 4; j++)
-+                      valm |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8);
-+              writel(valm, regs + NFI_FDMM(i));
-+      }
-+}
-+
-+/* NOTE: pio not use auto format */
-+static int pio_rx_data(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                     int sectors)
-+{
-+      struct nfiecc_status ecc_status;
-+      struct nfi *nfi = &nb->nfi;
-+      void *regs = nb->res.nfi_regs;
-+      u32 val, bitflips = 0;
-+      int len, ret, i;
-+      u8 *buf;
-+
-+      val = readl(regs + NFI_CNFG) | CNFG_BYTE_RW;
-+      writel(val, regs + NFI_CNFG);
-+
-+      len = nfi->sector_size + nfi->sector_spare_size;
-+      len *= sectors;
-+
-+      for (i = 0; i < len; i++) {
-+              ret = wait_io_ready(regs);
-+              if (ret)
-+                      return ret;
-+
-+              nb->buf[i] = readb(regs + NFI_DATAR);
-+      }
-+
-+      /* TODO: do error handle for autoformat setting of pio */
-+      if (nb->ecc_en) {
-+              for (i = 0; i < sectors; i++) {
-+                      buf = nb->buf + i * (nfi->sector_size +
-+                                           nfi->sector_spare_size);
-+                      ret = nb->ecc->correct_data(nb->ecc, &ecc_status,
-+                                                  buf, i);
-+                      if (data)
-+                              memcpy(data + i * nfi->sector_size,
-+                                     buf, nfi->sector_size);
-+                      if (fdm)
-+                              memcpy(fdm + i * nfi->fdm_size,
-+                                     buf + nfi->sector_size, nfi->fdm_size);
-+                      if (ret) {
-+                              ret = nb->ecc->decode_status(nb->ecc, i, 1);
-+                              if (ret < 0)
-+                                      return ret;
-+
-+                              bitflips = max_t(int, (int)bitflips, ret);
-+                      }
-+              }
-+
-+              return bitflips;
-+      }
-+
-+      /* raw read, only data not null, and its length should be $len */
-+      if (data)
-+              memcpy(data, nb->buf, len);
-+
-+      return 0;
-+}
-+
-+static int pio_tx_data(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                     int sectors)
-+{
-+      struct nfi *nfi = &nb->nfi;
-+      void *regs = nb->res.nfi_regs;
-+      u32 i, val;
-+      int len, ret;
-+
-+      val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW;
-+      writew(val, regs + NFI_CNFG);
-+
-+      len = nb->ecc_en ? nfi->sector_size :
-+            nfi->sector_size + nfi->sector_spare_size;
-+      len *= sectors;
-+
-+      /* data shouldn't null,
-+       * and if ecc enable ,fdm been written in prepare process
-+       */
-+      for (i = 0; i < len; i++) {
-+              ret = wait_io_ready(regs);
-+              if (ret)
-+                      return ret;
-+              writeb(data[i], regs + NFI_DATAW);
-+      }
-+
-+      return 0;
-+}
-+
-+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                        int sectors)
-+{
-+      u32 empty = readl(nb->res.nfi_regs + NFI_STA) & STA_EMP_PAGE;
-+
-+      if (empty) {
-+              pr_info("empty page!\n");
-+              return true;
-+      }
-+
-+      return false;
-+}
-+
-+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data,
-+                    u8 *fdm, bool read)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      u32 len = nb->nfi.sector_size * sectors;
-+      bool irq_en = nb->dma_en && nb->nfi_irq_en;
-+      void *dma_addr;
-+      u32 val;
-+      int ret;
-+
-+      nb->rw_sectors = sectors;
-+
-+      if (irq_en) {
-+              nandx_event_init(nb->done);
-+              writel(INTR_AHB_DONE_EN, regs + NFI_INTR_EN);
-+      }
-+
-+      val = readw(regs + NFI_CNFG);
-+      if (read)
-+              val |= CNFG_READ_EN;
-+      else
-+              val &= ~CNFG_READ_EN;
-+
-+      /* as design, now, auto format enabled when ecc enabled */
-+      if (nb->ecc_en) {
-+              val |= CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN;
-+
-+              if (read)
-+                      ret = enable_ecc_decode(nb, sectors);
-+              else
-+                      ret = enable_ecc_encode(nb);
-+
-+              if (ret) {
-+                      pr_info("%s: ecc enable %s fail!\n", __func__,
-+                             read ? "decode" : "encode");
-+                      return ret;
-+              }
-+      } else {
-+              val &= ~(CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN);
-+      }
-+
-+      if (!read && nb->bad_mark_swap_en)
-+              nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm);
-+
-+      if (!nb->ecc_en && read)
-+              len += sectors * nb->nfi.sector_spare_size;
-+
-+      if (nb->dma_en) {
-+              val |= CNFG_DMA_BURST_EN | CNFG_AHB;
-+
-+              if (read) {
-+                      dma_addr = (void *)(unsigned long)nandx_dma_map(
-+                                              nb->res.dev, nb->buf,
-+                                              (u64)len, NDMA_FROM_DEV);
-+              } else {
-+                      memcpy(nb->buf, data, len);
-+                      dma_addr = (void *)(unsigned long)nandx_dma_map(
-+                                              nb->res.dev, nb->buf,
-+                                              (u64)len, NDMA_TO_DEV);
-+              }
-+
-+              writel((unsigned long)dma_addr, (void *)regs + NFI_STRADDR);
-+
-+              nb->access_len = len;
-+              nb->dma_addr = dma_addr;
-+      }
-+
-+      if (nb->ecc_en && !read && fdm)
-+              write_fdm(nb, fdm);
-+
-+      writew(val, regs + NFI_CNFG);
-+      /* setup R/W sector number */
-+      writel(sectors << CON_SEC_SHIFT, regs + NFI_CON);
-+
-+      return 0;
-+}
-+
-+static void rw_trigger(struct nfi_base *nb, bool read)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      val = read ? CON_BRD : CON_BWR;
-+      val |= readl(regs + NFI_CON);
-+      writel(val, regs + NFI_CON);
-+
-+      writel(STAR_EN, regs + NFI_STRDATA);
-+}
-+
-+static int rw_wait_done(struct nfi_base *nb, int sectors, bool read)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      bool irq_en = nb->dma_en && nb->nfi_irq_en;
-+      int ret;
-+      u32 val;
-+
-+      if (irq_en) {
-+              ret = nandx_event_wait_complete(nb->done, NFI_TIMEOUT);
-+              if (!ret) {
-+                      writew(0, regs + NFI_INTR_EN);
-+                      return ret;
-+              }
-+      }
-+
-+      if (read) {
-+              ret = readl_poll_timeout_atomic(regs + NFI_BYTELEN, val,
-+                                              ADDRCNTR_SEC(val) >=
-+                                              (u32)sectors,
-+                                              2, NFI_TIMEOUT);
-+              /* HW issue: if not wait ahb done, need polling bus busy */
-+              if (!ret && !irq_en)
-+                      ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA,
-+                                                      val,
-+                                                      !(val &
-+                                                        MASTER_BUS_BUSY),
-+                                                      2, NFI_TIMEOUT);
-+      } else {
-+              ret = readl_poll_timeout_atomic(regs + NFI_ADDRCNTR, val,
-+                                              ADDRCNTR_SEC(val) >=
-+                                              (u32)sectors,
-+                                              2, NFI_TIMEOUT);
-+      }
-+
-+      if (ret) {
-+              pr_info("do page %s timeout\n", read ? "read" : "write");
-+              return ret;
-+      }
-+
-+      if (read && nb->ecc_en) {
-+              ret = nb->ecc->wait_done(nb->ecc);
-+              if (ret)
-+                      return ret;
-+
-+              return nb->ecc->decode_status(nb->ecc, 0, sectors);
-+      }
-+
-+      return 0;
-+}
-+
-+static int rw_data(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors,
-+                 bool read)
-+{
-+      if (read && nb->dma_en && nb->ecc_en && fdm)
-+              read_fdm(nb, fdm, 0, sectors);
-+
-+      if (!nb->dma_en) {
-+              if (read)
-+                      return pio_rx_data(nb, data, fdm, sectors);
-+
-+              return pio_tx_data(nb, data, fdm, sectors);
-+      }
-+
-+      return 0;
-+}
-+
-+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                      bool read)
-+{
-+      int data_len = 0;
-+      bool is_empty;
-+
-+      if (nb->dma_en) {
-+              if (read) {
-+                      nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr,
-+                                      (u64)nb->access_len, NDMA_FROM_DEV);
-+
-+                      if (data) {
-+                              data_len = nb->rw_sectors * nb->nfi.sector_size;
-+                              memcpy(data, nb->buf, data_len);
-+                      }
-+
-+                      if (fdm)
-+                              memcpy(fdm, nb->buf + data_len,
-+                                     nb->access_len - data_len);
-+
-+                      if (nb->read_status == -ENANDREAD) {
-+                              is_empty = nb->is_page_empty(nb, data, fdm,
-+                                                      nb->rw_sectors);
-+                              if (is_empty)
-+                                      nb->read_status = 0;
-+                      }
-+              } else {
-+                      nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr,
-+                                      (u64)nb->access_len, NDMA_TO_DEV);
-+              }
-+      }
-+
-+      /* whether it's reading or writing, we all check if nee swap
-+       * for write, we need to restore data
-+       */
-+      if (nb->bad_mark_swap_en)
-+              nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm);
-+
-+      if (nb->ecc_en)
-+              nb->ecc->disable(nb->ecc);
-+
-+      writel(0, nb->res.nfi_regs + NFI_CNFG);
-+      writel(0, nb->res.nfi_regs + NFI_CON);
-+}
-+
-+static int nfi_read_sectors(struct nfi *nfi, u8 *data, u8 *fdm,
-+                          int sectors)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      int bitflips = 0, ret;
-+
-+      pr_debug("%s: read page#%d\n", __func__, nb->row);
-+      pr_debug("%s: data address 0x%x, fdm address 0x%x, sectors 0x%x\n",
-+               __func__, (u32)((unsigned long)data),
-+               (u32)((unsigned long)fdm), sectors);
-+
-+      nb->read_status = 0;
-+
-+      ret = nb->rw_prepare(nb, sectors, data, fdm, true);
-+      if (ret)
-+              return ret;
-+
-+      nb->rw_trigger(nb, true);
-+
-+      if (nb->dma_en) {
-+              ret = nb->rw_wait_done(nb, sectors, true);
-+              if (ret > 0)
-+                      bitflips = ret;
-+              else if (ret == -ENANDREAD)
-+                      nb->read_status = -ENANDREAD;
-+              else if (ret < 0)
-+                      goto complete;
-+
-+      }
-+
-+      ret = nb->rw_data(nb, data, fdm, sectors, true);
-+      if (ret > 0)
-+              ret = max_t(int, ret, bitflips);
-+
-+complete:
-+      nb->rw_complete(nb, data, fdm, true);
-+
-+      if (nb->read_status == -ENANDREAD)
-+              return -ENANDREAD;
-+
-+      return ret;
-+}
-+
-+int nfi_write_page(struct nfi *nfi, u8 *data, u8 *fdm)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      u32 sectors = div_down(nb->format.page_size, nfi->sector_size);
-+      int ret;
-+
-+      pr_debug("%s: data address 0x%x, fdm address 0x%x\n",
-+               __func__, (int)((unsigned long)data),
-+               (int)((unsigned long)fdm));
-+
-+      ret = nb->rw_prepare(nb, sectors, data, fdm, false);
-+      if (ret)
-+              return ret;
-+
-+      nb->rw_trigger(nb, false);
-+
-+      ret = nb->rw_data(nb, data, fdm, sectors, false);
-+      if (ret)
-+              return ret;
-+
-+      ret = nb->rw_wait_done(nb, sectors, false);
-+
-+      nb->rw_complete(nb, data, fdm, false);
-+
-+      return ret;
-+}
-+
-+static int nfi_rw_bytes(struct nfi *nfi, u8 *data, int count, bool read)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      int i, ret;
-+      u32 val;
-+
-+      for (i = 0; i < count; i++) {
-+              val = readl(regs + NFI_STA) & NFI_FSM_MASK;
-+              if (val != NFI_FSM_CUSTDATA) {
-+                      val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW;
-+                      if (read)
-+                              val |= CNFG_READ_EN;
-+                      writew(val, regs + NFI_CNFG);
-+
-+                      val = div_up(count, nfi->sector_size);
-+                      val = (val << CON_SEC_SHIFT) | CON_BRD | CON_BWR;
-+                      writel(val, regs + NFI_CON);
-+
-+                      writew(STAR_EN, regs + NFI_STRDATA);
-+              }
-+
-+              ret = wait_io_ready(regs);
-+              if (ret)
-+                      return ret;
-+
-+              if (read)
-+                      data[i] = readb(regs + NFI_DATAR);
-+              else
-+                      writeb(data[i], regs + NFI_DATAW);
-+      }
-+
-+      writel(0, nb->res.nfi_regs + NFI_CNFG);
-+
-+      return 0;
-+}
-+
-+static int nfi_read_bytes(struct nfi *nfi, u8 *data, int count)
-+{
-+      return nfi_rw_bytes(nfi, data, count, true);
-+}
-+
-+static int nfi_write_bytes(struct nfi *nfi, u8 *data, int count)
-+{
-+      return nfi_rw_bytes(nfi, data, count, false);
-+}
-+
-+/* As register map says, only when flash macro is idle,
-+ * sw reset or nand interface change can be issued
-+ */
-+static inline int wait_flash_macro_idle(void *regs)
-+{
-+      u32 val;
-+
-+      return readl_poll_timeout_atomic(regs + NFI_STA, val,
-+                                       val & FLASH_MACRO_IDLE, 2,
-+                                       NFI_TIMEOUT);
-+}
-+
-+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
-+      ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \
-+       (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt))
-+
-+static int nfi_set_sdr_timing(struct nfi *nfi, void *timing, u8 type)
-+{
-+      struct nand_sdr_timing *sdr = (struct nand_sdr_timing *) timing;
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      u32 tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt, tstrobe;
-+      u32 rate, val;
-+      int ret;
-+
-+      ret = wait_flash_macro_idle(regs);
-+      if (ret)
-+              return ret;
-+
-+      /* turn clock rate into KHZ */
-+      rate = nb->res.clock_1x / 1000;
-+
-+      tpoecs = max_t(u16, sdr->tALH, sdr->tCLH);
-+      tpoecs = div_up(tpoecs * rate, 1000000);
-+      tpoecs &= 0xf;
-+
-+      tprecs = max_t(u16, sdr->tCLS, sdr->tALS);
-+      tprecs = div_up(tprecs * rate, 1000000);
-+      tprecs &= 0x3f;
-+
-+      /* tc2r is in unit of 2T */
-+      tc2r = div_up(sdr->tCR * rate, 1000000);
-+      tc2r = div_down(tc2r, 2);
-+      tc2r &= 0x3f;
-+
-+      tw2r = div_up(sdr->tWHR * rate, 1000000);
-+      tw2r = div_down(tw2r, 2);
-+      tw2r &= 0xf;
-+
-+      twh = max_t(u16, sdr->tREH, sdr->tWH);
-+      twh = div_up(twh * rate, 1000000) - 1;
-+      twh &= 0xf;
-+
-+      twst = div_up(sdr->tWP * rate, 1000000) - 1;
-+      twst &= 0xf;
-+
-+      trlt = div_up(sdr->tRP * rate, 1000000) - 1;
-+      trlt &= 0xf;
-+
-+      /* If tREA is bigger than tRP, setup strobe sel here */
-+      if ((trlt + 1) * 1000000 / rate < sdr->tREA) {
-+              tstrobe = sdr->tREA - (trlt + 1) * 1000000 / rate;
-+              tstrobe = div_up(tstrobe * rate, 1000000);
-+              val = readl(regs + NFI_DEBUG_CON1);
-+              val &= ~STROBE_MASK;
-+              val |= tstrobe << STROBE_SHIFT;
-+              writel(val, regs + NFI_DEBUG_CON1);
-+      }
-+
-+      /*
-+       * ACCON: access timing control register
-+       * -------------------------------------
-+       * 31:28: tpoecs, minimum required time for CS post pulling down after
-+       *        accessing the device
-+       * 27:22: tprecs, minimum required time for CS pre pulling down before
-+       *        accessing the device
-+       * 21:16: tc2r, minimum required time from NCEB low to NREB low
-+       * 15:12: tw2r, minimum required time from NWEB high to NREB low.
-+       * 11:08: twh, write enable hold time
-+       * 07:04: twst, write wait states
-+       * 03:00: trlt, read wait states
-+       */
-+      val = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt);
-+      pr_info("acctiming: 0x%x\n", val);
-+      writel(val, regs + NFI_ACCCON);
-+
-+      /* set NAND type */
-+      writel(NAND_TYPE_ASYNC, regs + NFI_NAND_TYPE_CNFG);
-+
-+      return ret;
-+}
-+
-+static int nfi_set_timing(struct nfi *nfi, void *timing, int type)
-+{
-+      switch (type) {
-+      case NAND_TIMING_SDR:
-+              return nfi_set_sdr_timing(nfi, timing, type);
-+
-+      /* NOTE: for mlc/tlc */
-+      case NAND_TIMING_SYNC_DDR:
-+      case NAND_TIMING_TOGGLE_DDR:
-+      case NAND_TIMING_NVDDR2:
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      return 0;
-+}
-+
-+static void set_nfi_funcs(struct nfi *nfi)
-+{
-+      nfi->select_chip = nfi_select_chip;
-+      nfi->set_format = nfi_set_format;
-+      nfi->nfi_ctrl = nfi_ctrl;
-+      nfi->set_timing = nfi_set_timing;
-+
-+      nfi->reset = nfi_reset;
-+      nfi->send_cmd = nfi_send_cmd;
-+      nfi->send_addr = nfi_send_addr;
-+      nfi->trigger = nfi_trigger;
-+
-+      nfi->write_page = nfi_write_page;
-+      nfi->write_bytes = nfi_write_bytes;
-+      nfi->read_sectors = nfi_read_sectors;
-+      nfi->read_bytes = nfi_read_bytes;
-+
-+      nfi->wait_ready = nfi_wait_ready;
-+
-+      nfi->enable_randomizer = nfi_enable_randomizer;
-+      nfi->disable_randomizer = nfi_disable_randomizer;
-+}
-+
-+static struct nfi_caps nfi_caps_mt7622 = {
-+      .max_fdm_size = 8,
-+      .fdm_ecc_size = 1,
-+      .ecc_parity_bits = 13,
-+      .spare_size = spare_size_mt7622,
-+      .spare_size_num = 4,
-+};
-+
-+static struct nfi_caps *nfi_get_match_data(enum mtk_ic_version ic)
-+{
-+      /* NOTE: add other IC's data */
-+      return &nfi_caps_mt7622;
-+}
-+
-+static void set_nfi_base_params(struct nfi_base *nb)
-+{
-+      nb->ecc_en = false;
-+      nb->dma_en = false;
-+      nb->nfi_irq_en = false;
-+      nb->ecc_irq_en = false;
-+      nb->page_irq_en = false;
-+      nb->ecc_clk_en = false;
-+      nb->randomize_en = false;
-+      nb->custom_sector_en = false;
-+      nb->bad_mark_swap_en = false;
-+
-+      nb->op_mode = CNFG_CUSTOM_MODE;
-+      nb->ecc_deccon = ECC_DEC_CORRECT;
-+      nb->ecc_mode = ECC_NFI_MODE;
-+
-+      nb->done = nandx_event_create();
-+      nb->caps = nfi_get_match_data(nb->res.ic_ver);
-+
-+      nb->set_op_mode = set_op_mode;
-+      nb->is_page_empty = is_page_empty;
-+
-+      nb->rw_prepare = rw_prepare;
-+      nb->rw_trigger = rw_trigger;
-+      nb->rw_wait_done = rw_wait_done;
-+      nb->rw_data = rw_data;
-+      nb->rw_complete = rw_complete;
-+}
-+
-+struct nfi *__weak nfi_extend_init(struct nfi_base *nb)
-+{
-+      return &nb->nfi;
-+}
-+
-+void __weak nfi_extend_exit(struct nfi_base *nb)
-+{
-+      mem_free(nb);
-+}
-+
-+struct nfi *nfi_init(struct nfi_resource *res)
-+{
-+      struct nfiecc_resource ecc_res;
-+      struct nfi_base *nb;
-+      struct nfiecc *ecc;
-+      struct nfi *nfi;
-+      int ret;
-+
-+      nb = mem_alloc(1, sizeof(struct nfi_base));
-+      if (!nb) {
-+              pr_info("nfi alloc memory fail @%s.\n", __func__);
-+              return NULL;
-+      }
-+
-+      nb->res = *res;
-+
-+      ret = nandx_irq_register(res->dev, res->nfi_irq_id, nfi_irq_handler,
-+                               "mtk_nand", nb);
-+      if (ret) {
-+              pr_info("nfi irq register failed!\n");
-+              goto error;
-+      }
-+
-+      /* fill ecc paras and init ecc */
-+      ecc_res.ic_ver = nb->res.ic_ver;
-+      ecc_res.dev = nb->res.dev;
-+      ecc_res.irq_id = nb->res.ecc_irq_id;
-+      ecc_res.regs = nb->res.ecc_regs;
-+      ecc = nfiecc_init(&ecc_res);
-+      if (!ecc) {
-+              pr_info("nfiecc init fail.\n");
-+              return NULL;
-+      }
-+
-+      nb->ecc = ecc;
-+
-+      set_nfi_base_params(nb);
-+      set_nfi_funcs(&nb->nfi);
-+
-+      /* Assign a temp sector size for reading ID & para page.
-+       * We may assign new value later.
-+       */
-+      nb->nfi.sector_size = 512;
-+
-+      /* give a default timing, and as discuss
-+       * this is the only thing what we need do for nfi init
-+       * if need do more, then we can add a function
-+       */
-+      writel(0x30C77FFF, nb->res.nfi_regs + NFI_ACCCON);
-+
-+      nfi = nfi_extend_init(nb);
-+      if (nfi)
-+              return nfi;
-+
-+error:
-+      mem_free(nb);
-+      return NULL;
-+}
-+
-+void nfi_exit(struct nfi *nfi)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+
-+      nandx_event_destroy(nb->done);
-+      nfiecc_exit(nb->ecc);
-+#if !NANDX_BULK_IO_USE_DRAM
-+      mem_free(nb->buf);
-+#endif
-+      nfi_extend_exit(nb);
-+}
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_base.h
-@@ -0,0 +1,95 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFI_BASE_H__
-+#define __NFI_BASE_H__
-+
-+#define NFI_TIMEOUT             1000000
-+
-+enum randomizer_op {
-+      RAND_ENCODE,
-+      RAND_DECODE
-+};
-+
-+struct bad_mark_ctrl {
-+      void (*bad_mark_swap)(struct nfi *nfi, u8 *buf, u8 *fdm);
-+      u8 *(*fdm_shift)(struct nfi *nfi, u8 *fdm, int sector);
-+      u32 sector;
-+      u32 position;
-+};
-+
-+struct nfi_caps {
-+      u8 max_fdm_size;
-+      u8 fdm_ecc_size;
-+      u8 ecc_parity_bits;
-+      const int *spare_size;
-+      u32 spare_size_num;
-+};
-+
-+struct nfi_base {
-+      struct nfi nfi;
-+      struct nfi_resource res;
-+      struct nfiecc *ecc;
-+      struct nfi_format format;
-+      struct nfi_caps *caps;
-+      struct bad_mark_ctrl bad_mark_ctrl;
-+
-+      /* page_size + spare_size */
-+      u8 *buf;
-+
-+      /* used for spi nand */
-+      u8 cmd_mode;
-+      u32 op_mode;
-+
-+      int page_sectors;
-+
-+      void *done;
-+
-+      /* for read/write */
-+      int col;
-+      int row;
-+      int access_len;
-+      int rw_sectors;
-+      void *dma_addr;
-+      int read_status;
-+
-+      bool dma_en;
-+      bool nfi_irq_en;
-+      bool page_irq_en;
-+      bool auto_format;
-+      bool ecc_en;
-+      bool ecc_irq_en;
-+      bool ecc_clk_en;
-+      bool randomize_en;
-+      bool custom_sector_en;
-+      bool bad_mark_swap_en;
-+
-+      enum nfiecc_deccon ecc_deccon;
-+      enum nfiecc_mode ecc_mode;
-+
-+      void (*set_op_mode)(void *regs, u32 mode);
-+      bool (*is_page_empty)(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                            int sectors);
-+
-+      int (*rw_prepare)(struct nfi_base *nb, int sectors, u8 *data, u8 *fdm,
-+                        bool read);
-+      void (*rw_trigger)(struct nfi_base *nb, bool read);
-+      int (*rw_wait_done)(struct nfi_base *nb, int sectors, bool read);
-+      int (*rw_data)(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors,
-+                     bool read);
-+      void (*rw_complete)(struct nfi_base *nb, u8 *data, u8 *fdm, bool read);
-+};
-+
-+static inline struct nfi_base *nfi_to_base(struct nfi *nfi)
-+{
-+      return container_of(nfi, struct nfi_base, nfi);
-+}
-+
-+struct nfi *nfi_extend_init(struct nfi_base *nb);
-+void nfi_extend_exit(struct nfi_base *nb);
-+
-+#endif /* __NFI_BASE_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_regs.h
-@@ -0,0 +1,114 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFI_REGS_H__
-+#define __NFI_REGS_H__
-+
-+#define NFI_CNFG                0x000
-+#define         CNFG_AHB                BIT(0)
-+#define         CNFG_READ_EN            BIT(1)
-+#define         CNFG_DMA_BURST_EN       BIT(2)
-+#define         CNFG_RESEED_SEC_EN      BIT(4)
-+#define         CNFG_RAND_SEL           BIT(5)
-+#define         CNFG_BYTE_RW            BIT(6)
-+#define         CNFG_HW_ECC_EN          BIT(8)
-+#define         CNFG_AUTO_FMT_EN        BIT(9)
-+#define         CNFG_RAND_MASK          GENMASK(5, 4)
-+#define         CNFG_OP_MODE_MASK       GENMASK(14, 12)
-+#define         CNFG_IDLE_MOD           0
-+#define         CNFG_READ_MODE          (1 << 12)
-+#define         CNFG_SINGLE_READ_MODE   (2 << 12)
-+#define         CNFG_PROGRAM_MODE       (3 << 12)
-+#define         CNFG_ERASE_MODE         (4 << 12)
-+#define         CNFG_RESET_MODE         (5 << 12)
-+#define         CNFG_CUSTOM_MODE        (6 << 12)
-+#define NFI_PAGEFMT             0x004
-+#define         PAGEFMT_SPARE_SHIFT     4
-+#define         PAGEFMT_FDM_ECC_SHIFT   12
-+#define         PAGEFMT_FDM_SHIFT       8
-+#define         PAGEFMT_SEC_SEL_512     BIT(2)
-+#define         PAGEFMT_512_2K          0
-+#define         PAGEFMT_2K_4K           1
-+#define         PAGEFMT_4K_8K           2
-+#define         PAGEFMT_8K_16K          3
-+#define NFI_CON                 0x008
-+#define         CON_FIFO_FLUSH          BIT(0)
-+#define         CON_NFI_RST             BIT(1)
-+#define         CON_BRD                 BIT(8)
-+#define         CON_BWR                 BIT(9)
-+#define         CON_SEC_SHIFT           12
-+#define NFI_ACCCON              0x00c
-+#define NFI_INTR_EN             0x010
-+#define         INTR_BUSY_RETURN_EN     BIT(4)
-+#define         INTR_AHB_DONE_EN        BIT(6)
-+#define NFI_INTR_STA            0x014
-+#define NFI_CMD                 0x020
-+#define NFI_ADDRNOB             0x030
-+#define         ROW_SHIFT               4
-+#define NFI_COLADDR             0x034
-+#define NFI_ROWADDR             0x038
-+#define NFI_STRDATA             0x040
-+#define         STAR_EN                 1
-+#define         STAR_DE                 0
-+#define NFI_CNRNB               0x044
-+#define NFI_DATAW               0x050
-+#define NFI_DATAR               0x054
-+#define NFI_PIO_DIRDY           0x058
-+#define         PIO_DI_RDY              1
-+#define NFI_STA                 0x060
-+#define         STA_CMD                 BIT(0)
-+#define         STA_ADDR                BIT(1)
-+#define         FLASH_MACRO_IDLE        BIT(5)
-+#define         STA_BUSY                BIT(8)
-+#define         STA_BUSY2READY          BIT(9)
-+#define         STA_EMP_PAGE            BIT(12)
-+#define         NFI_FSM_CUSTDATA        (0xe << 16)
-+#define         NFI_FSM_MASK            GENMASK(19, 16)
-+#define         NAND_FSM_MASK           GENMASK(29, 23)
-+#define NFI_ADDRCNTR            0x070
-+#define         CNTR_VALID_MASK         GENMASK(16, 0)
-+#define         CNTR_MASK               GENMASK(15, 12)
-+#define         ADDRCNTR_SEC_SHIFT      12
-+#define         ADDRCNTR_SEC(val) \
-+      (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT)
-+#define NFI_STRADDR             0x080
-+#define NFI_BYTELEN             0x084
-+#define NFI_CSEL                0x090
-+#define NFI_FDML(x)             (0x0a0 + (x) * 8)
-+#define NFI_FDMM(x)             (0x0a4 + (x) * 8)
-+#define NFI_DEBUG_CON1          0x220
-+#define         STROBE_MASK             GENMASK(4, 3)
-+#define         STROBE_SHIFT            3
-+#define         ECC_CLK_EN              BIT(11)
-+#define         AUTOC_SRAM_MODE         BIT(12)
-+#define         BYPASS_MASTER_EN        BIT(15)
-+#define NFI_MASTER_STA          0x224
-+#define         MASTER_BUS_BUSY         0x3
-+#define NFI_SECCUS_SIZE         0x22c
-+#define         SECCUS_SIZE_EN          BIT(17)
-+#define NFI_RANDOM_CNFG         0x238
-+#define         RAN_ENCODE_EN           BIT(0)
-+#define         ENCODE_SEED_SHIFT       1
-+#define         RAN_DECODE_EN           BIT(16)
-+#define         DECODE_SEED_SHIFT       17
-+#define         RAN_SEED_MASK           0x7fff
-+#define NFI_EMPTY_THRESH        0x23c
-+#define NFI_NAND_TYPE_CNFG      0x240
-+#define         NAND_TYPE_ASYNC         0
-+#define         NAND_TYPE_TOGGLE        1
-+#define         NAND_TYPE_SYNC          2
-+#define NFI_ACCCON1             0x244
-+#define NFI_DELAY_CTRL          0x248
-+#define NFI_TLC_RD_WHR2         0x300
-+#define         TLC_RD_WHR2_EN          BIT(12)
-+#define         TLC_RD_WHR2_MASK        GENMASK(11, 0)
-+#define SNF_SNF_CNFG            0x55c
-+#define         SPI_MODE_EN             1
-+#define         SPI_MODE_DIS            0
-+
-+#endif /* __NFI_REGS_H__ */
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.c
-@@ -0,0 +1,689 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "../nfi.h"
-+#include "nfiecc.h"
-+#include "nfi_regs.h"
-+#include "nfi_base.h"
-+#include "nfi_spi_regs.h"
-+#include "nfi_spi.h"
-+
-+#define NFI_CMD_DUMMY_RD 0x00
-+#define NFI_CMD_DUMMY_WR 0x80
-+
-+static struct nfi_spi_delay spi_delay[SPI_NAND_MAX_DELAY] = {
-+      /*
-+       * tCLK_SAM_DLY, tCLK_OUT_DLY, tCS_DLY, tWR_EN_DLY,
-+       * tIO_IN_DLY[4], tIO_OUT_DLY[4], tREAD_LATCH_LATENCY
-+       */
-+      {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
-+      {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
-+      {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
-+      {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1},
-+      {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1},
-+      {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}
-+};
-+
-+static inline struct nfi_spi *base_to_snfi(struct nfi_base *nb)
-+{
-+      return container_of(nb, struct nfi_spi, base);
-+}
-+
-+static void snfi_mac_enable(struct nfi_base *nb)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      val = readl(regs + SNF_MAC_CTL);
-+      val &= ~MAC_XIO_SEL;
-+      val |= SF_MAC_EN;
-+
-+      writel(val, regs + SNF_MAC_CTL);
-+}
-+
-+static void snfi_mac_disable(struct nfi_base *nb)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      val = readl(regs + SNF_MAC_CTL);
-+      val &= ~(SF_TRIG | SF_MAC_EN);
-+      writel(val, regs + SNF_MAC_CTL);
-+}
-+
-+static int snfi_mac_trigger(struct nfi_base *nb)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+      u32 val;
-+
-+      val = readl(regs + SNF_MAC_CTL);
-+      val |= SF_TRIG;
-+      writel(val, regs + SNF_MAC_CTL);
-+
-+      ret = readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val,
-+                                      val & WIP_READY, 10,
-+                                      NFI_TIMEOUT);
-+      if (ret) {
-+              pr_info("polling wip ready for read timeout\n");
-+              return ret;
-+      }
-+
-+      return readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val,
-+                                       !(val & WIP), 10,
-+                                       NFI_TIMEOUT);
-+}
-+
-+static int snfi_mac_op(struct nfi_base *nb)
-+{
-+      int ret;
-+
-+      snfi_mac_enable(nb);
-+      ret = snfi_mac_trigger(nb);
-+      snfi_mac_disable(nb);
-+
-+      return ret;
-+}
-+
-+static void snfi_write_mac(struct nfi_spi *nfi_spi, u8 *data, int count)
-+{
-+      struct nandx_split32 split = {0};
-+      u32 reg_offset = round_down(nfi_spi->tx_count, 4);
-+      void *regs = nfi_spi->base.res.nfi_regs;
-+      u32 data_offset = 0, i, val;
-+      u8 *p_val = (u8 *)(&val);
-+
-+      nandx_split(&split, nfi_spi->tx_count, count, val, 4);
-+
-+      if (split.head_len) {
-+              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
-+
-+              for (i = 0; i < split.head_len; i++)
-+                      p_val[split.head + i] = data[i];
-+
-+              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
-+      }
-+
-+      if (split.body_len) {
-+              reg_offset = split.body;
-+              data_offset = split.head_len;
-+
-+              for (i = 0; i < split.body_len; i++) {
-+                      p_val[i & 3] = data[data_offset + i];
-+
-+                      if ((i & 3) == 3) {
-+                              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
-+                              reg_offset += 4;
-+                      }
-+              }
-+      }
-+
-+      if (split.tail_len) {
-+              reg_offset = split.tail;
-+              data_offset += split.body_len;
-+
-+              for (i = 0; i < split.tail_len; i++) {
-+                      p_val[i] = data[data_offset + i];
-+
-+                      if (i == split.tail_len - 1)
-+                              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
-+              }
-+      }
-+}
-+
-+static void snfi_read_mac(struct nfi_spi *nfi_spi, u8 *data, int count)
-+{
-+      void *regs = nfi_spi->base.res.nfi_regs;
-+      u32 reg_offset = round_down(nfi_spi->tx_count, 4);
-+      struct nandx_split32 split = {0};
-+      u32 data_offset = 0, i, val;
-+      u8 *p_val = (u8 *)&val;
-+
-+      nandx_split(&split, nfi_spi->tx_count, count, val, 4);
-+
-+      if (split.head_len) {
-+              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
-+
-+              for (i = 0; i < split.head_len; i++)
-+                      data[data_offset + i] = p_val[split.head + i];
-+      }
-+
-+      if (split.body_len) {
-+              reg_offset = split.body;
-+              data_offset = split.head_len;
-+
-+              for (i = 0; i < split.body_len; i++) {
-+                      if ((i & 3) == 0) {
-+                              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
-+                              reg_offset += 4;
-+                      }
-+
-+                      data[data_offset + i] = p_val[i % 4];
-+              }
-+      }
-+
-+      if (split.tail_len) {
-+              reg_offset = split.tail;
-+              data_offset += split.body_len;
-+              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
-+
-+              for (i = 0; i < split.tail_len; i++)
-+                      data[data_offset + i] = p_val[i];
-+      }
-+}
-+
-+static int snfi_send_command(struct nfi *nfi, short cmd)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+
-+      if (cmd == -1)
-+              return 0;
-+
-+      if (nfi_spi->snfi_mode == SNFI_MAC_MODE) {
-+              snfi_write_mac(nfi_spi, (u8 *)&cmd, 1);
-+              nfi_spi->tx_count++;
-+              return 0;
-+      }
-+
-+      nfi_spi->cmd[nfi_spi->cur_cmd_idx++] = cmd;
-+      return 0;
-+}
-+
-+static int snfi_send_address(struct nfi *nfi, int col, int row,
-+                           int col_cycle,
-+                           int row_cycle)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      u32 addr, cycle, temp;
-+
-+      nb->col = col;
-+      nb->row = row;
-+
-+      if (nfi_spi->snfi_mode == SNFI_MAC_MODE) {
-+              addr = row;
-+              cycle = row_cycle;
-+
-+              if (!row_cycle) {
-+                      addr = col;
-+                      cycle = col_cycle;
-+              }
-+
-+              temp = nandx_cpu_to_be32(addr) >> ((4 - cycle) << 3);
-+              snfi_write_mac(nfi_spi, (u8 *)&temp, cycle);
-+              nfi_spi->tx_count += cycle;
-+      }  else {
-+              nfi_spi->row_addr[nfi_spi->cur_addr_idx++] = row;
-+              nfi_spi->col_addr[nfi_spi->cur_addr_idx++] = col;
-+      }
-+
-+      return 0;
-+}
-+
-+static int snfi_trigger(struct nfi *nfi)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+
-+      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
-+      writel(0, regs + SNF_MAC_INL);
-+
-+      nfi_spi->tx_count = 0;
-+      nfi_spi->cur_cmd_idx = 0;
-+      nfi_spi->cur_addr_idx = 0;
-+
-+      return snfi_mac_op(nb);
-+}
-+
-+static int snfi_select_chip(struct nfi *nfi, int cs)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+
-+      if (cs == 0) {
-+              val &= ~SF2CS_SEL;
-+              val &= ~SF2CS_EN;
-+      } else if (cs == 1) {
-+              val |= SF2CS_SEL;
-+              val |= SF2CS_EN;
-+      } else {
-+              return -EIO;
-+      }
-+
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      return 0;
-+}
-+
-+static int snfi_set_delay(struct nfi_base *nb, u8 delay_mode)
-+{
-+      void *regs = nb->res.nfi_regs;
-+      struct nfi_spi_delay *delay;
-+      u32 val;
-+
-+      if (delay_mode < 0 || delay_mode > SPI_NAND_MAX_DELAY)
-+              return -EINVAL;
-+
-+      delay = &spi_delay[delay_mode];
-+
-+      val = delay->tIO_OUT_DLY[0] | delay->tIO_OUT_DLY[1] << 8 |
-+            delay->tIO_OUT_DLY[2] << 16 |
-+            delay->tIO_OUT_DLY[3] << 24;
-+      writel(val, regs + SNF_DLY_CTL1);
-+
-+      val = delay->tIO_IN_DLY[0] | (delay->tIO_IN_DLY[1] << 8) |
-+            delay->tIO_IN_DLY[2] << 16 |
-+            delay->tIO_IN_DLY[3] << 24;
-+      writel(val, regs + SNF_DLY_CTL2);
-+
-+      val = delay->tCLK_SAM_DLY | delay->tCLK_OUT_DLY << 8 |
-+            delay->tCS_DLY << 16 |
-+            delay->tWR_EN_DLY << 24;
-+      writel(val, regs + SNF_DLY_CTL3);
-+
-+      writel(delay->tCS_DLY, regs + SNF_DLY_CTL4);
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+      val |= (delay->tREAD_LATCH_LATENCY) <<
-+             LATCH_LAT_SHIFT;
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      return 0;
-+}
-+
-+static int snfi_set_timing(struct nfi *nfi, void *timing, int type)
-+{
-+      /* Nothing need to do. */
-+      return 0;
-+}
-+
-+static int snfi_wait_ready(struct nfi *nfi, int type, u32 timeout)
-+{
-+      /* Nothing need to do. */
-+      return 0;
-+}
-+
-+static int snfi_ctrl(struct nfi *nfi, int cmd, void *args)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      int ret = 0;
-+
-+      if (!args)
-+              return -EINVAL;
-+
-+      switch (cmd) {
-+      case NFI_CTRL_DMA:
-+              nb->dma_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_NFI_IRQ:
-+              nb->nfi_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_IRQ:
-+              nb->ecc_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_PAGE_IRQ:
-+              nb->page_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC:
-+              nb->ecc_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_BAD_MARK_SWAP:
-+              nb->bad_mark_swap_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_CLOCK:
-+              nb->ecc_clk_en = *(bool *)args;
-+              break;
-+
-+      case SNFI_CTRL_OP_MODE:
-+              nfi_spi->snfi_mode = *(u8 *)args;
-+              break;
-+
-+      case SNFI_CTRL_RX_MODE:
-+              nfi_spi->read_cache_mode = *(u8 *)args;
-+              break;
-+
-+      case SNFI_CTRL_TX_MODE:
-+              nfi_spi->write_cache_mode = *(u8 *)args;
-+              break;
-+
-+      case SNFI_CTRL_DELAY_MODE:
-+              ret = snfi_set_delay(nb, *(u8 *)args);
-+              break;
-+
-+      default:
-+              pr_info("operation not support.\n");
-+              ret = -EOPNOTSUPP;
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+static int snfi_read_bytes(struct nfi *nfi, u8 *data, int count)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+      int ret;
-+
-+      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
-+      writel(count, regs + SNF_MAC_INL);
-+
-+      ret = snfi_mac_op(nb);
-+      if (ret)
-+              return ret;
-+
-+      snfi_read_mac(nfi_spi, data, count);
-+
-+      nfi_spi->tx_count = 0;
-+
-+      return 0;
-+}
-+
-+static int snfi_write_bytes(struct nfi *nfi, u8 *data, int count)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+
-+      snfi_write_mac(nfi_spi, data, count);
-+      nfi_spi->tx_count += count;
-+
-+      writel(0, regs + SNF_MAC_INL);
-+      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
-+
-+      nfi_spi->tx_count = 0;
-+
-+      return snfi_mac_op(nb);
-+}
-+
-+static int snfi_reset(struct nfi *nfi)
-+{
-+      struct nfi_base *nb = nfi_to_base(nfi);
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+      int ret;
-+
-+      ret = nfi_spi->parent->nfi.reset(nfi);
-+      if (ret)
-+              return ret;
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+      val |= SW_RST;
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      ret = readx_poll_timeout_atomic(readw, regs + SNF_STA_CTL1, val,
-+                                      !(val & SPI_STATE), 50,
-+                                      NFI_TIMEOUT);
-+      if (ret) {
-+              pr_info("spi state active in reset [0x%x] = 0x%x\n",
-+                      SNF_STA_CTL1, val);
-+              return ret;
-+      }
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+      val &= ~SW_RST;
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      return 0;
-+}
-+
-+static int snfi_config_for_write(struct nfi_base *nb, int count)
-+{
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      nb->set_op_mode(regs, CNFG_CUSTOM_MODE);
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+
-+      if (nfi_spi->write_cache_mode == SNFI_TX_114)
-+              val |= PG_LOAD_X4_EN;
-+
-+      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
-+              val |= PG_LOAD_CUSTOM_EN;
-+
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size);
-+      writel(val << PG_LOAD_SHIFT, regs + SNF_MISC_CTL2);
-+
-+      val = readl(regs + SNF_PG_CTL1);
-+
-+      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
-+              val |= nfi_spi->cmd[0] << PG_LOAD_CMD_SHIFT;
-+      else {
-+              val |= nfi_spi->cmd[0] | nfi_spi->cmd[1] << PG_LOAD_CMD_SHIFT |
-+                     nfi_spi->cmd[2] << PG_EXE_CMD_SHIFT;
-+
-+              writel(nfi_spi->row_addr[1], regs + SNF_PG_CTL3);
-+              writel(nfi_spi->cmd[3] << GF_CMD_SHIFT | nfi_spi->col_addr[2] <<
-+                     GF_ADDR_SHIFT, regs + SNF_GF_CTL1);
-+      }
-+
-+      writel(val, regs + SNF_PG_CTL1);
-+      writel(nfi_spi->col_addr[1], regs + SNF_PG_CTL2);
-+
-+      writel(NFI_CMD_DUMMY_WR, regs + NFI_CMD);
-+
-+      return 0;
-+}
-+
-+static int snfi_config_for_read(struct nfi_base *nb, int count)
-+{
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+      int ret = 0;
-+
-+      nb->set_op_mode(regs, CNFG_CUSTOM_MODE);
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+      val &= ~DARA_READ_MODE_MASK;
-+
-+      switch (nfi_spi->read_cache_mode) {
-+
-+      case SNFI_RX_111:
-+              break;
-+
-+      case SNFI_RX_112:
-+              val |= X2_DATA_MODE << READ_MODE_SHIFT;
-+              break;
-+
-+      case SNFI_RX_114:
-+              val |= X4_DATA_MODE << READ_MODE_SHIFT;
-+              break;
-+
-+      case SNFI_RX_122:
-+              val |= DUAL_IO_MODE << READ_MODE_SHIFT;
-+              break;
-+
-+      case SNFI_RX_144:
-+              val |= QUAD_IO_MODE << READ_MODE_SHIFT;
-+              break;
-+
-+      default:
-+              pr_info("Not support this read operarion: %d!\n",
-+                     nfi_spi->read_cache_mode);
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
-+              val |= DATARD_CUSTOM_EN;
-+
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size);
-+      writel(val, regs + SNF_MISC_CTL2);
-+
-+      val = readl(regs + SNF_RD_CTL2);
-+
-+      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) {
-+              val |= nfi_spi->cmd[0];
-+              writel(nfi_spi->col_addr[1], regs + SNF_RD_CTL3);
-+      } else {
-+              val |= nfi_spi->cmd[2];
-+              writel(nfi_spi->cmd[0] << PAGE_READ_CMD_SHIFT |
-+                     nfi_spi->row_addr[0], regs + SNF_RD_CTL1);
-+              writel(nfi_spi->cmd[1] << GF_CMD_SHIFT |
-+                     nfi_spi->col_addr[1] << GF_ADDR_SHIFT,
-+                     regs + SNF_GF_CTL1);
-+              writel(nfi_spi->col_addr[2], regs + SNF_RD_CTL3);
-+      }
-+
-+      writel(val, regs + SNF_RD_CTL2);
-+
-+      writel(NFI_CMD_DUMMY_RD, regs + NFI_CMD);
-+
-+      return ret;
-+}
-+
-+static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                        int sectors)
-+{
-+      u32 *data32 = (u32 *)data;
-+      u32 *fdm32 = (u32 *)fdm;
-+      u32 i, count = 0;
-+
-+      for (i = 0; i < nb->format.page_size >> 2; i++) {
-+              if (data32[i] != 0xffff) {
-+                      count += zero_popcount(data32[i]);
-+                      if (count > 10) {
-+                              pr_info("%s %d %d count:%d\n",
-+                                      __func__, __LINE__, i, count);
-+                              return false;
-+                      }
-+              }
-+      }
-+
-+      if (fdm) {
-+              for (i = 0; i < (nb->nfi.fdm_size * sectors >> 2); i++)
-+              if (fdm32[i] != 0xffff) {
-+                      count += zero_popcount(fdm32[i]);
-+                      if (count > 10) {
-+                              pr_info("%s %d %d count:%d\n",
-+                                      __func__, __LINE__, i, count);
-+                              return false;
-+                      }
-+              }
-+      }
-+
-+      return true;
-+}
-+
-+static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data,
-+                    u8 *fdm,
-+                    bool read)
-+{
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      int ret;
-+
-+      ret = nfi_spi->parent->rw_prepare(nb, sectors, data, fdm, read);
-+      if (ret)
-+              return ret;
-+
-+      if (read)
-+              ret = snfi_config_for_read(nb, sectors);
-+      else
-+              ret = snfi_config_for_write(nb, sectors);
-+
-+      return ret;
-+}
-+
-+static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm,
-+                      bool read)
-+{
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+      void *regs = nb->res.nfi_regs;
-+      u32 val;
-+
-+      nfi_spi->parent->rw_complete(nb, data, fdm, read);
-+
-+      val = readl(regs + SNF_MISC_CTL);
-+
-+      if (read)
-+              val &= ~DATARD_CUSTOM_EN;
-+      else
-+              val &= ~PG_LOAD_CUSTOM_EN;
-+
-+      writel(val, regs + SNF_MISC_CTL);
-+
-+      nfi_spi->tx_count = 0;
-+      nfi_spi->cur_cmd_idx = 0;
-+      nfi_spi->cur_addr_idx = 0;
-+}
-+
-+static void set_nfi_base_funcs(struct nfi_base *nb)
-+{
-+      nb->nfi.reset = snfi_reset;
-+      nb->nfi.set_timing = snfi_set_timing;
-+      nb->nfi.wait_ready = snfi_wait_ready;
-+
-+      nb->nfi.send_cmd = snfi_send_command;
-+      nb->nfi.send_addr = snfi_send_address;
-+      nb->nfi.trigger = snfi_trigger;
-+      nb->nfi.nfi_ctrl = snfi_ctrl;
-+      nb->nfi.select_chip = snfi_select_chip;
-+
-+      nb->nfi.read_bytes = snfi_read_bytes;
-+      nb->nfi.write_bytes = snfi_write_bytes;
-+
-+      nb->rw_prepare = rw_prepare;
-+      nb->rw_complete = rw_complete;
-+      nb->is_page_empty = is_page_empty;
-+
-+}
-+
-+struct nfi *nfi_extend_init(struct nfi_base *nb)
-+{
-+      struct nfi_spi *nfi_spi;
-+
-+      nfi_spi = mem_alloc(1, sizeof(struct nfi_spi));
-+      if (!nfi_spi) {
-+              pr_info("snfi alloc memory fail @%s.\n", __func__);
-+              return NULL;
-+      }
-+
-+      memcpy(&nfi_spi->base, nb, sizeof(struct nfi_base));
-+      nfi_spi->parent = nb;
-+
-+      nfi_spi->read_cache_mode = SNFI_RX_114;
-+      nfi_spi->write_cache_mode = SNFI_TX_114;
-+
-+      set_nfi_base_funcs(&nfi_spi->base);
-+
-+      /* Change nfi to spi mode */
-+      writel(SPI_MODE, nb->res.nfi_regs + SNF_SNF_CNFG);
-+
-+      return &(nfi_spi->base.nfi);
-+}
-+
-+void nfi_extend_exit(struct nfi_base *nb)
-+{
-+      struct nfi_spi *nfi_spi = base_to_snfi(nb);
-+
-+      mem_free(nfi_spi->parent);
-+      mem_free(nfi_spi);
-+}
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_spi.h
-@@ -0,0 +1,44 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFI_SPI_H__
-+#define __NFI_SPI_H__
-+
-+#define SPI_NAND_MAX_DELAY      6
-+#define SPI_NAND_MAX_OP         4
-+
-+/*TODO - add comments */
-+struct nfi_spi_delay {
-+      u8 tCLK_SAM_DLY;
-+      u8 tCLK_OUT_DLY;
-+      u8 tCS_DLY;
-+      u8 tWR_EN_DLY;
-+      u8 tIO_IN_DLY[4];
-+      u8 tIO_OUT_DLY[4];
-+      u8 tREAD_LATCH_LATENCY;
-+};
-+
-+/* SPI Nand structure */
-+struct nfi_spi {
-+      struct nfi_base base;
-+      struct nfi_base *parent;
-+
-+      u8 snfi_mode;
-+      u8 tx_count;
-+
-+      u8 cmd[SPI_NAND_MAX_OP];
-+      u8 cur_cmd_idx;
-+
-+      u32 row_addr[SPI_NAND_MAX_OP];
-+      u32 col_addr[SPI_NAND_MAX_OP];
-+      u8 cur_addr_idx;
-+
-+      u8 read_cache_mode;
-+      u8 write_cache_mode;
-+};
-+
-+#endif /* __NFI_SPI_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
-@@ -0,0 +1,64 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFI_SPI_REGS_H__
-+#define __NFI_SPI_REGS_H__
-+
-+#define SNF_MAC_CTL             0x500
-+#define         WIP                     BIT(0)
-+#define         WIP_READY               BIT(1)
-+#define         SF_TRIG                 BIT(2)
-+#define         SF_MAC_EN               BIT(3)
-+#define         MAC_XIO_SEL             BIT(4)
-+#define SNF_MAC_OUTL            0x504
-+#define SNF_MAC_INL             0x508
-+#define SNF_RD_CTL1             0x50c
-+#define         PAGE_READ_CMD_SHIFT     24
-+#define SNF_RD_CTL2             0x510
-+#define SNF_RD_CTL3             0x514
-+#define SNF_GF_CTL1             0x518
-+#define         GF_ADDR_SHIFT           16
-+#define         GF_CMD_SHIFT            24
-+#define SNF_GF_CTL3             0x520
-+#define SNF_PG_CTL1             0x524
-+#define         PG_EXE_CMD_SHIFT        16
-+#define         PG_LOAD_CMD_SHIFT       8
-+#define SNF_PG_CTL2             0x528
-+#define SNF_PG_CTL3             0x52c
-+#define SNF_ER_CTL              0x530
-+#define SNF_ER_CTL2             0x534
-+#define SNF_MISC_CTL            0x538
-+#define         SW_RST                  BIT(28)
-+#define         PG_LOAD_X4_EN           BIT(20)
-+#define         X2_DATA_MODE            1
-+#define         X4_DATA_MODE            2
-+#define         DUAL_IO_MODE            5
-+#define         QUAD_IO_MODE            6
-+#define         READ_MODE_SHIFT         16
-+#define         LATCH_LAT_SHIFT         8
-+#define         LATCH_LAT_MASK          GENMASK(9, 8)
-+#define         DARA_READ_MODE_MASK     GENMASK(18, 16)
-+#define         SF2CS_SEL               BIT(13)
-+#define         SF2CS_EN                BIT(12)
-+#define         PG_LOAD_CUSTOM_EN       BIT(7)
-+#define         DATARD_CUSTOM_EN        BIT(6)
-+#define SNF_MISC_CTL2           0x53c
-+#define         PG_LOAD_SHIFT           16
-+#define SNF_DLY_CTL1            0x540
-+#define SNF_DLY_CTL2            0x544
-+#define SNF_DLY_CTL3            0x548
-+#define SNF_DLY_CTL4            0x54c
-+#define SNF_STA_CTL1            0x550
-+#define         SPI_STATE               GENMASK(3, 0)
-+#define SNF_STA_CTL2            0x554
-+#define SNF_STA_CTL3            0x558
-+#define SNF_SNF_CNFG            0x55c
-+#define         SPI_MODE                BIT(0)
-+#define SNF_DEBUG_SEL           0x560
-+#define SPI_GPRAM_ADDR          0x800
-+
-+#endif /* __NFI_SPI_REGS_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfiecc.c
-@@ -0,0 +1,510 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "nfiecc_regs.h"
-+#include "nfiecc.h"
-+
-+#define NFIECC_IDLE_REG(op) \
-+      ((op) == ECC_ENCODE ? NFIECC_ENCIDLE : NFIECC_DECIDLE)
-+#define         IDLE_MASK       1
-+#define NFIECC_CTL_REG(op) \
-+      ((op) == ECC_ENCODE ? NFIECC_ENCCON : NFIECC_DECCON)
-+#define NFIECC_IRQ_REG(op) \
-+      ((op) == ECC_ENCODE ? NFIECC_ENCIRQEN : NFIECC_DECIRQEN)
-+#define NFIECC_ADDR(op) \
-+      ((op) == ECC_ENCODE ? NFIECC_ENCDIADDR : NFIECC_DECDIADDR)
-+
-+#define ECC_TIMEOUT     500000
-+
-+/* ecc strength that each IP supports */
-+static const int ecc_strength_mt7622[] = {
-+      4, 6, 8, 10, 12, 14, 16
-+};
-+
-+static int nfiecc_irq_handler(void *data)
-+{
-+      struct nfiecc *ecc = data;
-+      void *regs = ecc->res.regs;
-+      u32 status;
-+
-+      status = readl(regs + NFIECC_DECIRQSTA) & DEC_IRQSTA_GEN;
-+      if (status) {
-+              status = readl(regs + NFIECC_DECDONE);
-+              if (!(status & ecc->config.sectors))
-+                      return NAND_IRQ_NONE;
-+
-+              /*
-+               * Clear decode IRQ status once again to ensure that
-+               * there will be no extra IRQ.
-+               */
-+              readl(regs + NFIECC_DECIRQSTA);
-+              ecc->config.sectors = 0;
-+              nandx_event_complete(ecc->done);
-+      } else {
-+              status = readl(regs + NFIECC_ENCIRQSTA) & ENC_IRQSTA_GEN;
-+              if (!status)
-+                      return NAND_IRQ_NONE;
-+
-+              nandx_event_complete(ecc->done);
-+      }
-+
-+      return NAND_IRQ_HANDLED;
-+}
-+
-+static inline int nfiecc_wait_idle(struct nfiecc *ecc)
-+{
-+      int op = ecc->config.op;
-+      int ret, val;
-+
-+      ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_IDLE_REG(op),
-+                                      val, val & IDLE_MASK,
-+                                      10, ECC_TIMEOUT);
-+      if (ret)
-+              pr_info("%s not idle\n",
-+                      op == ECC_ENCODE ? "encoder" : "decoder");
-+
-+      return ret;
-+}
-+
-+static int nfiecc_wait_encode_done(struct nfiecc *ecc)
-+{
-+      int ret, val;
-+
-+      if (ecc->ecc_irq_en) {
-+              /* poll one time to avoid missing irq event */
-+              ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA,
-+                                              val, val & ENC_FSM_IDLE, 1, 1);
-+              if (!ret)
-+                      return 0;
-+
-+              /* irq done, if not, we can go on to poll status for a while */
-+              ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT);
-+              if (ret)
-+                      return 0;
-+      }
-+
-+      ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA,
-+                                      val, val & ENC_FSM_IDLE,
-+                                      10, ECC_TIMEOUT);
-+      if (ret)
-+              pr_info("encode timeout\n");
-+
-+      return ret;
-+
-+}
-+
-+static int nfiecc_wait_decode_done(struct nfiecc *ecc)
-+{
-+      u32 secbit = BIT(ecc->config.sectors - 1);
-+      void *regs = ecc->res.regs;
-+      int ret, val;
-+
-+      if (ecc->ecc_irq_en) {
-+              ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE,
-+                                              val, val & secbit, 1, 1);
-+              if (!ret)
-+                      return 0;
-+
-+              ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT);
-+              if (ret)
-+                      return 0;
-+      }
-+
-+      ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE,
-+                                      val, val & secbit,
-+                                      10, ECC_TIMEOUT);
-+      if (ret) {
-+              pr_info("decode timeout\n");
-+              return ret;
-+      }
-+
-+      /* decode done does not stands for ecc all work done.
-+       * we need check syn, bma, chien, autoc all idle.
-+       * just check it when ECC_DECCNFG[13:12] is 3,
-+       * which means auto correct.
-+       */
-+      ret = readl_poll_timeout_atomic(regs + NFIECC_DECFSM,
-+                                      val, (val & FSM_MASK) == FSM_IDLE,
-+                                      10, ECC_TIMEOUT);
-+      if (ret)
-+              pr_info("decode fsm(0x%x) is not idle\n",
-+                     readl(regs + NFIECC_DECFSM));
-+
-+      return ret;
-+}
-+
-+static int nfiecc_wait_done(struct nfiecc *ecc)
-+{
-+      if (ecc->config.op == ECC_ENCODE)
-+              return nfiecc_wait_encode_done(ecc);
-+
-+      return nfiecc_wait_decode_done(ecc);
-+}
-+
-+static void nfiecc_encode_config(struct nfiecc *ecc, u32 ecc_idx)
-+{
-+      struct nfiecc_config *config = &ecc->config;
-+      u32 val;
-+
-+      val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift);
-+
-+      if (config->mode == ECC_DMA_MODE)
-+              val |= ENC_BURST_EN;
-+
-+      val |= (config->len << 3) << ENCCNFG_MS_SHIFT;
-+      writel(val, ecc->res.regs + NFIECC_ENCCNFG);
-+}
-+
-+static void nfiecc_decode_config(struct nfiecc *ecc, u32 ecc_idx)
-+{
-+      struct nfiecc_config *config = &ecc->config;
-+      u32 dec_sz = (config->len << 3) +
-+                   config->strength * ecc->caps->parity_bits;
-+      u32 val;
-+
-+      val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift);
-+
-+      if (config->mode == ECC_DMA_MODE)
-+              val |= DEC_BURST_EN;
-+
-+      val |= (dec_sz << DECCNFG_MS_SHIFT) |
-+             (config->deccon << DEC_CON_SHIFT);
-+      val |= DEC_EMPTY_EN;
-+      writel(val, ecc->res.regs + NFIECC_DECCNFG);
-+}
-+
-+static void nfiecc_config(struct nfiecc *ecc)
-+{
-+      u32 idx;
-+
-+      for (idx = 0; idx < ecc->caps->ecc_strength_num; idx++) {
-+              if (ecc->config.strength == ecc->caps->ecc_strength[idx])
-+                      break;
-+      }
-+
-+      if (ecc->config.op == ECC_ENCODE)
-+              nfiecc_encode_config(ecc, idx);
-+      else
-+              nfiecc_decode_config(ecc, idx);
-+}
-+
-+static int nfiecc_enable(struct nfiecc *ecc)
-+{
-+      enum nfiecc_operation op = ecc->config.op;
-+      void *regs = ecc->res.regs;
-+
-+      nfiecc_config(ecc);
-+
-+      writel(ECC_OP_EN, regs + NFIECC_CTL_REG(op));
-+
-+      if (ecc->ecc_irq_en) {
-+              writel(ECC_IRQEN, regs + NFIECC_IRQ_REG(op));
-+
-+              if (ecc->page_irq_en)
-+                      writel(ECC_IRQEN | ECC_PG_IRQ_SEL,
-+                             regs + NFIECC_IRQ_REG(op));
-+
-+              nandx_event_init(ecc->done);
-+      }
-+
-+      return 0;
-+}
-+
-+static int nfiecc_disable(struct nfiecc *ecc)
-+{
-+      enum nfiecc_operation op = ecc->config.op;
-+      void *regs = ecc->res.regs;
-+
-+      nfiecc_wait_idle(ecc);
-+
-+      writel(0, regs + NFIECC_IRQ_REG(op));
-+      writel(~ECC_OP_EN, regs + NFIECC_CTL_REG(op));
-+
-+      return 0;
-+}
-+
-+static int nfiecc_correct_data(struct nfiecc *ecc,
-+                             struct nfiecc_status *status,
-+                             u8 *data, u32 sector)
-+{
-+      u32 err, offset, i;
-+      u32 loc, byteloc, bitloc;
-+
-+      status->corrected = 0;
-+      status->failed = 0;
-+
-+      offset = (sector >> 2);
-+      err = readl(ecc->res.regs + NFIECC_DECENUM(offset));
-+      err >>= (sector % 4) * 8;
-+      err &= ecc->caps->err_mask;
-+
-+      if (err == ecc->caps->err_mask) {
-+              status->failed++;
-+              return -ENANDREAD;
-+      }
-+
-+      status->corrected += err;
-+      status->bitflips = max_t(u32, status->bitflips, err);
-+
-+      for (i = 0; i < err; i++) {
-+              loc = readl(ecc->res.regs + NFIECC_DECEL(i >> 1));
-+              loc >>= ((i & 0x1) << 4);
-+              byteloc = loc >> 3;
-+              bitloc = loc & 0x7;
-+              data[byteloc] ^= (1 << bitloc);
-+      }
-+
-+      return 0;
-+}
-+
-+static int nfiecc_fill_data(struct nfiecc *ecc, u8 *data)
-+{
-+      struct nfiecc_config *config = &ecc->config;
-+      void *regs = ecc->res.regs;
-+      int size, ret, i;
-+      u32 val;
-+
-+      if (config->mode == ECC_DMA_MODE) {
-+              if ((unsigned long)config->dma_addr & 0x3)
-+                      pr_info("encode address is not 4B aligned: 0x%x\n",
-+                             (u32)(unsigned long)config->dma_addr);
-+
-+              writel((unsigned long)config->dma_addr,
-+                     regs + NFIECC_ADDR(config->op));
-+      } else if (config->mode == ECC_PIO_MODE) {
-+              if (config->op == ECC_ENCODE) {
-+                      size = (config->len + 3) >> 2;
-+              } else {
-+                      size = config->strength * ecc->caps->parity_bits;
-+                      size = (size + 7) >> 3;
-+                      size += config->len;
-+                      size >>= 2;
-+              }
-+
-+              for (i = 0; i < size; i++) {
-+                      ret = readl_poll_timeout_atomic(regs + NFIECC_PIO_DIRDY,
-+                                                      val, val & PIO_DI_RDY,
-+                                                      10, ECC_TIMEOUT);
-+                      if (ret)
-+                              return ret;
-+
-+                      writel(*((u32 *)data + i), regs + NFIECC_PIO_DI);
-+              }
-+      }
-+
-+      return 0;
-+}
-+
-+static int nfiecc_encode(struct nfiecc *ecc, u8 *data)
-+{
-+      struct nfiecc_config *config = &ecc->config;
-+      u32 len, i, val = 0;
-+      u8 *p;
-+      int ret;
-+
-+      /* Under NFI mode, nothing need to do */
-+      if (config->mode == ECC_NFI_MODE)
-+              return 0;
-+
-+      ret = nfiecc_fill_data(ecc, data);
-+      if (ret)
-+              return ret;
-+
-+      ret = nfiecc_wait_encode_done(ecc);
-+      if (ret)
-+              return ret;
-+
-+      ret = nfiecc_wait_idle(ecc);
-+      if (ret)
-+              return ret;
-+
-+      /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
-+      len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
-+      p = data + config->len;
-+
-+      /* Write the parity bytes generated by the ECC back to the OOB region */
-+      for (i = 0; i < len; i++) {
-+              if ((i % 4) == 0)
-+                      val = readl(ecc->res.regs + NFIECC_ENCPAR(i / 4));
-+
-+              p[i] = (val >> ((i % 4) * 8)) & 0xff;
-+      }
-+
-+      return 0;
-+}
-+
-+static int nfiecc_decode(struct nfiecc *ecc, u8 *data)
-+{
-+      int ret;
-+
-+      /* Under NFI mode, nothing need to do */
-+      if (ecc->config.mode == ECC_NFI_MODE)
-+              return 0;
-+
-+      ret = nfiecc_fill_data(ecc, data);
-+      if (ret)
-+              return ret;
-+
-+      return nfiecc_wait_decode_done(ecc);
-+}
-+
-+static int nfiecc_decode_status(struct nfiecc *ecc, u32 start_sector,
-+                              u32 sectors)
-+{
-+      void *regs = ecc->res.regs;
-+      u32 i, val = 0, err;
-+      u32 bitflips = 0;
-+
-+      for (i = start_sector; i < start_sector + sectors; i++) {
-+              if ((i % 4) == 0)
-+                      val = readl(regs + NFIECC_DECENUM(i / 4));
-+
-+              err = val >> ((i % 4) * 5);
-+              err &= ecc->caps->err_mask;
-+
-+              if (err == ecc->caps->err_mask)
-+                      pr_err("sector %d is uncorrect\n", i);
-+
-+              bitflips = max_t(u32, bitflips, err);
-+      }
-+
-+      if (bitflips == ecc->caps->err_mask)
-+              return -ENANDREAD;
-+
-+      if (bitflips)
-+              pr_info("bitflips %d is corrected\n", bitflips);
-+
-+      return bitflips;
-+}
-+
-+static int nfiecc_adjust_strength(struct nfiecc *ecc, int strength)
-+{
-+      struct nfiecc_caps *caps = ecc->caps;
-+      int i, count = caps->ecc_strength_num;
-+
-+      if (strength >= caps->ecc_strength[count - 1])
-+              return caps->ecc_strength[count - 1];
-+
-+      if (strength < caps->ecc_strength[0])
-+              return -EINVAL;
-+
-+      for (i = 1; i < count; i++) {
-+              if (strength < caps->ecc_strength[i])
-+                      return caps->ecc_strength[i - 1];
-+      }
-+
-+      return -EINVAL;
-+}
-+
-+static int nfiecc_ctrl(struct nfiecc *ecc, int cmd, void *args)
-+{
-+      int ret = 0;
-+
-+      switch (cmd) {
-+      case NFI_CTRL_ECC_IRQ:
-+              ecc->ecc_irq_en = *(bool *)args;
-+              break;
-+
-+      case NFI_CTRL_ECC_PAGE_IRQ:
-+              ecc->page_irq_en = *(bool *)args;
-+              break;
-+
-+      default:
-+              pr_info("invalid arguments.\n");
-+              ret = -EINVAL;
-+              break;
-+      }
-+
-+      return ret;
-+}
-+
-+static int nfiecc_hw_init(struct nfiecc *ecc)
-+{
-+      int ret;
-+
-+      ret = nfiecc_wait_idle(ecc);
-+      if (ret)
-+              return ret;
-+
-+      writel(~ECC_OP_EN, ecc->res.regs + NFIECC_ENCCON);
-+
-+      ret = nfiecc_wait_idle(ecc);
-+      if (ret)
-+              return ret;
-+
-+      writel(~ECC_OP_EN, ecc->res.regs + NFIECC_DECCON);
-+
-+      return 0;
-+}
-+
-+static struct nfiecc_caps nfiecc_caps_mt7622 = {
-+      .err_mask = 0x1f,
-+      .ecc_mode_shift = 4,
-+      .parity_bits = 13,
-+      .ecc_strength = ecc_strength_mt7622,
-+      .ecc_strength_num = 7,
-+};
-+
-+static struct nfiecc_caps *nfiecc_get_match_data(enum mtk_ic_version ic)
-+{
-+      /* NOTE: add other IC's data */
-+      return &nfiecc_caps_mt7622;
-+}
-+
-+struct nfiecc *nfiecc_init(struct nfiecc_resource *res)
-+{
-+      struct nfiecc *ecc;
-+      int ret;
-+
-+      ecc = mem_alloc(1, sizeof(struct nfiecc));
-+      if (!ecc)
-+              return NULL;
-+
-+      ecc->res = *res;
-+
-+      ret = nandx_irq_register(res->dev, res->irq_id, nfiecc_irq_handler,
-+                               "mtk-ecc", ecc);
-+      if (ret) {
-+              pr_info("ecc irq register failed!\n");
-+              goto error;
-+      }
-+
-+      ecc->ecc_irq_en = false;
-+      ecc->page_irq_en = false;
-+      ecc->done = nandx_event_create();
-+      ecc->caps = nfiecc_get_match_data(res->ic_ver);
-+
-+      ecc->adjust_strength = nfiecc_adjust_strength;
-+      ecc->enable = nfiecc_enable;
-+      ecc->disable = nfiecc_disable;
-+      ecc->decode = nfiecc_decode;
-+      ecc->encode = nfiecc_encode;
-+      ecc->wait_done = nfiecc_wait_done;
-+      ecc->decode_status = nfiecc_decode_status;
-+      ecc->correct_data = nfiecc_correct_data;
-+      ecc->nfiecc_ctrl = nfiecc_ctrl;
-+
-+      ret = nfiecc_hw_init(ecc);
-+      if (ret)
-+              return NULL;
-+
-+      return ecc;
-+
-+error:
-+      mem_free(ecc);
-+
-+      return NULL;
-+}
-+
-+void nfiecc_exit(struct nfiecc *ecc)
-+{
-+      nandx_event_destroy(ecc->done);
-+      mem_free(ecc);
-+}
-+
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfiecc.h
-@@ -0,0 +1,90 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFIECC_H__
-+#define __NFIECC_H__
-+
-+enum nfiecc_mode {
-+      ECC_DMA_MODE,
-+      ECC_NFI_MODE,
-+      ECC_PIO_MODE
-+};
-+
-+enum nfiecc_operation {
-+      ECC_ENCODE,
-+      ECC_DECODE
-+};
-+
-+enum nfiecc_deccon {
-+      ECC_DEC_FER = 1,
-+      ECC_DEC_LOCATE = 2,
-+      ECC_DEC_CORRECT = 3
-+};
-+
-+struct nfiecc_resource {
-+      int ic_ver;
-+      void *dev;
-+      void *regs;
-+      int irq_id;
-+
-+};
-+
-+struct nfiecc_status {
-+      u32 corrected;
-+      u32 failed;
-+      u32 bitflips;
-+};
-+
-+struct nfiecc_caps {
-+      u32 err_mask;
-+      u32 ecc_mode_shift;
-+      u32 parity_bits;
-+      const int *ecc_strength;
-+      u32 ecc_strength_num;
-+};
-+
-+struct nfiecc_config {
-+      enum nfiecc_operation op;
-+      enum nfiecc_mode mode;
-+      enum nfiecc_deccon deccon;
-+
-+      void *dma_addr; /* DMA use only */
-+      u32 strength;
-+      u32 sectors;
-+      u32 len;
-+};
-+
-+struct nfiecc {
-+      struct nfiecc_resource res;
-+      struct nfiecc_config config;
-+      struct nfiecc_caps *caps;
-+
-+      bool ecc_irq_en;
-+      bool page_irq_en;
-+
-+      void *done;
-+
-+      int (*adjust_strength)(struct nfiecc *ecc, int strength);
-+      int (*enable)(struct nfiecc *ecc);
-+      int (*disable)(struct nfiecc *ecc);
-+
-+      int (*decode)(struct nfiecc *ecc, u8 *data);
-+      int (*encode)(struct nfiecc *ecc, u8 *data);
-+
-+      int (*decode_status)(struct nfiecc *ecc, u32 start_sector, u32 sectors);
-+      int (*correct_data)(struct nfiecc *ecc,
-+                          struct nfiecc_status *status,
-+                          u8 *data, u32 sector);
-+      int (*wait_done)(struct nfiecc *ecc);
-+
-+      int (*nfiecc_ctrl)(struct nfiecc *ecc, int cmd, void *args);
-+};
-+
-+struct nfiecc *nfiecc_init(struct nfiecc_resource *res);
-+void nfiecc_exit(struct nfiecc *ecc);
-+
-+#endif /* __NFIECC_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h
-@@ -0,0 +1,51 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NFIECC_REGS_H__
-+#define __NFIECC_REGS_H__
-+
-+#define NFIECC_ENCCON           0x000
-+/* NFIECC_DECCON has same bit define */
-+#define         ECC_OP_EN               BIT(0)
-+#define NFIECC_ENCCNFG          0x004
-+#define         ENCCNFG_MS_SHIFT        16
-+#define         ENC_BURST_EN            BIT(8)
-+#define NFIECC_ENCDIADDR        0x008
-+#define NFIECC_ENCIDLE          0x00c
-+#define NFIECC_ENCSTA           0x02c
-+#define         ENC_FSM_IDLE            1
-+#define NFIECC_ENCIRQEN         0x030
-+/* NFIECC_DECIRQEN has same bit define */
-+#define         ECC_IRQEN               BIT(0)
-+#define         ECC_PG_IRQ_SEL          BIT(1)
-+#define NFIECC_ENCIRQSTA        0x034
-+#define         ENC_IRQSTA_GEN          BIT(0)
-+#define NFIECC_PIO_DIRDY        0x080
-+#define         PIO_DI_RDY              BIT(0)
-+#define NFIECC_PIO_DI           0x084
-+#define NFIECC_DECCON           0x100
-+#define NFIECC_DECCNFG          0x104
-+#define         DEC_BURST_EN            BIT(8)
-+#define         DEC_EMPTY_EN            BIT(31)
-+#define         DEC_CON_SHIFT           12
-+#define         DECCNFG_MS_SHIFT        16
-+#define NFIECC_DECDIADDR        0x108
-+#define NFIECC_DECIDLE          0x10c
-+#define NFIECC_DECENUM(x)       (0x114 + (x) * 4)
-+#define NFIECC_DECDONE          0x11c
-+#define NFIECC_DECIRQEN         0x140
-+#define NFIECC_DECIRQSTA        0x144
-+#define         DEC_IRQSTA_GEN          BIT(0)
-+#define NFIECC_DECFSM           0x14c
-+#define         FSM_MASK                0x7f0f0f0f
-+#define         FSM_IDLE                0x01010101
-+#define NFIECC_BYPASS           0x20c
-+#define         NFIECC_BYPASS_EN        BIT(0)
-+#define NFIECC_ENCPAR(x)        (0x010 + (x) * 4)
-+#define NFIECC_DECEL(x)         (0x120 + (x) * 4)
-+
-+#endif /* __NFIECC_REGS_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/driver/Nandx.mk
-@@ -0,0 +1,18 @@
-+#
-+# Copyright (C) 2017 MediaTek Inc.
-+# Licensed under either
-+#     BSD Licence, (see NOTICE for more details)
-+#     GNU General Public License, version 2.0, (see NOTICE for more details)
-+#
-+
-+nandx-$(NANDX_SIMULATOR_SUPPORT) += simulator/driver.c
-+
-+nandx-$(NANDX_CTP_SUPPORT) += ctp/ts_nand.c
-+nandx-$(NANDX_CTP_SUPPORT) += ctp/nand_test.c
-+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nand_test.h
-+
-+nandx-$(NANDX_BBT_SUPPORT) += bbt/bbt.c
-+nandx-$(NANDX_BROM_SUPPORT) += brom/driver.c
-+nandx-$(NANDX_KERNEL_SUPPORT) += kernel/driver.c
-+nandx-$(NANDX_LK_SUPPORT) += lk/driver.c
-+nandx-$(NANDX_UBOOT_SUPPORT) += uboot/driver.c
---- /dev/null
-+++ b/drivers/mtd/nandx/driver/bbt/bbt.c
-@@ -0,0 +1,408 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include "nandx_util.h"
-+#include "nandx_core.h"
-+#include "bbt.h"
-+
-+/* Not support: multi-chip */
-+static u8 main_bbt_pattern[] = {'B', 'b', 't', '0' };
-+static u8 mirror_bbt_pattern[] = {'1', 't', 'b', 'B' };
-+
-+static struct bbt_manager g_bbt_manager = {
-+      {       {{main_bbt_pattern, 4}, 0, BBT_INVALID_ADDR},
-+              {{mirror_bbt_pattern, 4}, 0, BBT_INVALID_ADDR}
-+      },
-+      NAND_BBT_SCAN_MAXBLOCKS, NULL
-+};
-+
-+static inline void set_bbt_mark(u8 *bbt, int block, u8 mark)
-+{
-+      int index, offset;
-+
-+      index = GET_ENTRY(block);
-+      offset = GET_POSITION(block);
-+
-+      bbt[index] &= ~(BBT_ENTRY_MASK << offset);
-+      bbt[index] |= (mark & BBT_ENTRY_MASK) << offset;
-+      pr_info("%s %d block:%d, bbt[%d]:0x%x, offset:%d, mark:%d\n",
-+              __func__, __LINE__, block, index, bbt[index], offset, mark);
-+}
-+
-+static inline u8 get_bbt_mark(u8 *bbt, int block)
-+{
-+      int offset = GET_POSITION(block);
-+      int index = GET_ENTRY(block);
-+      u8 value = bbt[index];
-+
-+      return (value >> offset) & BBT_ENTRY_MASK;
-+}
-+
-+static void mark_nand_bad(struct nandx_info *nand, int block)
-+{
-+      u8 *buf;
-+
-+      buf = mem_alloc(1, nand->page_size + nand->oob_size);
-+      if (!buf) {
-+              pr_info("%s, %d, memory alloc fail, pagesize:%d, oobsize:%d\n",
-+                     __func__, __LINE__, nand->page_size, nand->oob_size);
-+              return;
-+      }
-+      memset(buf, 0, nand->page_size + nand->oob_size);
-+      nandx_erase(block * nand->block_size, nand->block_size);
-+      nandx_write(buf, buf + nand->page_size, block * nand->block_size,
-+                  nand->page_size);
-+      mem_free(buf);
-+}
-+
-+static inline bool is_bbt_data(u8 *buf, struct bbt_pattern *pattern)
-+{
-+      int i;
-+
-+      for (i = 0; i < pattern->len; i++) {
-+              if (buf[i] != pattern->data[i])
-+                      return false;
-+      }
-+
-+      return true;
-+}
-+
-+static u64 get_bbt_address(struct nandx_info *nand, u8 *bbt,
-+                         u64 mirror_addr,
-+                         int max_blocks)
-+{
-+      u64 addr, end_addr;
-+      u8 mark;
-+
-+      addr = nand->total_size;
-+      end_addr = nand->total_size - nand->block_size * max_blocks;
-+
-+      while (addr > end_addr) {
-+              addr -= nand->block_size;
-+              mark = get_bbt_mark(bbt, div_down(addr, nand->block_size));
-+
-+              if (mark == BBT_BLOCK_WORN || mark == BBT_BLOCK_FACTORY_BAD)
-+                      continue;
-+              if (addr != mirror_addr)
-+                      return addr;
-+      }
-+
-+      return BBT_INVALID_ADDR;
-+}
-+
-+static int read_bbt(struct bbt_desc *desc, u8 *bbt, u32 len)
-+{
-+      int ret;
-+
-+      ret = nandx_read(bbt, NULL, desc->bbt_addr + desc->pattern.len + 1,
-+                       len);
-+      if (ret < 0)
-+              pr_info("nand_bbt: error reading BBT page, ret:-%x\n", ret);
-+
-+      return ret;
-+}
-+
-+static void create_bbt(struct nandx_info *nand, u8 *bbt)
-+{
-+      u32 offset = 0, block = 0;
-+
-+      do {
-+              if (nandx_is_bad_block(offset)) {
-+                      pr_info("Create bbt at bad block:%d\n", block);
-+                      set_bbt_mark(bbt, block, BBT_BLOCK_FACTORY_BAD);
-+              }
-+              block++;
-+              offset += nand->block_size;
-+      } while (offset < nand->total_size);
-+}
-+
-+static int search_bbt(struct nandx_info *nand, struct bbt_desc *desc,
-+                    int max_blocks)
-+{
-+      u64 addr, end_addr;
-+      u8 *buf;
-+      int ret;
-+
-+      buf = mem_alloc(1, nand->page_size);
-+      if (!buf) {
-+              pr_info("%s, %d, mem alloc fail!!! len:%d\n",
-+                     __func__, __LINE__, nand->page_size);
-+              return -ENOMEM;
-+      }
-+
-+      addr = nand->total_size;
-+      end_addr = nand->total_size - max_blocks * nand->block_size;
-+      while (addr > end_addr) {
-+              addr -= nand->block_size;
-+
-+              nandx_read(buf, NULL, addr, nand->page_size);
-+
-+              if (is_bbt_data(buf, &desc->pattern)) {
-+                      desc->bbt_addr = addr;
-+                      desc->version = buf[desc->pattern.len];
-+                      pr_info("BBT is found at addr 0x%llx, version %d\n",
-+                              desc->bbt_addr, desc->version);
-+                      ret = 0;
-+                      break;
-+              }
-+              ret = -EFAULT;
-+      }
-+
-+      mem_free(buf);
-+      return ret;
-+}
-+
-+static int save_bbt(struct nandx_info *nand, struct bbt_desc *desc,
-+                  u8 *bbt)
-+{
-+      u32 page_size_mask, total_block;
-+      int write_len;
-+      u8 *buf;
-+      int ret;
-+
-+      ret = nandx_erase(desc->bbt_addr, nand->block_size);
-+      if (ret) {
-+              pr_info("erase addr 0x%llx fail !!!, ret %d\n",
-+                      desc->bbt_addr, ret);
-+              return ret;
-+      }
-+
-+      total_block = div_down(nand->total_size, nand->block_size);
-+      write_len = GET_BBT_LENGTH(total_block) + desc->pattern.len + 1;
-+      page_size_mask = nand->page_size - 1;
-+      write_len = (write_len + page_size_mask) & (~page_size_mask);
-+
-+      buf = (u8 *)mem_alloc(1, write_len);
-+      if (!buf) {
-+              pr_info("%s, %d, mem alloc fail!!! len:%d\n",
-+                     __func__, __LINE__, write_len);
-+              return -ENOMEM;
-+      }
-+      memset(buf, 0xFF, write_len);
-+
-+      memcpy(buf, desc->pattern.data, desc->pattern.len);
-+      buf[desc->pattern.len] = desc->version;
-+
-+      memcpy(buf + desc->pattern.len + 1, bbt, GET_BBT_LENGTH(total_block));
-+
-+      ret = nandx_write(buf, NULL, desc->bbt_addr, write_len);
-+
-+      if (ret)
-+              pr_info("nandx_write fail(%d), offset:0x%llx, len(%d)\n",
-+                     ret, desc->bbt_addr, write_len);
-+      mem_free(buf);
-+
-+      return ret;
-+}
-+
-+static int write_bbt(struct nandx_info *nand, struct bbt_desc *main,
-+                   struct bbt_desc *mirror, u8 *bbt, int max_blocks)
-+{
-+      int block;
-+      int ret;
-+
-+      do {
-+              if (main->bbt_addr == BBT_INVALID_ADDR) {
-+                      main->bbt_addr = get_bbt_address(nand, bbt,
-+                                       mirror->bbt_addr, max_blocks);
-+                      if (main->bbt_addr == BBT_INVALID_ADDR)
-+                              return -ENOSPC;
-+              }
-+
-+              ret = save_bbt(nand, main, bbt);
-+              if (!ret)
-+                      break;
-+
-+              block = div_down(main->bbt_addr, nand->block_size);
-+              set_bbt_mark(bbt, block, BBT_BLOCK_WORN);
-+              main->version++;
-+              mark_nand_bad(nand, block);
-+              main->bbt_addr = BBT_INVALID_ADDR;
-+      } while (1);
-+
-+      return 0;
-+}
-+
-+static void mark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks)
-+{
-+      int total_block;
-+      int block;
-+      u8 mark;
-+
-+      total_block = div_down(nand->total_size, nand->block_size);
-+      block = total_block - bbt_blocks;
-+
-+      while (bbt_blocks) {
-+              mark = get_bbt_mark(bbt, block);
-+              if (mark == BBT_BLOCK_GOOD)
-+                      set_bbt_mark(bbt, block, BBT_BLOCK_RESERVED);
-+              block++;
-+              bbt_blocks--;
-+      }
-+}
-+
-+static void unmark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks)
-+{
-+      int total_block;
-+      int block;
-+      u8 mark;
-+
-+      total_block = div_down(nand->total_size, nand->block_size);
-+      block = total_block - bbt_blocks;
-+
-+      while (bbt_blocks) {
-+              mark = get_bbt_mark(bbt, block);
-+              if (mark == BBT_BLOCK_RESERVED)
-+                      set_bbt_mark(bbt, block, BBT_BLOCK_GOOD);
-+              block++;
-+              bbt_blocks--;
-+      }
-+}
-+
-+static int update_bbt(struct nandx_info *nand, struct bbt_desc *desc,
-+                    u8 *bbt,
-+                    int max_blocks)
-+{
-+      int ret = 0, i;
-+
-+      /* The reserved info is not stored in NAND*/
-+      unmark_bbt_region(nand, bbt, max_blocks);
-+
-+      desc[0].version++;
-+      for (i = 0; i < 2; i++) {
-+              if (i > 0)
-+                      desc[i].version = desc[i - 1].version;
-+
-+              ret = write_bbt(nand, &desc[i], &desc[1 - i], bbt, max_blocks);
-+              if (ret)
-+                      break;
-+      }
-+      mark_bbt_region(nand, bbt, max_blocks);
-+
-+      return ret;
-+}
-+
-+int scan_bbt(struct nandx_info *nand)
-+{
-+      struct bbt_manager *manager = &g_bbt_manager;
-+      struct bbt_desc *pdesc;
-+      int total_block, len, i;
-+      int valid_desc = 0;
-+      int ret = 0;
-+      u8 *bbt;
-+
-+      total_block = div_down(nand->total_size, nand->block_size);
-+      len = GET_BBT_LENGTH(total_block);
-+
-+      if (!manager->bbt) {
-+              manager->bbt = (u8 *)mem_alloc(1, len);
-+              if (!manager->bbt) {
-+                      pr_info("%s, %d, mem alloc fail!!! len:%d\n",
-+                             __func__, __LINE__, len);
-+                      return -ENOMEM;
-+              }
-+      }
-+      bbt = manager->bbt;
-+      memset(bbt, 0xFF, len);
-+
-+      /* scan bbt */
-+      for (i = 0; i < 2; i++) {
-+              pdesc = &manager->desc[i];
-+              pdesc->bbt_addr = BBT_INVALID_ADDR;
-+              pdesc->version = 0;
-+              ret = search_bbt(nand, pdesc, manager->max_blocks);
-+              if (!ret && (pdesc->bbt_addr != BBT_INVALID_ADDR))
-+                      valid_desc += 1 << i;
-+      }
-+
-+      pdesc = &manager->desc[0];
-+      if ((valid_desc == 0x3) && (pdesc[0].version != pdesc[1].version))
-+              valid_desc = (pdesc[0].version > pdesc[1].version) ? 1 : 2;
-+
-+      /* read bbt */
-+      for (i = 0; i < 2; i++) {
-+              if (!(valid_desc & (1 << i)))
-+                      continue;
-+              ret = read_bbt(&pdesc[i], bbt, len);
-+              if (ret) {
-+                      pdesc->bbt_addr = BBT_INVALID_ADDR;
-+                      pdesc->version = 0;
-+                      valid_desc &= ~(1 << i);
-+              }
-+              /* If two BBT version is same, only need to read the first bbt*/
-+              if ((valid_desc == 0x3) &&
-+                  (pdesc[0].version == pdesc[1].version))
-+                      break;
-+      }
-+
-+      if (!valid_desc) {
-+              create_bbt(nand, bbt);
-+              pdesc[0].version = 1;
-+              pdesc[1].version = 1;
-+      }
-+
-+      pdesc[0].version = max_t(u8, pdesc[0].version, pdesc[1].version);
-+      pdesc[1].version = pdesc[0].version;
-+
-+      for (i = 0; i < 2; i++) {
-+              if (valid_desc & (1 << i))
-+                      continue;
-+
-+              ret = write_bbt(nand, &pdesc[i], &pdesc[1 - i], bbt,
-+                              manager->max_blocks);
-+              if (ret) {
-+                      pr_info("write bbt(%d) fail, ret:%d\n", i, ret);
-+                      manager->bbt = NULL;
-+                      return ret;
-+              }
-+      }
-+
-+      /* Prevent the bbt regions from erasing / writing */
-+      mark_bbt_region(nand, manager->bbt, manager->max_blocks);
-+
-+      for (i = 0; i < total_block; i++) {
-+              if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_WORN)
-+                      pr_info("Checked WORN bad blk: %d\n", i);
-+              else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_FACTORY_BAD)
-+                      pr_info("Checked Factory bad blk: %d\n", i);
-+              else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_RESERVED)
-+                      pr_info("Checked Reserved blk: %d\n", i);
-+              else if (get_bbt_mark(manager->bbt, i) != BBT_BLOCK_GOOD)
-+                      pr_info("Checked unknown blk: %d\n", i);
-+      }
-+
-+      return 0;
-+}
-+
-+int bbt_mark_bad(struct nandx_info *nand, off_t offset)
-+{
-+      struct bbt_manager *manager = &g_bbt_manager;
-+      int block = div_down(offset, nand->block_size);
-+      int ret = 0;
-+
-+      mark_nand_bad(nand, block);
-+
-+#if 0
-+      set_bbt_mark(manager->bbt, block, BBT_BLOCK_WORN);
-+
-+      /* Update flash-based bad block table */
-+      ret = update_bbt(nand, manager->desc, manager->bbt,
-+                       manager->max_blocks);
-+#endif
-+      pr_info("block %d, update result %d.\n", block, ret);
-+
-+      return ret;
-+}
-+
-+int bbt_is_bad(struct nandx_info *nand, off_t offset)
-+{
-+      int block;
-+
-+      block = div_down(offset, nand->block_size);
-+
-+      return get_bbt_mark(g_bbt_manager.bbt, block) != BBT_BLOCK_GOOD;
-+}
---- /dev/null
-+++ b/drivers/mtd/nandx/driver/uboot/driver.c
-@@ -0,0 +1,574 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#include <common.h>
-+#include <linux/io.h>
-+#include <dm.h>
-+#include <clk.h>
-+#include <nand.h>
-+#include <linux/iopoll.h>
-+#include <linux/delay.h>
-+#include <linux/mtd/nand.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/partitions.h>
-+#include "nandx_core.h"
-+#include "nandx_util.h"
-+#include "bbt.h"
-+
-+typedef int (*func_nandx_operation)(u8 *, u8 *, u64, size_t);
-+
-+struct nandx_clk {
-+      struct clk *nfi_clk;
-+      struct clk *ecc_clk;
-+      struct clk *snfi_clk;
-+      struct clk *snfi_clk_sel;
-+      struct clk *snfi_parent_50m;
-+};
-+
-+struct nandx_nfc {
-+      struct nandx_info info;
-+      struct nandx_clk clk;
-+      struct nfi_resource *res;
-+
-+      struct nand_chip *nand;
-+      spinlock_t lock;
-+};
-+
-+/* Default flash layout for MTK nand controller
-+ * 64Bytes oob format.
-+ */
-+static struct nand_ecclayout eccoob = {
-+      .eccbytes = 42,
-+      .eccpos = {
-+              17, 18, 19, 20, 21, 22, 23, 24, 25,
-+              26, 27, 28, 29, 30, 31, 32, 33, 34,
-+              35, 36, 37, 38, 39, 40, 41
-+      },
-+      .oobavail = 16,
-+      .oobfree = {
-+                      {
-+                      .offset = 0,
-+                      .length = 16,
-+                      },
-+      }
-+};
-+
-+static struct nandx_nfc *mtd_to_nfc(struct mtd_info *mtd)
-+{
-+      struct nand_chip *nand = mtd_to_nand(mtd);
-+
-+      return (struct nandx_nfc *)nand_get_controller_data(nand);
-+}
-+
-+static int nandx_enable_clk(struct nandx_clk *clk)
-+{
-+      int ret;
-+
-+      ret = clk_enable(clk->nfi_clk);
-+      if (ret) {
-+              pr_info("failed to enable nfi clk\n");
-+              return ret;
-+      }
-+
-+      ret = clk_enable(clk->ecc_clk);
-+      if (ret) {
-+              pr_info("failed to enable ecc clk\n");
-+              goto disable_nfi_clk;
-+      }
-+
-+      ret = clk_enable(clk->snfi_clk);
-+      if (ret) {
-+              pr_info("failed to enable snfi clk\n");
-+              goto disable_ecc_clk;
-+      }
-+
-+      ret = clk_enable(clk->snfi_clk_sel);
-+      if (ret) {
-+              pr_info("failed to enable snfi clk sel\n");
-+              goto disable_snfi_clk;
-+      }
-+
-+      ret = clk_set_parent(clk->snfi_clk_sel, clk->snfi_parent_50m);
-+      if (ret) {
-+              pr_info("failed to set snfi parent 50MHz\n");
-+              goto disable_snfi_clk;
-+      }
-+
-+      return 0;
-+
-+disable_snfi_clk:
-+      clk_disable(clk->snfi_clk);
-+disable_ecc_clk:
-+      clk_disable(clk->ecc_clk);
-+disable_nfi_clk:
-+      clk_disable(clk->nfi_clk);
-+
-+      return ret;
-+}
-+
-+static void nandx_disable_clk(struct nandx_clk *clk)
-+{
-+      clk_disable(clk->ecc_clk);
-+      clk_disable(clk->nfi_clk);
-+      clk_disable(clk->snfi_clk);
-+}
-+
-+static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section,
-+                                struct mtd_oob_region *oob_region)
-+{
-+      struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
-+      u32 eccsteps;
-+
-+      eccsteps = div_down(mtd->writesize, mtd->ecc_step_size);
-+
-+      if (section >= eccsteps)
-+              return -EINVAL;
-+
-+      oob_region->length = nfc->info.fdm_reg_size - nfc->info.fdm_ecc_size;
-+      oob_region->offset = section * nfc->info.fdm_reg_size
-+              + nfc->info.fdm_ecc_size;
-+
-+      return 0;
-+}
-+
-+static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
-+                               struct mtd_oob_region *oob_region)
-+{
-+      struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
-+      u32 eccsteps;
-+
-+      if (section)
-+              return -EINVAL;
-+
-+      eccsteps = div_down(mtd->writesize, mtd->ecc_step_size);
-+      oob_region->offset = nfc->info.fdm_reg_size * eccsteps;
-+      oob_region->length = mtd->oobsize - oob_region->offset;
-+
-+      return 0;
-+}
-+
-+static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = {
-+      .rfree = mtk_nfc_ooblayout_free,
-+      .ecc = mtk_nfc_ooblayout_ecc,
-+};
-+
-+struct nfc_compatible {
-+      enum mtk_ic_version ic_ver;
-+
-+      u32 clock_1x;
-+      u32 *clock_2x;
-+      int clock_2x_num;
-+
-+      int min_oob_req;
-+};
-+
-+static const struct nfc_compatible nfc_compats_mt7622 = {
-+      .ic_ver = NANDX_MT7622,
-+      .clock_1x = 26000000,
-+      .clock_2x = NULL,
-+      .clock_2x_num = 8,
-+      .min_oob_req = 1,
-+};
-+
-+static const struct udevice_id ic_of_match[] = {
-+      {.compatible = "mediatek,mt7622-nfc", .data = &nfc_compats_mt7622},
-+      {}
-+};
-+
-+static int nand_operation(struct mtd_info *mtd, loff_t addr, size_t len,
-+            size_t *retlen, uint8_t *data, uint8_t *oob, bool read)
-+{
-+      struct nandx_split64 split = {0};
-+      func_nandx_operation operation;
-+      u64 block_oobs, val, align;
-+      uint8_t *databuf, *oobbuf;
-+      struct nandx_nfc *nfc;
-+      bool readoob;
-+      int ret = 0;
-+
-+      nfc = (struct nandx_nfc *)nand_get_controller_data;
-+      spin_lock(&nfc->lock);
-+
-+      databuf = data;
-+      oobbuf = oob;
-+
-+      readoob = data ? false : true;
-+      block_oobs = div_up(mtd->erasesize, mtd->writesize) * mtd->oobavail;
-+      align = readoob ? block_oobs : mtd->erasesize;
-+
-+      operation = read ? nandx_read : nandx_write;
-+
-+      nandx_split(&split, addr, len, val, align);
-+
-+      if (split.head_len) {
-+              ret = operation((u8 *) databuf, oobbuf, addr, split.head_len);
-+
-+              if (databuf)
-+                      databuf += split.head_len;
-+
-+              if (oobbuf)
-+                      oobbuf += split.head_len;
-+
-+              addr += split.head_len;
-+              *retlen += split.head_len;
-+      }
-+
-+      if (split.body_len) {
-+              while (div_up(split.body_len, align)) {
-+                      ret = operation((u8 *) databuf, oobbuf, addr, align);
-+
-+                      if (databuf) {
-+                              databuf += mtd->erasesize;
-+                              split.body_len -= mtd->erasesize;
-+                              *retlen += mtd->erasesize;
-+                      }
-+
-+                      if (oobbuf) {
-+                              oobbuf += block_oobs;
-+                              split.body_len -= block_oobs;
-+                              *retlen += block_oobs;
-+                      }
-+
-+                      addr += mtd->erasesize;
-+              }
-+
-+      }
-+
-+      if (split.tail_len) {
-+              ret = operation((u8 *) databuf, oobbuf, addr, split.tail_len);
-+              *retlen += split.tail_len;
-+      }
-+
-+      spin_unlock(&nfc->lock);
-+
-+      return ret;
-+}
-+
-+static int mtk_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
-+            size_t *retlen, u_char *buf)
-+{
-+      return nand_operation(mtd, from, len, retlen, buf, NULL, true);
-+}
-+
-+static int mtk_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
-+             size_t *retlen, const u_char *buf)
-+{
-+      return nand_operation(mtd, to, len, retlen, (uint8_t *)buf,
-+              NULL, false);
-+}
-+
-+int mtk_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
-+{
-+      size_t retlen;
-+
-+      return nand_operation(mtd, from, ops->ooblen, &retlen, NULL,
-+              ops->oobbuf, true);
-+}
-+
-+int mtk_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
-+{
-+      size_t retlen;
-+
-+      return nand_operation(mtd, to, ops->ooblen, &retlen, NULL,
-+              ops->oobbuf, false);
-+}
-+
-+static int mtk_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
-+{
-+      struct nandx_nfc *nfc;
-+      u64 erase_len, erase_addr;
-+      u32 block_size;
-+      int ret = 0;
-+
-+      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
-+      block_size = nfc->info.block_size;
-+      erase_len = instr->len;
-+      erase_addr = instr->addr;
-+      spin_lock(&nfc->lock);
-+      instr->state = MTD_ERASING;
-+
-+      while (erase_len) {
-+              if (mtk_nand_is_bad(mtd, erase_addr)) {
-+                      pr_info("block(0x%llx) is bad, not erase\n",
-+                              erase_addr);
-+                      instr->state = MTD_ERASE_FAILED;
-+                      goto erase_exit;
-+              } else {
-+                      ret = nandx_erase(erase_addr, block_size);
-+                      if (ret < 0) {
-+                              instr->state = MTD_ERASE_FAILED;
-+                              goto erase_exit;
-+                              pr_info("erase fail at blk %llu, ret:%d\n",
-+                                      erase_addr, ret);
-+                      }
-+              }
-+              erase_addr += block_size;
-+              erase_len -= block_size;
-+      }
-+
-+      instr->state = MTD_ERASE_DONE;
-+
-+erase_exit:
-+      ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-+
-+      spin_unlock(&nfc->lock);
-+      /* Do mtd call back function */
-+      if (!ret)
-+              mtd_erase_callback(instr);
-+
-+      return ret;
-+}
-+
-+int mtk_nand_is_bad(struct mtd_info *mtd, loff_t ofs)
-+{
-+      struct nandx_nfc *nfc;
-+      int ret;
-+
-+      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
-+      spin_lock(&nfc->lock);
-+
-+      /*ret = bbt_is_bad(&nfc->info, ofs);*/
-+      ret = nandx_is_bad_block(ofs);
-+      spin_unlock(&nfc->lock);
-+
-+      if (ret) {
-+              pr_info("nand block 0x%x is bad, ret %d!\n", ofs, ret);
-+              return 1;
-+      } else {
-+              return 0;
-+      }
-+}
-+
-+int mtk_nand_mark_bad(struct mtd_info *mtd, loff_t ofs)
-+{
-+      struct nandx_nfc *nfc;
-+      int ret;
-+
-+      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
-+      spin_lock(&nfc->lock);
-+      pr_info("%s, %d\n", __func__, __LINE__);
-+      ret = bbt_mark_bad(&nfc->info, ofs);
-+
-+      spin_unlock(&nfc->lock);
-+
-+      return ret;
-+}
-+
-+void mtk_nand_sync(struct mtd_info *mtd)
-+{
-+      nandx_sync();
-+}
-+
-+static struct mtd_info *mtd_info_create(struct udevice *pdev,
-+              struct nandx_nfc *nfc, struct nand_chip *nand)
-+{
-+      struct mtd_info *mtd = nand_to_mtd(nand);
-+      int ret;
-+
-+      nand_set_controller_data(nand, nfc);
-+
-+      nand->flash_node = dev_of_offset(pdev);
-+      nand->ecc.layout = &eccoob;
-+
-+      ret = nandx_ioctl(CORE_CTRL_NAND_INFO, &nfc->info);
-+      if (ret) {
-+              pr_info("fail to get nand info (%d)!\n", ret);
-+              mem_free(mtd);
-+              return NULL;
-+      }
-+
-+      mtd->owner = THIS_MODULE;
-+
-+      mtd->name = "MTK-SNand";
-+      mtd->writesize = nfc->info.page_size;
-+      mtd->erasesize = nfc->info.block_size;
-+      mtd->oobsize = nfc->info.oob_size;
-+      mtd->size = nfc->info.total_size;
-+      mtd->type = MTD_NANDFLASH;
-+      mtd->flags = MTD_CAP_NANDFLASH;
-+      mtd->_erase = mtk_nand_erase;
-+      mtd->_read = mtk_nand_read;
-+      mtd->_write = mtk_nand_write;
-+      mtd->_read_oob = mtk_nand_read_oob;
-+      mtd->_write_oob = mtk_nand_write_oob;
-+      mtd->_sync = mtk_nand_sync;
-+      mtd->_lock = NULL;
-+      mtd->_unlock = NULL;
-+      mtd->_block_isbad = mtk_nand_is_bad;
-+      mtd->_block_markbad = mtk_nand_mark_bad;
-+      mtd->writebufsize = mtd->writesize;
-+
-+      mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops);
-+
-+      mtd->ecc_strength = nfc->info.ecc_strength;
-+      mtd->ecc_step_size = nfc->info.sector_size;
-+
-+      if (!mtd->bitflip_threshold)
-+              mtd->bitflip_threshold = mtd->ecc_strength;
-+
-+      return mtd;
-+}
-+
-+int board_nand_init(struct nand_chip *nand)
-+{
-+      struct udevice *dev;
-+      struct mtd_info *mtd;
-+      struct nandx_nfc *nfc;
-+      int arg = 1;
-+      int ret;
-+
-+      ret = uclass_get_device_by_driver(UCLASS_MTD,
-+                                        DM_GET_DRIVER(mtk_snand_drv),
-+                                        &dev);
-+      if (ret) {
-+              pr_err("Failed to get mtk_nand_drv. (error %d)\n", ret);
-+              return ret;
-+      }
-+
-+      nfc = dev_get_priv(dev);
-+
-+      ret = nandx_enable_clk(&nfc->clk);
-+      if (ret) {
-+              pr_err("failed to enable nfi clk (error %d)\n", ret);
-+              return ret;
-+      }
-+
-+      ret = nandx_init(nfc->res);
-+      if (ret) {
-+              pr_err("nandx init error (%d)!\n", ret);
-+              goto disable_clk;
-+      }
-+
-+      arg = 1;
-+      nandx_ioctl(NFI_CTRL_DMA, &arg);
-+      nandx_ioctl(NFI_CTRL_ECC, &arg);
-+
-+#ifdef NANDX_UNIT_TEST
-+      nandx_unit_test(0x780000, 0x800);
-+#endif
-+
-+      mtd = mtd_info_create(dev, nfc, nand);
-+      if (!mtd) {
-+              ret = -ENOMEM;
-+              goto disable_clk;
-+      }
-+
-+      spin_lock_init(&nfc->lock);
-+#if 0
-+      ret = scan_bbt(&nfc->info);
-+      if (ret) {
-+              pr_info("bbt init error (%d)!\n", ret);
-+              goto disable_clk;
-+      }
-+#endif
-+      return ret;
-+
-+disable_clk:
-+      nandx_disable_clk(&nfc->clk);
-+
-+      return ret;
-+}
-+
-+static int mtk_snand_ofdata_to_platdata(struct udevice *dev)
-+{
-+      struct nandx_nfc *nfc = dev_get_priv(dev);
-+      struct nfc_compatible *compat;
-+      struct nfi_resource *res;
-+
-+      int ret = 0;
-+
-+      res = mem_alloc(1, sizeof(struct nfi_resource));
-+      if (!res)
-+              return -ENOMEM;
-+
-+      nfc->res = res;
-+
-+      res->nfi_regs = (void *)dev_read_addr_index(dev, 0);
-+      res->ecc_regs = (void *)dev_read_addr_index(dev, 1);
-+      pr_debug("mtk snand nfi_regs:0x%x ecc_regs:0x%x\n",
-+              res->nfi_regs, res->ecc_regs);
-+
-+      compat = (struct nfc_compatible *)dev_get_driver_data(dev);
-+
-+      res->ic_ver = (enum mtk_ic_version)(compat->ic_ver);
-+      res->clock_1x = compat->clock_1x;
-+      res->clock_2x = compat->clock_2x;
-+      res->clock_2x_num = compat->clock_2x_num;
-+
-+      memset(&nfc->clk, 0, sizeof(struct nandx_clk));
-+      nfc->clk.nfi_clk =
-+          kmalloc(sizeof(*nfc->clk.nfi_clk), GFP_KERNEL);
-+      nfc->clk.ecc_clk =
-+          kmalloc(sizeof(*nfc->clk.ecc_clk), GFP_KERNEL);
-+      nfc->clk.snfi_clk=
-+          kmalloc(sizeof(*nfc->clk.snfi_clk), GFP_KERNEL);
-+      nfc->clk.snfi_clk_sel =
-+          kmalloc(sizeof(*nfc->clk.snfi_clk_sel), GFP_KERNEL);
-+      nfc->clk.snfi_parent_50m =
-+          kmalloc(sizeof(*nfc->clk.snfi_parent_50m), GFP_KERNEL);
-+
-+      if (!nfc->clk.nfi_clk || !nfc->clk.ecc_clk || !nfc->clk.snfi_clk ||
-+              !nfc->clk.snfi_clk_sel || !nfc->clk.snfi_parent_50m) {
-+              ret = -ENOMEM;
-+              goto err;
-+      }
-+
-+      ret = clk_get_by_name(dev, "nfi_clk", nfc->clk.nfi_clk);
-+      if (IS_ERR(nfc->clk.nfi_clk)) {
-+              ret = PTR_ERR(nfc->clk.nfi_clk);
-+              goto err;
-+      }
-+
-+      ret = clk_get_by_name(dev, "ecc_clk", nfc->clk.ecc_clk);
-+      if (IS_ERR(nfc->clk.ecc_clk)) {
-+              ret = PTR_ERR(nfc->clk.ecc_clk);
-+              goto err;
-+      }
-+
-+      ret = clk_get_by_name(dev, "snfi_clk", nfc->clk.snfi_clk);
-+      if (IS_ERR(nfc->clk.snfi_clk)) {
-+              ret = PTR_ERR(nfc->clk.snfi_clk);
-+              goto err;
-+      }
-+
-+      ret = clk_get_by_name(dev, "spinfi_sel", nfc->clk.snfi_clk_sel);
-+      if (IS_ERR(nfc->clk.snfi_clk_sel)) {
-+              ret = PTR_ERR(nfc->clk.snfi_clk_sel);
-+              goto err;
-+      }
-+
-+      ret = clk_get_by_name(dev, "spinfi_parent_50m", nfc->clk.snfi_parent_50m);
-+      if (IS_ERR(nfc->clk.snfi_parent_50m))
-+              pr_info("spinfi parent 50MHz is not configed\n");
-+
-+      return 0;
-+err:
-+      if (nfc->clk.nfi_clk)
-+              kfree(nfc->clk.nfi_clk);
-+      if (nfc->clk.snfi_clk)
-+              kfree(nfc->clk.snfi_clk);
-+      if (nfc->clk.ecc_clk)
-+              kfree(nfc->clk.ecc_clk);
-+      if (nfc->clk.snfi_clk_sel)
-+              kfree(nfc->clk.snfi_clk_sel);
-+      if (nfc->clk.snfi_parent_50m)
-+              kfree(nfc->clk.snfi_parent_50m);
-+
-+      return ret;
-+}
-+
-+U_BOOT_DRIVER(mtk_snand_drv) = {
-+      .name = "mtk_snand",
-+      .id = UCLASS_MTD,
-+      .of_match = ic_of_match,
-+      .ofdata_to_platdata = mtk_snand_ofdata_to_platdata,
-+      .priv_auto_alloc_size = sizeof(struct nandx_nfc),
-+};
-+
-+MODULE_LICENSE("GPL v2");
-+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
-+MODULE_AUTHOR("MediaTek");
---- /dev/null
-+++ b/drivers/mtd/nandx/include/Nandx.mk
-@@ -0,0 +1,16 @@
-+#
-+# Copyright (C) 2017 MediaTek Inc.
-+# Licensed under either
-+#     BSD Licence, (see NOTICE for more details)
-+#     GNU General Public License, version 2.0, (see NOTICE for more details)
-+#
-+
-+nandx-header-y += internal/nandx_core.h
-+nandx-header-y += internal/nandx_errno.h
-+nandx-header-y += internal/nandx_util.h
-+nandx-header-$(NANDX_BBT_SUPPORT) += internal/bbt.h
-+nandx-header-$(NANDX_SIMULATOR_SUPPORT) += simulator/nandx_os.h
-+nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nandx_os.h
-+nandx-header-$(NANDX_LK_SUPPORT) += lk/nandx_os.h
-+nandx-header-$(NANDX_KERNEL_SUPPORT) += kernel/nandx_os.h
-+nandx-header-$(NANDX_UBOOT_SUPPORT) += uboot/nandx_os.h
---- /dev/null
-+++ b/drivers/mtd/nandx/include/internal/bbt.h
-@@ -0,0 +1,62 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __BBT_H__
-+#define __BBT_H__
-+
-+#define BBT_BLOCK_GOOD      0x03
-+#define BBT_BLOCK_WORN      0x02
-+#define BBT_BLOCK_RESERVED      0x01
-+#define BBT_BLOCK_FACTORY_BAD   0x00
-+
-+#define BBT_INVALID_ADDR 0
-+/* The maximum number of blocks to scan for a bbt */
-+#define NAND_BBT_SCAN_MAXBLOCKS 4
-+#define NAND_BBT_USE_FLASH  0x00020000
-+#define NAND_BBT_NO_OOB     0x00040000
-+
-+/* Search good / bad pattern on the first and the second page */
-+#define NAND_BBT_SCAN2NDPAGE    0x00008000
-+/* Search good / bad pattern on the last page of the eraseblock */
-+#define NAND_BBT_SCANLASTPAGE   0x00010000
-+
-+#define NAND_DRAM_BUF_DATABUF_ADDR  (NAND_BUF_ADDR)
-+
-+struct bbt_pattern {
-+      u8 *data;
-+      int len;
-+};
-+
-+struct bbt_desc {
-+      struct bbt_pattern pattern;
-+      u8 version;
-+      u64 bbt_addr;/*0: invalid value; otherwise, valid value*/
-+};
-+
-+struct bbt_manager {
-+      /* main bbt descriptor and mirror descriptor */
-+      struct bbt_desc desc[2];/* 0: main bbt; 1: mirror bbt */
-+      int max_blocks;
-+      u8 *bbt;
-+};
-+
-+#define BBT_ENTRY_MASK      0x03
-+#define BBT_ENTRY_SHIFT     2
-+
-+#define GET_BBT_LENGTH(blocks) (blocks >> 2)
-+#define GET_ENTRY(block) ((block) >> BBT_ENTRY_SHIFT)
-+#define GET_POSITION(block) (((block) & BBT_ENTRY_MASK) * 2)
-+#define GET_MARK_VALUE(block, mark) \
-+      (((mark) & BBT_ENTRY_MASK) << GET_POSITION(block))
-+
-+int scan_bbt(struct nandx_info *nand);
-+
-+int bbt_mark_bad(struct nandx_info *nand, off_t offset);
-+
-+int bbt_is_bad(struct nandx_info *nand, off_t offset);
-+
-+#endif /*__BBT_H__*/
---- /dev/null
-+++ b/drivers/mtd/nandx/include/internal/nandx_core.h
-@@ -0,0 +1,250 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NANDX_CORE_H__
-+#define __NANDX_CORE_H__
-+
-+/**
-+ * mtk_ic_version - indicates specifical IC, IP need this to load some info
-+ */
-+enum mtk_ic_version {
-+      NANDX_MT7622,
-+};
-+
-+/**
-+ * nandx_ioctl_cmd - operations supported by nandx
-+ *
-+ * @NFI_CTRL_DMA dma enable or not
-+ * @NFI_CTRL_NFI_MODE customer/read/program/erase...
-+ * @NFI_CTRL_ECC ecc enable or not
-+ * @NFI_CTRL_ECC_MODE nfi/dma/pio
-+ * @CHIP_CTRL_DRIVE_STRENGTH enum chip_ctrl_drive_strength
-+ */
-+enum nandx_ctrl_cmd {
-+      CORE_CTRL_NAND_INFO,
-+
-+      NFI_CTRL_DMA,
-+      NFI_CTRL_NFI_MODE,
-+      NFI_CTRL_AUTOFORMAT,
-+      NFI_CTRL_NFI_IRQ,
-+      NFI_CTRL_PAGE_IRQ,
-+      NFI_CTRL_RANDOMIZE,
-+      NFI_CTRL_BAD_MARK_SWAP,
-+
-+      NFI_CTRL_ECC,
-+      NFI_CTRL_ECC_MODE,
-+      NFI_CTRL_ECC_CLOCK,
-+      NFI_CTRL_ECC_IRQ,
-+      NFI_CTRL_ECC_PAGE_IRQ,
-+      NFI_CTRL_ECC_DECODE_MODE,
-+
-+      SNFI_CTRL_OP_MODE,
-+      SNFI_CTRL_RX_MODE,
-+      SNFI_CTRL_TX_MODE,
-+      SNFI_CTRL_DELAY_MODE,
-+
-+      CHIP_CTRL_OPS_CACHE,
-+      CHIP_CTRL_OPS_MULTI,
-+      CHIP_CTRL_PSLC_MODE,
-+      CHIP_CTRL_DRIVE_STRENGTH,
-+      CHIP_CTRL_DDR_MODE,
-+      CHIP_CTRL_ONDIE_ECC,
-+      CHIP_CTRL_TIMING_MODE
-+};
-+
-+enum snfi_ctrl_op_mode {
-+      SNFI_CUSTOM_MODE,
-+      SNFI_AUTO_MODE,
-+      SNFI_MAC_MODE
-+};
-+
-+enum snfi_ctrl_rx_mode {
-+      SNFI_RX_111,
-+      SNFI_RX_112,
-+      SNFI_RX_114,
-+      SNFI_RX_122,
-+      SNFI_RX_144
-+};
-+
-+enum snfi_ctrl_tx_mode {
-+      SNFI_TX_111,
-+      SNFI_TX_114,
-+};
-+
-+enum chip_ctrl_drive_strength {
-+      CHIP_DRIVE_NORMAL,
-+      CHIP_DRIVE_HIGH,
-+      CHIP_DRIVE_MIDDLE,
-+      CHIP_DRIVE_LOW
-+};
-+
-+enum chip_ctrl_timing_mode {
-+      CHIP_TIMING_MODE0,
-+      CHIP_TIMING_MODE1,
-+      CHIP_TIMING_MODE2,
-+      CHIP_TIMING_MODE3,
-+      CHIP_TIMING_MODE4,
-+      CHIP_TIMING_MODE5,
-+};
-+
-+/**
-+ * nandx_info - basic information
-+ */
-+struct nandx_info {
-+      u32 max_io_count;
-+      u32 min_write_pages;
-+      u32 plane_num;
-+      u32 oob_size;
-+      u32 page_parity_size;
-+      u32 page_size;
-+      u32 block_size;
-+      u64 total_size;
-+      u32 fdm_reg_size;
-+      u32 fdm_ecc_size;
-+      u32 ecc_strength;
-+      u32 sector_size;
-+};
-+
-+/**
-+ * nfi_resource - the resource needed by nfi & ecc to do initialization
-+ */
-+struct nfi_resource {
-+      int ic_ver;
-+      void *dev;
-+
-+      void *ecc_regs;
-+      int ecc_irq_id;
-+
-+      void *nfi_regs;
-+      int nfi_irq_id;
-+
-+      u32 clock_1x;
-+      u32 *clock_2x;
-+      int clock_2x_num;
-+
-+      int min_oob_req;
-+};
-+
-+/**
-+ * nandx_init - init all related modules below
-+ *
-+ * @res: basic resource of the project
-+ *
-+ * return 0 if init success, otherwise return negative error code
-+ */
-+int nandx_init(struct nfi_resource *res);
-+
-+/**
-+ * nandx_exit - release resource those that obtained in init flow
-+ */
-+void nandx_exit(void);
-+
-+/**
-+ * nandx_read - read data from nand this function can read data and related
-+ *   oob from specifical address
-+ *   if do multi_ops, set one operation per time, and call nandx_sync at last
-+ *   in multi mode, not support page partial read
-+ *   oob not support partial read
-+ *
-+ * @data: buf to receive data from nand
-+ * @oob: buf to receive oob data from nand which related to data page
-+ *   length of @oob should oob size aligned, oob not support partial read
-+ * @offset: offset address on the whole flash
-+ * @len: the length of @data that need to read
-+ *
-+ * if read success return 0, otherwise return negative error code
-+ */
-+int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len);
-+
-+/**
-+ * nandx_write -  write data to nand
-+ *   this function can write data and related oob to specifical address
-+ *   if do multi_ops, set one operation per time, and call nandx_sync at last
-+ *
-+ * @data: source data to be written to nand,
-+ *   for multi operation, the length of @data should be page size aliged
-+ * @oob: source oob which related to data page to be written to nand,
-+ *   length of @oob should oob size aligned
-+ * @offset: offset address on the whole flash, the value should be start address
-+ *   of a page
-+ * @len: the length of @data that need to write,
-+ *   for multi operation, the len should be page size aliged
-+ *
-+ * if write success return 0, otherwise return negative error code
-+ * if return value > 0, it indicates that how many pages still need to write,
-+ * and data has not been written to nand
-+ * please call nandx_sync after pages alligned $nandx_info.min_write_pages
-+ */
-+int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len);
-+
-+/**
-+ * nandx_erase - erase an area of nand
-+ *   if do multi_ops, set one operation per time, and call nandx_sync at last
-+ *
-+ * @offset: offset address on the flash
-+ * @len: erase length which should be block size aligned
-+ *
-+ * if erase success return 0, otherwise return negative error code
-+ */
-+int nandx_erase(u64 offset, size_t len);
-+
-+/**
-+ * nandx_sync - sync all operations to nand
-+ *   when do multi_ops, this function will be called at last operation
-+ *   when write data, if number of pages not alligned
-+ *   by $nandx_info.min_write_pages, this interface could be called to do
-+ *   force write, 0xff will be padded to blanked pages.
-+ */
-+int nandx_sync(void);
-+
-+/**
-+ * nandx_is_bad_block - check if the block is bad
-+ *   only check the flag that marked by the flash vendor
-+ *
-+ * @offset: offset address on the whole flash
-+ *
-+ * return true if the block is bad, otherwise return false
-+ */
-+bool nandx_is_bad_block(u64 offset);
-+
-+/**
-+ * nandx_ioctl - set/get property of nand chip
-+ *
-+ * @cmd: parameter that defined in enum nandx_ioctl_cmd
-+ * @arg: operate parameter
-+ *
-+ * return 0 if operate success, otherwise return negative error code
-+ */
-+int nandx_ioctl(int cmd, void *arg);
-+
-+/**
-+ * nandx_suspend - suspend nand, and store some data
-+ *
-+ * return 0 if suspend success, otherwise return negative error code
-+ */
-+int nandx_suspend(void);
-+
-+/**
-+ * nandx_resume - resume nand, and replay some data
-+ *
-+ * return 0 if resume success, otherwise return negative error code
-+ */
-+int nandx_resume(void);
-+
-+#ifdef NANDX_UNIT_TEST
-+/**
-+ * nandx_unit_test - unit test
-+ *
-+ * @offset: offset address on the whole flash
-+ * @len: should be not larger than a block size, we only test a block per time
-+ *
-+ * return 0 if test success, otherwise return negative error code
-+ */
-+int nandx_unit_test(u64 offset, size_t len);
-+#endif
-+
-+#endif /* __NANDX_CORE_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/include/internal/nandx_errno.h
-@@ -0,0 +1,40 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NANDX_ERRNO_H__
-+#define __NANDX_ERRNO_H__
-+
-+#ifndef EIO
-+#define EIO             5       /* I/O error */
-+#define ENOMEM          12      /* Out of memory */
-+#define EFAULT          14      /* Bad address */
-+#define EBUSY           16      /* Device or resource busy */
-+#define ENODEV          19      /* No such device */
-+#define EINVAL          22      /* Invalid argument */
-+#define ENOSPC          28      /* No space left on device */
-+/* Operation not supported on transport endpoint */
-+#define EOPNOTSUPP      95
-+#define ETIMEDOUT       110     /* Connection timed out */
-+#endif
-+
-+#define ENANDFLIPS      1024    /* Too many bitflips, uncorrected */
-+#define ENANDREAD       1025    /* Read fail, can't correct */
-+#define ENANDWRITE      1026    /* Write fail */
-+#define ENANDERASE      1027    /* Erase fail */
-+#define ENANDBAD        1028    /* Bad block */
-+#define ENANDWP         1029
-+
-+#define IS_NAND_ERR(err)        ((err) >= -ENANDBAD && (err) <= -ENANDFLIPS)
-+
-+#ifndef MAX_ERRNO
-+#define MAX_ERRNO       4096
-+#define ERR_PTR(errno)  ((void *)((long)errno))
-+#define PTR_ERR(ptr)    ((long)(ptr))
-+#define IS_ERR(ptr)     ((unsigned long)(ptr) > (unsigned long)-MAX_ERRNO)
-+#endif
-+
-+#endif /* __NANDX_ERRNO_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/include/internal/nandx_util.h
-@@ -0,0 +1,221 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NANDX_UTIL_H__
-+#define __NANDX_UTIL_H__
-+
-+typedef unsigned char u8;
-+typedef unsigned short u16;
-+typedef unsigned int u32;
-+typedef unsigned long long u64;
-+
-+enum nand_irq_return {
-+      NAND_IRQ_NONE,
-+      NAND_IRQ_HANDLED,
-+};
-+
-+enum nand_dma_operation {
-+      NDMA_FROM_DEV,
-+      NDMA_TO_DEV,
-+};
-+
-+
-+/*
-+ * Compatible function
-+ * used for preloader/lk/kernel environment
-+ */
-+#include "nandx_os.h"
-+#include "nandx_errno.h"
-+
-+#ifndef BIT
-+#define BIT(a)                  (1 << (a))
-+#endif
-+
-+#ifndef min_t
-+#define min_t(type, x, y) ({                    \
-+      type __min1 = (x);                      \
-+      type __min2 = (y);                      \
-+      __min1 < __min2 ? __min1 : __min2; })
-+
-+#define max_t(type, x, y) ({                    \
-+      type __max1 = (x);                      \
-+      type __max2 = (y);                      \
-+      __max1 > __max2 ? __max1 : __max2; })
-+#endif
-+
-+#ifndef GENMASK
-+#define GENMASK(h, l) \
-+      (((~0UL) << (l)) & (~0UL >> ((sizeof(unsigned long) * 8) - 1 - (h))))
-+#endif
-+
-+#ifndef __weak
-+#define __weak __attribute__((__weak__))
-+#endif
-+
-+#ifndef __packed
-+#define __packed __attribute__((__packed__))
-+#endif
-+
-+#ifndef KB
-+#define KB(x)   ((x) << 10)
-+#define MB(x)   (KB(x) << 10)
-+#define GB(x)   (MB(x) << 10)
-+#endif
-+
-+#ifndef offsetof
-+#define offsetof(type, member) ((size_t)&((type *)0)->member)
-+#endif
-+
-+#ifndef NULL
-+#define NULL (void *)0
-+#endif
-+static inline u32 nandx_popcount(u32 x)
-+{
-+      x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
-+      x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
-+      x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
-+      x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
-+      x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
-+
-+      return x;
-+}
-+
-+#ifndef zero_popcount
-+#define zero_popcount(x) (32 - nandx_popcount(x))
-+#endif
-+
-+#ifndef do_div
-+#define do_div(n, base) \
-+      ({ \
-+              u32 __base = (base); \
-+              u32 __rem; \
-+              __rem = ((u64)(n)) % __base; \
-+              (n) = ((u64)(n)) / __base; \
-+              __rem; \
-+      })
-+#endif
-+
-+#define div_up(x, y) \
-+      ({ \
-+              u64 __temp = ((x) + (y) - 1); \
-+              do_div(__temp, (y)); \
-+              __temp; \
-+      })
-+
-+#define div_down(x, y) \
-+      ({ \
-+              u64 __temp = (x); \
-+              do_div(__temp, (y)); \
-+              __temp; \
-+      })
-+
-+#define div_round_up(x, y)      (div_up(x, y) * (y))
-+#define div_round_down(x, y)    (div_down(x, y) * (y))
-+
-+#define reminder(x, y) \
-+      ({ \
-+              u64 __temp = (x); \
-+              do_div(__temp, (y)); \
-+      })
-+
-+#ifndef round_up
-+#define round_up(x, y)          ((((x) - 1) | ((y) - 1)) + 1)
-+#define round_down(x, y)        ((x) & ~((y) - 1))
-+#endif
-+
-+#ifndef readx_poll_timeout_atomic
-+#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \
-+      ({ \
-+              u64 end = get_current_time_us() + timeout_us; \
-+              for (;;) { \
-+                      u64 now = get_current_time_us(); \
-+                      (val) = op(addr); \
-+                      if (cond) \
-+                              break; \
-+                      if (now > end) { \
-+                              (val) = op(addr); \
-+                              break; \
-+                      } \
-+              } \
-+              (cond) ? 0 : -ETIMEDOUT; \
-+      })
-+
-+#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
-+      readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us)
-+#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
-+      readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us)
-+#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
-+      readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us)
-+#endif
-+
-+struct nandx_split64 {
-+      u64 head;
-+      size_t head_len;
-+      u64 body;
-+      size_t body_len;
-+      u64 tail;
-+      size_t tail_len;
-+};
-+
-+struct nandx_split32 {
-+      u32 head;
-+      u32 head_len;
-+      u32 body;
-+      u32 body_len;
-+      u32 tail;
-+      u32 tail_len;
-+};
-+
-+#define nandx_split(split, offset, len, val, align) \
-+      do { \
-+              (split)->head = (offset); \
-+              (val) = div_round_down((offset), (align)); \
-+              (val) = (align) - ((offset) - (val)); \
-+              if ((val) == (align)) \
-+                      (split)->head_len = 0; \
-+              else if ((val) > (len)) \
-+                      (split)->head_len = len; \
-+              else \
-+                      (split)->head_len = val; \
-+              (split)->body = (offset) + (split)->head_len; \
-+              (split)->body_len = div_round_down((len) - \
-+                                                 (split)->head_len,\
-+                                                 (align)); \
-+              (split)->tail = (split)->body + (split)->body_len; \
-+              (split)->tail_len = (len) - (split)->head_len - \
-+                                  (split)->body_len; \
-+      } while (0)
-+
-+#ifndef container_of
-+#define container_of(ptr, type, member) \
-+      ({const __typeof__(((type *)0)->member) * __mptr = (ptr); \
-+              (type *)((char *)__mptr - offsetof(type, member)); })
-+#endif
-+
-+static inline u32 nandx_cpu_to_be32(u32 val)
-+{
-+      u32 temp = 1;
-+      u8 *p_temp = (u8 *)&temp;
-+
-+      if (*p_temp)
-+              return ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
-+                     ((val >> 8) & 0xff00) | ((val >> 24) & 0xff);
-+
-+      return val;
-+}
-+
-+static inline void nandx_set_bits32(unsigned long addr, u32 mask,
-+                                  u32 val)
-+{
-+      u32 temp = readl((void *)addr);
-+
-+      temp &= ~(mask);
-+      temp |= val;
-+      writel(temp, (void *)addr);
-+}
-+
-+#endif /* __NANDX_UTIL_H__ */
---- /dev/null
-+++ b/drivers/mtd/nandx/include/uboot/nandx_os.h
-@@ -0,0 +1,78 @@
-+/*
-+ * Copyright (C) 2017 MediaTek Inc.
-+ * Licensed under either
-+ *     BSD Licence, (see NOTICE for more details)
-+ *     GNU General Public License, version 2.0, (see NOTICE for more details)
-+ */
-+
-+#ifndef __NANDX_OS_H__
-+#define __NANDX_OS_H__
-+
-+#include <common.h>
-+#include <dm.h>
-+#include <clk.h>
-+#include <asm/dma-mapping.h>
-+#include <linux/io.h>
-+#include <linux/err.h>
-+#include <linux/errno.h>
-+#include <linux/bitops.h>
-+#include <linux/kernel.h>
-+#include <linux/compiler-gcc.h>
-+
-+#define NANDX_BULK_IO_USE_DRAM 0
-+
-+#define nandx_event_create()     NULL
-+#define nandx_event_destroy(event)
-+#define nandx_event_complete(event)
-+#define nandx_event_init(event)
-+#define nandx_event_wait_complete(event, timeout)        true
-+
-+#define nandx_irq_register(dev, irq, irq_handler, name, data) NULL
-+
-+static inline void *mem_alloc(u32 count, u32 size)
-+{
-+      return kmalloc(count * size, GFP_KERNEL | __GFP_ZERO);
-+}
-+
-+static inline void mem_free(void *mem)
-+{
-+      kfree(mem);
-+}
-+
-+static inline u64 get_current_time_us(void)
-+{
-+      return timer_get_us();
-+}
-+
-+static inline u32 nandx_dma_map(void *dev, void *buf, u64 len,
-+                              enum nand_dma_operation op)
-+{
-+      unsigned long addr = (unsigned long)buf;
-+      u64 size;
-+
-+      size = ALIGN(len, ARCH_DMA_MINALIGN);
-+
-+      if (op == NDMA_FROM_DEV)
-+              invalidate_dcache_range(addr, addr + size);
-+      else
-+              flush_dcache_range(addr, addr + size);
-+
-+      return addr;
-+}
-+
-+static inline void nandx_dma_unmap(void *dev, void *buf, void *addr,
-+                                 u64 len, enum nand_dma_operation op)
-+{
-+      u64 size;
-+
-+      size = ALIGN(len, ARCH_DMA_MINALIGN);
-+
-+      if (op != NDMA_FROM_DEV)
-+              invalidate_dcache_range((unsigned long)addr, addr + size);
-+      else
-+              flush_dcache_range((unsigned long)addr, addr + size);
-+
-+      return addr;
-+}
-+
-+#endif /* __NANDX_OS_H__ */
---- a/include/configs/mt7622.h
-+++ b/include/configs/mt7622.h
-@@ -11,6 +11,31 @@
- #include <linux/sizes.h>
-+/* SPI Nand */
-+#if defined(CONFIG_MTD_RAW_NAND)
-+#define CONFIG_SYS_MAX_NAND_DEVICE 1
-+#define CONFIG_SYS_NAND_BASE          0x1100d000
-+
-+#define ENV_BOOT_READ_IMAGE \
-+      "boot_rd_img=" \
-+      "nand read 0x4007ff28 0x380000 0x1400000" \
-+      ";iminfo 0x4007ff28 \0"
-+
-+#define ENV_BOOT_WRITE_IMAGE \
-+      "boot_wr_img=" \
-+      "nand write 0x4007ff28 0x380000 0x1400000" \
-+      ";iminfo 0x4007ff28 \0"
-+
-+#define ENV_BOOT_CMD \
-+      "mtk_boot=run boot_rd_img;bootm;\0"
-+
-+#define CONFIG_EXTRA_ENV_SETTINGS \
-+      ENV_BOOT_READ_IMAGE \
-+      ENV_BOOT_CMD \
-+      "bootcmd=run mtk_boot;\0"
-+
-+#endif
-+
- #define CONFIG_SYS_MAXARGS            8
- #define CONFIG_SYS_BOOTM_LEN          SZ_64M
- #define CONFIG_SYS_CBSIZE             SZ_1K
diff --git a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch b/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch
deleted file mode 100644 (file)
index 7167a49..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-From b1b3c3d2ce62872c8dec4a7d645af6b3c565e094 Mon Sep 17 00:00:00 2001
-From: Sam Shih <sam.shih@mediatek.com>
-Date: Mon, 20 Apr 2020 17:11:32 +0800
-Subject: [PATCH 2/3] mt7622 uboot: add dts and config for spi nand
-
-This patch add dts and config for mt7622 spi nand
-
-Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
----
- arch/arm/dts/mt7622-rfb.dts |  6 ++++++
- arch/arm/dts/mt7622.dtsi    | 20 ++++++++++++++++++++
- 2 files changed, 26 insertions(+)
-
---- a/arch/arm/dts/mt7622-rfb.dts
-+++ b/arch/arm/dts/mt7622-rfb.dts
-@@ -174,6 +174,12 @@
-       };
- };
-+&nandc {
-+      pinctrl-names = "default";
-+      pinctrl-0 = <&snfi_pins>;
-+      status = "okay";
-+};
-+
- &uart0 {
-       pinctrl-names = "default";
-       pinctrl-0 = <&uart0_pins>;
---- a/arch/arm/dts/mt7622.dtsi
-+++ b/arch/arm/dts/mt7622.dtsi
-@@ -53,6 +53,26 @@
-               #size-cells = <0>;
-       };
-+      nandc: nfi@1100d000 {
-+              compatible = "mediatek,mt7622-nfc";
-+              reg = <0x1100d000 0x1000>,
-+                    <0x1100e000 0x1000>;
-+              interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>,
-+                           <GIC_SPI 95 IRQ_TYPE_LEVEL_LOW>;
-+              clocks = <&pericfg CLK_PERI_NFI_PD>,
-+                       <&pericfg CLK_PERI_NFIECC_PD>,
-+                       <&pericfg CLK_PERI_SNFI_PD>,
-+                       <&topckgen CLK_TOP_NFI_INFRA_SEL>,
-+                       <&topckgen CLK_TOP_UNIVPLL2_D8>;
-+              clock-names = "nfi_clk",
-+                            "ecc_clk",
-+                            "snfi_clk",
-+                            "spinfi_sel",
-+                            "spinfi_parent_50m";
-+              nand-ecc-mode = "hw";
-+              status = "disabled";
-+      };
-+
-       timer {
-               compatible = "arm,armv8-timer";
-               interrupt-parent = <&gic>;
diff --git a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch b/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch
deleted file mode 100644 (file)
index 6999e5e..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-From e5745143a2984cf44fbfc0b3aedb49e57873f109 Mon Sep 17 00:00:00 2001
-From: Sam Shih <sam.shih@mediatek.com>
-Date: Mon, 20 Apr 2020 17:17:04 +0800
-Subject: [PATCH 3/3] configs: enable mtd and mtk_spi_nand in defconfig
-
-This patch enable mtk and mtk_spi_nand in mt7622_rfb defconfig
-
-Signed-off-by: Sam Shih <sam.shih@mediatek.com>
----
- configs/mt7622_rfb_defconfig | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/configs/mt7622_rfb_defconfig
-+++ b/configs/mt7622_rfb_defconfig
-@@ -13,6 +13,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7622-rfb"
- CONFIG_SYS_PROMPT="MT7622> "
- CONFIG_CMD_BOOTMENU=y
- CONFIG_CMD_MMC=y
-+CONFIG_CMD_NAND=y
- CONFIG_CMD_PCI=y
- CONFIG_CMD_SF_TEST=y
- CONFIG_CMD_PING=y
diff --git a/package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch b/package/boot/uboot-mediatek/patches/005-update-bpir2-defconfig.patch
deleted file mode 100644 (file)
index b750dda..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
---- a/configs/mt7623n_bpir2_defconfig
-+++ b/configs/mt7623n_bpir2_defconfig
-@@ -51,5 +51,15 @@ CONFIG_SYSRESET=y
- CONFIG_SYSRESET_WATCHDOG=y
- CONFIG_TIMER=y
- CONFIG_MTK_TIMER=y
-+CONFIG_CMD_BOOTZ=y
-+CONFIG_OF_LIBFDT_OVERLAY=y
-+#enables savenenv-command
-+CONFIG_ENV_IS_IN_FAT=y
-+CONFIG_ENV_FAT_INTERFACE="mmc"
-+CONFIG_ENV_FAT_DEVICE_AND_PART=":2"
-+CONFIG_ENV_FAT_FILE="uboot.env"
-+CONFIG_CMD_ASKENV=y
-+CONFIG_ENV_SIZE=0x2000
-+CONFIG_CMD_SETEXPR=y
- CONFIG_WDT_MTK=y
- CONFIG_LZMA=y
diff --git a/package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch b/package/boot/uboot-mediatek/patches/006-cmd-button-return-button-status.patch
deleted file mode 100644 (file)
index a413688..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-From a6bfd71a96201127836d59736abcb54dc2d5e1a5 Mon Sep 17 00:00:00 2001
-From: Heinrich Schuchardt <xypron.glpk@gmx.de>
-Date: Mon, 14 Sep 2020 12:50:56 +0200
-Subject: [PATCH] cmd/button: return button status
-
-To make the button command useful in a shell script it should return the
-status of the button:
-
-* 0 (true) - pressed, on
-* 1 (false) - not pressed, off
-
-The button command takes only one argument. Correct maxargs.
-
-Adjust the Python unit test.
-
-Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
-Reviewed-by: Philippe Reynes <philippe.reynes@softathome.com>
----
- cmd/button.c                 |  4 ++--
- test/py/tests/test_button.py | 34 ++++++++++++++++++++++++++--------
- 2 files changed, 28 insertions(+), 10 deletions(-)
-
---- a/cmd/button.c
-+++ b/cmd/button.c
-@@ -75,11 +75,11 @@ int do_button(struct cmd_tbl *cmdtp, int
-       ret = show_button_state(dev);
--      return 0;
-+      return !ret;
- }
- U_BOOT_CMD(
--      button, 4, 1, do_button,
-+      button, 2, 1, do_button,
-       "manage buttons",
-       "<button_label> \tGet button state\n"
-       "button list\t\tShow a list of buttons"
diff --git a/package/boot/uboot-mediatek/patches/007-env-readmem.patch b/package/boot/uboot-mediatek/patches/007-env-readmem.patch
deleted file mode 100644 (file)
index a8c88a2..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
---- a/cmd/Kconfig
-+++ b/cmd/Kconfig
-@@ -571,6 +571,12 @@ config CMD_ENV_EXISTS
-         Check if a variable is defined in the environment for use in
-         shell scripting.
-+config CMD_ENV_READMEM
-+      bool "env readmem"
-+      default y
-+      help
-+        Store memory content into environment variable.
-+
- config CMD_ENV_CALLBACK
-       bool "env callbacks - print callbacks and their associated variables"
-       help
---- a/cmd/nvedit.c
-+++ b/cmd/nvedit.c
-@@ -469,6 +469,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in
- }
- #endif
-+#if defined(CONFIG_CMD_ENV_READMEM)
-+int do_env_readmem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
-+{
-+      char varstr[CONFIG_SYS_CBSIZE];
-+      const void *buf;
-+      char *local_args[4];
-+      ulong addr, bytes = 6;
-+      int hexdump = 0;
-+
-+      /*
-+       * Check the syntax:
-+       *
-+       * readmem [-b] name address [size]
-+       */
-+      if (argc < 3)
-+              return CMD_RET_USAGE;
-+
-+      local_args[0] = argv[0];
-+
-+      if (!strncmp(argv[1], "-b", 3))
-+              hexdump = 1;
-+
-+      local_args[1] = argv[hexdump + 1];
-+      local_args[2] = varstr;
-+      local_args[3] = NULL;
-+
-+      addr = simple_strtoul(argv[hexdump + 2], NULL, 16);
-+
-+      if (!hexdump)
-+              bytes = simple_strtoul(argv[hexdump + 3], NULL, 16);
-+
-+      if (bytes < 1)
-+              return 1;
-+
-+      if ((hexdump * 3) * bytes >= CONFIG_SYS_CBSIZE)
-+              return 1;
-+
-+      buf = map_sysmem(addr, bytes);
-+      if (!buf)
-+              return 1;
-+
-+      if (hexdump) {
-+              sprintf(varstr, "%pM", buf);
-+      } else {
-+              memcpy(varstr, buf, bytes);
-+              varstr[bytes] = '\0';
-+      }
-+      unmap_sysmem(buf);
-+
-+      /* Continue calling setenv code */
-+      return _do_env_set(flag, 3, local_args, H_INTERACTIVE);
-+}
-+#endif
-+
- #if defined(CONFIG_CMD_ENV_CALLBACK)
- static int print_static_binding(const char *var_name, const char *callback_name,
-                               void *priv)
-@@ -1373,6 +1427,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, "", ""),
-+#if defined(CONFIG_CMD_ENV_READMEM)
-+      U_BOOT_CMD_MKENT(readmem, CONFIG_SYS_MAXARGS, 3, do_env_readmem, "", ""),
-+#endif
- #if defined(CONFIG_CMD_RUN)
-       U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
- #endif
-@@ -1461,6 +1518,9 @@ static char env_help_text[] =
- #if defined(CONFIG_CMD_NVEDIT_EFI)
-       "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n"
- #endif
-+#if defined(CONFIG_CMD_ENV_READMEM)
-+      "env readmem [-b] name address size - read variable from memory\n"
-+#endif
- #if defined(CONFIG_CMD_RUN)
-       "env run var [...] - run commands in an environment variable\n"
- #endif
-@@ -1570,6 +1630,17 @@ U_BOOT_CMD(
- );
- #endif
-+#if defined(CONFIG_CMD_ENV_READMEM)
-+U_BOOT_CMD_COMPLETE(
-+      readmem,        CONFIG_SYS_MAXARGS,     3,      do_env_readmem,
-+      "get environment variable from memory address",
-+      "name [-b] address size\n"
-+      "    - store memory address to env variable\n"
-+      "      \"-b\": read binary ethaddr",
-+      var_complete
-+);
-+#endif
-+
- #if defined(CONFIG_CMD_RUN)
- U_BOOT_CMD_COMPLETE(
-       run,    CONFIG_SYS_MAXARGS,     1,      do_run,
diff --git a/package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch b/package/boot/uboot-mediatek/patches/008-bootmenu-custom-title.patch
deleted file mode 100644 (file)
index 32f26ec..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
---- a/cmd/bootmenu.c
-+++ b/cmd/bootmenu.c
-@@ -38,6 +38,7 @@ struct bootmenu_data {
-       int active;                     /* active menu entry */
-       int count;                      /* total count of menu entries */
-       struct bootmenu_entry *first;   /* first menu entry */
-+      char *mtitle;                   /* custom menu title */
- };
- enum bootmenu_key {
-@@ -380,7 +381,12 @@ static void menu_display_statusline(stru
-       printf(ANSI_CURSOR_POSITION, 1, 1);
-       puts(ANSI_CLEAR_LINE);
-       printf(ANSI_CURSOR_POSITION, 2, 1);
--      puts("  *** U-Boot Boot Menu ***");
-+
-+      if (menu->mtitle)
-+              puts(menu->mtitle);
-+      else
-+              puts("  *** U-Boot Boot Menu ***");
-+
-       puts(ANSI_CLEAR_LINE_TO_END);
-       printf(ANSI_CURSOR_POSITION, 3, 1);
-       puts(ANSI_CLEAR_LINE);
-@@ -434,6 +440,7 @@ static void bootmenu_show(int delay)
-               return;
-       }
-+      bootmenu->mtitle = env_get("bootmenu_title");
-       for (iter = bootmenu->first; iter; iter = iter->next) {
-               if (!menu_item_add(menu, iter->key, iter))
-                       goto cleanup;
diff --git a/package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/009-mt7622-generic-reset-button-ignore-env.patch
deleted file mode 100644 (file)
index 037bbb8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
---- a/board/mediatek/mt7622/mt7622_rfb.c
-+++ b/board/mediatek/mt7622/mt7622_rfb.c
-@@ -6,9 +6,15 @@
- #include <common.h>
- #include <config.h>
-+#include <dm.h>
-+#include <button.h>
- #include <env.h>
- #include <init.h>
-+#ifndef CONFIG_RESET_BUTTON_LABEL
-+#define CONFIG_RESET_BUTTON_LABEL "reset"
-+#endif
-+
- DECLARE_GLOBAL_DATA_PTR;
- int board_init(void)
-@@ -19,7 +25,15 @@ int board_init(void)
- int board_late_init(void)
- {
--      gd->env_valid = 1; //to load environment variable from persistent store
-+      struct udevice *dev;
-+      int ret;
-+
-+      ret = !!button_get_by_label(CONFIG_RESET_BUTTON_LABEL, &dev);
-+
-+      if (!ret)
-+              ret = !button_get_state(dev);
-+
-+      gd->env_valid = ret; //to load environment variable from persistent store
-       env_relocate();
-       return 0;
- }
diff --git a/package/boot/uboot-mediatek/patches/010-no-binman.patch b/package/boot/uboot-mediatek/patches/010-no-binman.patch
deleted file mode 100644 (file)
index 7071a6c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
---- a/Makefile
-+++ b/Makefile
-@@ -1716,6 +1716,10 @@ u-boot-elf.lds: arch/u-boot-elf.lds prep
- ifeq ($(CONFIG_SPL),y)
- spl/u-boot-spl-mtk.bin: spl/u-boot-spl
-+OBJCOPYFLAGS_u-boot-mtk.bin = -I binary -O binary \
-+                      --pad-to=$(CONFIG_SPL_PAD_TO) --gap-fill=0xff
-+u-boot-mtk.bin: u-boot.img spl/u-boot-spl-mtk.bin FORCE
-+      $(call if_changed,pad_cat)
- else
- MKIMAGEFLAGS_u-boot-mtk.bin = -T mtk_image \
-       -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_TEXT_BASE) \
---- a/arch/arm/mach-mediatek/Kconfig
-+++ b/arch/arm/mach-mediatek/Kconfig
-@@ -36,7 +36,6 @@ config TARGET_MT7629
-       bool "MediaTek MT7629 SoC"
-       select CPU_V7A
-       select SPL
--      select BINMAN
-       help
-         The MediaTek MT7629 is a ARM-based SoC with a dual-core Cortex-A7
-         including DDR3, crypto engine, 3x3 11n/ac Wi-Fi, Gigabit Ethernet,
diff --git a/package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch b/package/boot/uboot-mediatek/patches/010-update-u7623-defconfig.patch
deleted file mode 100644 (file)
index 37d1b6a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/configs/mt7623a_unielec_u7623_02_defconfig
-+++ b/configs/mt7623a_unielec_u7623_02_defconfig
-@@ -52,3 +52,12 @@ CONFIG_TIMER=y
- CONFIG_MTK_TIMER=y
- CONFIG_WDT_MTK=y
- CONFIG_LZMA=y
-+CONFIG_CMD_BOOTZ=y
-+CONFIG_OF_LIBFDT_OVERLAY=y
-+#enables savenenv-command
-+CONFIG_ENV_IS_IN_FAT=y
-+CONFIG_ENV_FAT_INTERFACE="mmc"
-+CONFIG_ENV_FAT_DEVICE_AND_PART="0:2"
-+CONFIG_ENV_FAT_FILE="uboot.env"
-+CONFIG_CMD_ASKENV=y
-+CONFIG_CMD_SETEXPR=y
diff --git a/package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch b/package/boot/uboot-mediatek/patches/015-update-bananapi-bpi-r64-device-tree.patch
deleted file mode 100644 (file)
index dd4b94e..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
---- a/arch/arm/dts/mt7622-bananapi-bpi-r64.dts
-+++ b/arch/arm/dts/mt7622-bananapi-bpi-r64.dts
-@@ -20,6 +20,7 @@
-       aliases {
-               spi0 = &snfi;
-+              ethernet0 = &eth;
-       };
-       memory@40000000 {
-@@ -27,6 +29,42 @@
-               reg = <0x40000000 0x40000000>;
-       };
-+      gpio-keys {
-+              compatible = "gpio-keys";
-+
-+              reset {
-+                      label = "reset";
-+                      gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
-+              };
-+
-+              wps {
-+                      label = "wps";
-+                      gpios = <&gpio 102 GPIO_ACTIVE_LOW>;
-+              };
-+      };
-+
-+      leds {
-+              compatible = "gpio-leds";
-+/*
-+ *            red {
-+ *                    label = "bpi-r64:pio:red";
-+ *                    gpios = <&gpio 88 GPIO_ACTIVE_HIGH>;
-+ *                    default-state = "off";
-+ *            };
-+ */
-+              green {
-+                      label = "bpi-r64:pio:green";
-+                      gpios = <&gpio 89 GPIO_ACTIVE_HIGH>;
-+                      default-state = "off";
-+              };
-+
-+              blue {
-+                      label = "bpi-r64:pio:blue";
-+                      gpios = <&gpio 85 GPIO_ACTIVE_LOW>;
-+                      default-state = "off";
-+              };
-+      };
-+
-       reg_1p8v: regulator-1p8v {
-               compatible = "regulator-fixed";
-               regulator-name = "fixed-1.8V";
-@@ -139,11 +177,12 @@
-       };
--      mmc1_pins_default: mmc1default {
-+      sd0_pins_default: sd0-pins-default {
-               mux {
-                       function = "sd";
--                      groups =  "sd_0";
-+                      groups = "sd_0";
-               };
-+
-               /* "I2S2_OUT, "I2S4_IN"", "I2S3_IN", "I2S2_IN",
-                *  "I2S4_OUT", "I2S3_OUT" are used as DAT0, DAT1,
-                *  DAT2, DAT3, CMD, CLK for SD respectively.
-@@ -164,7 +203,6 @@
-                       pins = "TXD3";
-                       bias-pull-up;
-               };
--
-       };
- };
-@@ -199,7 +237,7 @@
-       status = "okay";
-       bus-width = <8>;
-       max-frequency = <50000000>;
--      cap-sd-highspeed;
-+      cap-mmc-highspeed;
-       vmmc-supply = <&reg_3p3v>;
-       vqmmc-supply = <&reg_3p3v>;
-       non-removable;
-@@ -207,14 +245,15 @@
- &mmc1 {
-       pinctrl-names = "default";
--      pinctrl-0 = <&mmc1_pins_default>;
-+      pinctrl-0 = <&sd0_pins_default>;
-       status = "okay";
-       bus-width = <4>;
--      max-frequency = <50000000>;
-+      max-frequency = <20000000>;
-       cap-sd-highspeed;
-       r_smpl = <1>;
-       vmmc-supply = <&reg_3p3v>;
-       vqmmc-supply = <&reg_3p3v>;
-+      cd-gpios = <&gpio 81 GPIO_ACTIVE_LOW>;
- };
- &watchdog {
diff --git a/package/boot/uboot-mediatek/patches/016-fit-totalsize.patch b/package/boot/uboot-mediatek/patches/016-fit-totalsize.patch
deleted file mode 100644 (file)
index 72fb4ac..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
---- a/cmd/bootm.c
-+++ b/cmd/bootm.c
-@@ -227,6 +227,65 @@ U_BOOT_CMD(
- /* iminfo - print header info for a requested image */
- /*******************************************************************/
- #if defined(CONFIG_CMD_IMI)
-+#define SECTOR_SHIFT 9
-+static int image_totalsize(struct cmd_tbl *cmdtp, int flag, int argc,
-+                         char *const argv[], short int in_blocks)
-+{
-+      ulong addr;
-+      void *fit;
-+      int bsize, tsize, maxhdrsize;
-+      char buf[16];
-+
-+      if (argc >= 2)
-+              addr = simple_strtoul(argv[1], NULL, 16);
-+      else
-+              addr = image_load_addr;
-+
-+      fit = (void *)map_sysmem(addr, 0);
-+      tsize = fit_get_totalsize(fit);
-+      unmap_sysmem(fit);
-+      if (tsize == 0)
-+              return 1;
-+
-+      bsize = (tsize >> SECTOR_SHIFT) + ((tsize & ((1 << SECTOR_SHIFT) - 1))?1:0);
-+
-+      if (!in_blocks)
-+              snprintf(buf, sizeof(buf), "%x", tsize);
-+      else
-+              snprintf(buf, sizeof(buf), "%x", bsize);
-+
-+      if (argc >= 3)
-+              return env_set(argv[2], buf);
-+      else
-+              printf("%s\n", buf);
-+
-+      return 0;
-+}
-+
-+static int do_imsz(struct cmd_tbl *cmdtp, int flag, int argc,
-+                   char *const argv[])
-+{
-+      return image_totalsize(cmdtp, flag, argc, argv, 0);
-+}
-+
-+static int do_imszb(struct cmd_tbl *cmdtp, int flag, int argc,
-+                   char *const argv[])
-+{
-+      return image_totalsize(cmdtp, flag, argc, argv, 1);
-+}
-+
-+U_BOOT_CMD(
-+      imsz,   CONFIG_SYS_MAXARGS,     1,      do_imsz,
-+      "get image total size (in bytes)",
-+      "addr [maxhdrlen] [varname]\n"
-+);
-+
-+U_BOOT_CMD(
-+      imszb,  CONFIG_SYS_MAXARGS,     1,      do_imszb,
-+      "get image total size (in blocks)",
-+      "addr [maxhdrlen] [varname]\n"
-+);
-+
- static int do_iminfo(struct cmd_tbl *cmdtp, int flag, int argc,
-                    char *const argv[])
- {
---- a/common/image-fit.c
-+++ b/common/image-fit.c
-@@ -1878,6 +1878,51 @@ static const char *fit_get_image_type_pr
-       return "unknown";
- }
-+size_t fit_get_totalsize(const void *fit)
-+{
-+      int ret, ndepth, noffset, images_noffset;
-+      size_t data_size, hdrsize, img_total, max_size = 0;
-+      const void *data;
-+
-+      ret = fdt_check_header(fit);
-+      if (ret) {
-+              debug("Wrong FIT format: not a flattened device tree (err=%d)\n",
-+                        ret);
-+              return 0;
-+      }
-+
-+      hdrsize = fdt_totalsize(fit);
-+
-+      /* simple FIT with internal images */
-+      if (hdrsize > 0x1000)
-+              return hdrsize;
-+
-+      images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
-+      if (images_noffset < 0) {
-+              printf("Can't find images parent node '%s' (%s)\n",
-+              FIT_IMAGES_PATH, fdt_strerror(images_noffset));
-+              return 0;
-+      }
-+
-+      for (ndepth = 0,
-+           noffset = fdt_next_node(fit, images_noffset, &ndepth);
-+           (noffset >= 0) && (ndepth > 0);
-+           noffset = fdt_next_node(fit, noffset, &ndepth)) {
-+              if (ndepth == 1) {
-+                      ret = fit_image_get_data_and_size(fit, noffset, &data, &data_size);
-+                      if (ret)
-+                              return 0;
-+
-+                      img_total = data_size + (data - fit);
-+
-+                      max_size = (max_size > img_total) ? max_size : img_total;
-+              }
-+      }
-+
-+      return max_size;
-+}
-+
-+
- int fit_image_load(bootm_headers_t *images, ulong addr,
-                  const char **fit_unamep, const char **fit_uname_configp,
-                  int arch, int image_type, int bootstage_id,
---- a/include/image.h
-+++ b/include/image.h
-@@ -1027,6 +1027,7 @@ int fit_parse_subimage(const char *spec,
-               ulong *addr, const char **image_name);
- int fit_get_subimage_count(const void *fit, int images_noffset);
-+size_t fit_get_totalsize(const void *fit);
- void fit_print_contents(const void *fit);
- void fit_image_print(const void *fit, int noffset, const char *p);
diff --git a/package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch b/package/boot/uboot-mediatek/patches/017-add-bananapi_bpi-r64_defconfigs.patch
deleted file mode 100644 (file)
index 3624c01..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
---- /dev/null
-+++ b/configs/mt7622_bananapi_bpi-r64-sdmmc_defconfig
-@@ -0,0 +1,123 @@
-+CONFIG_ARM=y
-+CONFIG_POSITION_INDEPENDENT=y
-+CONFIG_ARCH_MEDIATEK=y
-+CONFIG_SYS_TEXT_BASE=0x41e00000
-+CONFIG_SYS_MALLOC_F_LEN=0x4000
-+CONFIG_USE_DEFAULT_ENV_FILE=y
-+CONFIG_BOARD_LATE_INIT=y
-+CONFIG_BOOTP_SEND_HOSTNAME=y
-+CONFIG_NR_DRAM_BANKS=1
-+CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64"
-+CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-sdmmc_env"
-+CONFIG_NET_RANDOM_ETHADDR=y
-+CONFIG_SMBIOS_PRODUCT_NAME=""
-+CONFIG_AUTOBOOT_KEYED=y
-+CONFIG_BOOTDELAY=30
-+CONFIG_AUTOBOOT_MENU_SHOW=y
-+CONFIG_CFB_CONSOLE_ANSI=y
-+CONFIG_BUTTON=y
-+CONFIG_BUTTON_GPIO=y
-+CONFIG_CMD_ENV_FLAGS=y
-+CONFIG_FIT=y
-+CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
-+CONFIG_LED=y
-+CONFIG_LED_BLINK=y
-+CONFIG_LED_GPIO=y
-+CONFIG_LOGLEVEL=7
-+CONFIG_LOG=y
-+CONFIG_DEFAULT_FDT_FILE="mt7622-bananapi-bpi-r64"
-+CONFIG_SYS_PROMPT="MT7622> "
-+CONFIG_CMD_BOOTMENU=y
-+CONFIG_CMD_BOOTP=y
-+CONFIG_CMD_BUTTON=y
-+CONFIG_CMD_CDP=y
-+CONFIG_CMD_DHCP=y
-+CONFIG_CMD_DNS=y
-+CONFIG_CMD_ECHO=y
-+CONFIG_CMD_ENV_READMEM=y
-+CONFIG_CMD_ERASEENV=y
-+CONFIG_CMD_EXT4=y
-+CONFIG_CMD_FAT=y
-+CONFIG_CMD_FS_GENERIC=y
-+CONFIG_CMD_FS_UUID=y
-+CONFIG_CMD_GPIO=y
-+CONFIG_CMD_GPT=y
-+CONFIG_CMD_HASH=y
-+CONFIG_CMD_ITEST=y
-+CONFIG_CMD_LED=y
-+CONFIG_CMD_LICENSE=y
-+CONFIG_CMD_LINK_LOCAL=y
-+CONFIG_CMD_MBR=y
-+CONFIG_CMD_MMC=y
-+CONFIG_CMD_PCI=y
-+CONFIG_CMD_SF_TEST=y
-+CONFIG_CMD_PING=y
-+CONFIG_CMD_PXE=y
-+CONFIG_CMD_SMC=y
-+CONFIG_CMD_TFTPBOOT=y
-+CONFIG_CMD_TFTPSRV=y
-+CONFIG_CMD_ASKENV=y
-+CONFIG_CMD_PART=y
-+CONFIG_CMD_PSTORE=y
-+CONFIG_CMD_RARP=y
-+CONFIG_CMD_SETEXPR=y
-+CONFIG_CMD_SLEEP=y
-+CONFIG_CMD_SNTP=y
-+CONFIG_CMD_SOURCE=y
-+CONFIG_CMD_USB=y
-+CONFIG_CMD_UUID=y
-+CONFIG_DISPLAY_CPUINFO=y
-+CONFIG_DM_MMC=y
-+CONFIG_DM_REGULATOR=y
-+CONFIG_DM_REGULATOR_FIXED=y
-+CONFIG_DM_REGULATOR_GPIO=y
-+CONFIG_DM_USB=y
-+CONFIG_HUSH_PARSER=y
-+CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
-+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
-+CONFIG_ENV_IS_IN_MMC=y
-+CONFIG_SYS_MMC_ENV_DEV=1
-+CONFIG_ENV_OFFSET=0x400000
-+CONFIG_ENV_OFFSET_REDUND=0x480000
-+CONFIG_ENV_SIZE=0x80000
-+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
-+CONFIG_VERSION_VARIABLE=y
-+CONFIG_PARTITION_UUIDS=y
-+CONFIG_NETCONSOLE=y
-+CONFIG_REGMAP=y
-+CONFIG_SYSCON=y
-+CONFIG_CLK=y
-+CONFIG_PHY_FIXED=y
-+CONFIG_DM_ETH=y
-+CONFIG_MEDIATEK_ETH=y
-+CONFIG_PCI=y
-+CONFIG_DM_PCI=y
-+CONFIG_PCIE_MEDIATEK=y
-+CONFIG_PINCTRL=y
-+CONFIG_PINCONF=y
-+CONFIG_PINCTRL_MT7622=y
-+CONFIG_POWER_DOMAIN=y
-+CONFIG_PRE_CONSOLE_BUFFER=y
-+CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
-+CONFIG_MTK_POWER_DOMAIN=y
-+CONFIG_RAM=y
-+CONFIG_DM_SERIAL=y
-+CONFIG_MTK_SERIAL=y
-+CONFIG_MMC=y
-+CONFIG_MMC_DEFAULT_DEV=1
-+CONFIG_MMC_MTK=y
-+CONFIG_SUPPORT_EMMC_BOOT=y
-+CONFIG_SYSRESET_WATCHDOG=y
-+CONFIG_TIMER=y
-+CONFIG_MTK_TIMER=y
-+CONFIG_WDT_MTK=y
-+CONFIG_LZO=y
-+CONFIG_ZSTD=y
-+CONFIG_HEXDUMP=y
-+CONFIG_RANDOM_UUID=y
-+CONFIG_REGEX=y
-+CONFIG_USB=y
-+CONFIG_USB_HOST=y
-+CONFIG_USB_XHCI_HCD=y
-+CONFIG_USB_XHCI_MTK=y
-+CONFIG_USB_STORAGE=y
---- /dev/null
-+++ b/bananapi_bpi-r64-sdmmc_env
-@@ -0,0 +1,54 @@
-+ipaddr=192.168.1.1
-+serverip=192.168.1.254
-+loadaddr=0x4007ff28
-+bootcmd=run boot_sdmmc
-+bootargs=earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512 root=/dev/mmcblk1p6
-+bootdelay=0
-+bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb
-+bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb
-+bootfile_emmcbl3=openwrt-mediatek-mt7622-bananapi_bpi-r64-boot-emmc.img
-+bootfile_emmcbl2=openwrt-mediatek-mt7622-bananapi_bpi-r64-bl2-emmc.bin
-+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu
-+bootmenu_default=0
-+bootmenu_delay=0
-+bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[SD card]\e[0m
-+bootmenu_0=0. Initialize environment.=run _firstboot
-+bootmenu_0d=0. Run default boot command.=run boot_default
-+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
-+bootmenu_2=2. Boot production system from SD card.=run boot_production ; run bootmenu_confirm_return
-+bootmenu_3=3. Boot recovery system from SD card.=run boot_recovery ; run bootmenu_confirm_return
-+bootmenu_4=4. Load production system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_5=5. Load recovery system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_6=\e[31m6. Install bootloader and recovery to eMMC.\e[0m=run emmc_init ; run bootmenu_confirm_return
-+bootmenu_7=7. Reboot.=reset
-+bootmenu_8=8. Reset all settings to factory defaults.=run reset_factory ; reset
-+boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
-+boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
-+boot_production=led bpi-r64:pio:green on ; run sdmmc_read_production && bootm $loadaddr
-+boot_recovery=led bpi-r64:pio:green off ; run sdmmc_read_recovery && bootm $loadaddr
-+boot_sdmmc=run boot_production ; run boot_recovery
-+boot_tftp_forever=led bpi-r64:pio:blue on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
-+boot_tftp_production=tftpboot $loadaddr $bootfile_upg && run sdmmc_write_production ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && run sdmmc_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
-+emmc_write_bl2=mmc dev 0 1 && mmc partconf 0 1 1 1 && mmc erase 0x0 0x400 && mmc write $loadaddr 0x0 0x100 ; mmc partconf 0 1 1 0
-+emmc_write_hdr=mmc dev 0 0 && mmc erase 0x0 0x40 && mmc write $loadaddr 0x0 0x40
-+emmc_write_bl3=mmc dev 0 0 && mmc erase 0x1000 0x800 && mmc write $loadaddr 0x1000 0x800
-+emmc_write_recovery=iminfo $loadaddr && mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_write_vol
-+emmc_init=run sdmmc_read_emmc_bl2 && run emmc_write_bl2 && run sdmmc_read_emmc_hdr && run emmc_write_hdr && run sdmmc_read_emmc_bl3 && run emmc_write_bl3 && run sdmmc_read_recovery && run emmc_write_recovery ; env default bootcmd ; saveenv ; saveenv
-+sdmmc_write_production=iminfo $fileaddr && mmc dev 1 && part start mmc 1 $part_default part_addr && part size mmc 1 $part_default part_size && run mmc_write_vol
-+sdmmc_write_recovery=iminfo $fileaddr && mmc dev 1 && part start mmc 1 $part_recovery part_addr && part size mmc 1 $part_recovery part_size && run mmc_write_vol
-+sdmmc_read_production=mmc dev 1 && part start mmc 1 $part_default part_addr && part size mmc 1 $part_default part_size && run mmc_read_vol
-+sdmmc_read_recovery=mmc dev 1 && part start mmc 1 $part_recovery part_addr && part size mmc 1 $part_recovery part_size && run mmc_read_vol
-+sdmmc_read_emmc_hdr=mmc dev 1 && mmc read $loadaddr 0x100 0x40
-+sdmmc_read_emmc_bl2=mmc dev 1 && mmc read $loadaddr 0x200 0x100
-+sdmmc_read_emmc_bl3=mmc dev 1 && mmc read $loadaddr 0x800 0x800
-+mmc_write_vol=imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc erase 0x$part_addr 0x$part_size && mmc write $loadaddr 0x$part_addr 0x$image_size
-+mmc_read_vol=mmc read $loadaddr $part_addr 0x8 && imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc read $loadaddr 0x$part_addr 0x$image_size
-+part_default=production
-+part_recovery=recovery
-+reset_factory=eraseenv && reset
-+_init_env=setenv _init_env ; setenv _create_env ; saveenv ; saveenv
-+_firstboot=setenv _firstboot ; led bpi-r64:pio:blue on ; run _switch_to_menu ; run _init_env ; run boot_first
-+_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
-+_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
---- /dev/null
-+++ b/configs/mt7622_bananapi_bpi-r64-emmc_defconfig
-@@ -0,0 +1,123 @@
-+CONFIG_ARM=y
-+CONFIG_POSITION_INDEPENDENT=y
-+CONFIG_ARCH_MEDIATEK=y
-+CONFIG_SYS_TEXT_BASE=0x41e00000
-+CONFIG_SYS_MALLOC_F_LEN=0x4000
-+CONFIG_USE_DEFAULT_ENV_FILE=y
-+CONFIG_BOARD_LATE_INIT=y
-+CONFIG_BOOTP_SEND_HOSTNAME=y
-+CONFIG_NR_DRAM_BANKS=1
-+CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64"
-+CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-emmc_env"
-+CONFIG_NET_RANDOM_ETHADDR=y
-+CONFIG_SMBIOS_PRODUCT_NAME=""
-+CONFIG_AUTOBOOT_KEYED=y
-+CONFIG_BOOTDELAY=30
-+CONFIG_AUTOBOOT_MENU_SHOW=y
-+CONFIG_CFB_CONSOLE_ANSI=y
-+CONFIG_BUTTON=y
-+CONFIG_BUTTON_GPIO=y
-+CONFIG_CMD_ENV_FLAGS=y
-+CONFIG_FIT=y
-+CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
-+CONFIG_LED=y
-+CONFIG_LED_BLINK=y
-+CONFIG_LED_GPIO=y
-+CONFIG_LOGLEVEL=7
-+CONFIG_LOG=y
-+CONFIG_DEFAULT_FDT_FILE="mt7622-bananapi-bpi-r64"
-+CONFIG_SYS_PROMPT="MT7622> "
-+CONFIG_CMD_BOOTMENU=y
-+CONFIG_CMD_BOOTP=y
-+CONFIG_CMD_BUTTON=y
-+CONFIG_CMD_CDP=y
-+CONFIG_CMD_DHCP=y
-+CONFIG_CMD_DNS=y
-+CONFIG_CMD_ECHO=y
-+CONFIG_CMD_ENV_READMEM=y
-+CONFIG_CMD_ERASEENV=y
-+CONFIG_CMD_EXT4=y
-+CONFIG_CMD_FAT=y
-+CONFIG_CMD_FS_GENERIC=y
-+CONFIG_CMD_FS_UUID=y
-+CONFIG_CMD_GPIO=y
-+CONFIG_CMD_GPT=y
-+CONFIG_CMD_HASH=y
-+CONFIG_CMD_ITEST=y
-+CONFIG_CMD_LED=y
-+CONFIG_CMD_LICENSE=y
-+CONFIG_CMD_LINK_LOCAL=y
-+CONFIG_CMD_MBR=y
-+CONFIG_CMD_MMC=y
-+CONFIG_CMD_PCI=y
-+CONFIG_CMD_SF_TEST=y
-+CONFIG_CMD_PING=y
-+CONFIG_CMD_PXE=y
-+CONFIG_CMD_SMC=y
-+CONFIG_CMD_TFTPBOOT=y
-+CONFIG_CMD_TFTPSRV=y
-+CONFIG_CMD_ASKENV=y
-+CONFIG_CMD_PART=y
-+CONFIG_CMD_PSTORE=y
-+CONFIG_CMD_RARP=y
-+CONFIG_CMD_SETEXPR=y
-+CONFIG_CMD_SLEEP=y
-+CONFIG_CMD_SNTP=y
-+CONFIG_CMD_SOURCE=y
-+CONFIG_CMD_USB=y
-+CONFIG_CMD_UUID=y
-+CONFIG_DISPLAY_CPUINFO=y
-+CONFIG_DM_MMC=y
-+CONFIG_DM_REGULATOR=y
-+CONFIG_DM_REGULATOR_FIXED=y
-+CONFIG_DM_REGULATOR_GPIO=y
-+CONFIG_DM_USB=y
-+CONFIG_HUSH_PARSER=y
-+CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
-+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
-+CONFIG_ENV_IS_IN_MMC=y
-+CONFIG_SYS_MMC_ENV_DEV=0
-+CONFIG_ENV_OFFSET=0x400000
-+CONFIG_ENV_OFFSET_REDUND=0x480000
-+CONFIG_ENV_SIZE=0x80000
-+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
-+CONFIG_VERSION_VARIABLE=y
-+CONFIG_PARTITION_UUIDS=y
-+CONFIG_NETCONSOLE=y
-+CONFIG_REGMAP=y
-+CONFIG_SYSCON=y
-+CONFIG_CLK=y
-+CONFIG_PHY_FIXED=y
-+CONFIG_DM_ETH=y
-+CONFIG_MEDIATEK_ETH=y
-+CONFIG_PCI=y
-+CONFIG_DM_PCI=y
-+CONFIG_PCIE_MEDIATEK=y
-+CONFIG_PINCTRL=y
-+CONFIG_PINCONF=y
-+CONFIG_PINCTRL_MT7622=y
-+CONFIG_POWER_DOMAIN=y
-+CONFIG_PRE_CONSOLE_BUFFER=y
-+CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
-+CONFIG_MTK_POWER_DOMAIN=y
-+CONFIG_RAM=y
-+CONFIG_DM_SERIAL=y
-+CONFIG_MTK_SERIAL=y
-+CONFIG_MMC=y
-+CONFIG_MMC_DEFAULT_DEV=0
-+CONFIG_MMC_MTK=y
-+CONFIG_SUPPORT_EMMC_BOOT=y
-+CONFIG_SYSRESET_WATCHDOG=y
-+CONFIG_TIMER=y
-+CONFIG_MTK_TIMER=y
-+CONFIG_WDT_MTK=y
-+CONFIG_LZO=y
-+CONFIG_ZSTD=y
-+CONFIG_HEXDUMP=y
-+CONFIG_RANDOM_UUID=y
-+CONFIG_REGEX=y
-+CONFIG_USB=y
-+CONFIG_USB_HOST=y
-+CONFIG_USB_XHCI_HCD=y
-+CONFIG_USB_XHCI_MTK=y
-+CONFIG_USB_STORAGE=y
---- /dev/null
-+++ b/bananapi_bpi-r64-emmc_env
-@@ -0,0 +1,43 @@
-+ipaddr=192.168.1.1
-+serverip=192.168.1.254
-+loadaddr=0x4007ff28
-+bootcmd=run boot_emmc
-+bootargs=earlycon=uart8250,mmio32,0x11002000 console=ttyS0,115200n1 swiotlb=512 root=/dev/mmcblk0p5
-+bootdelay=0
-+bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb
-+bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb
-+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu
-+bootmenu_default=0
-+bootmenu_delay=0
-+bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[eMMC]\e[0m
-+bootmenu_0=0. Initialize environment.=run _firstboot
-+bootmenu_0d=0. Run default boot command.=run boot_default
-+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
-+bootmenu_2=2. Boot production system from eMMC.=run boot_production ; run bootmenu_confirm_return
-+bootmenu_3=3. Boot recovery system from eMMC.=run boot_recovery ; run bootmenu_confirm_return
-+bootmenu_4=4. Load production system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_5=5. Load recovery system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_6=6. Reboot.=reset
-+bootmenu_7=7. Reset all settings to factory defaults.=run reset_factory ; reset
-+boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
-+boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
-+boot_production=led bpi-r64:pio:green on ; run emmc_read_production && bootm $loadaddr
-+boot_recovery=led bpi-r64:pio:green off ; run emmc_read_recovery && bootm $loadaddr
-+boot_emmc=run boot_production ; run boot_recovery
-+boot_tftp_forever=led bpi-r64:pio:blue on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
-+boot_tftp_production=tftpboot $loadaddr $bootfile_upg && run emmc_write_production ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && run emmc_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
-+emmc_write_production=iminfo $fileaddr && mmc dev 0 && part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_write_vol
-+emmc_write_recovery=iminfo $fileaddr && mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_write_vol
-+emmc_read_production=mmc dev 0 && part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_read_vol
-+emmc_read_recovery=mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_read_vol
-+mmc_write_vol=imszb $fileaddr image_size && test 0x$image_size -le 0x$part_size && mmc erase 0x$part_addr 0x$part_size && mmc write $fileaddr 0x$part_addr 0x$image_size
-+mmc_read_vol=mmc read $loadaddr $part_addr 0x8 && imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc read $loadaddr 0x$part_addr 0x$image_size
-+part_default=production
-+part_recovery=recovery
-+reset_factory=eraseenv && reset
-+_init_env=setenv _init_env ; setenv _create_env ; saveenv ; saveenv
-+_firstboot=setenv _firstboot ; led bpi-r64:pio:blue on ; run _switch_to_menu ; run _init_env ; run boot_first
-+_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
-+_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
diff --git a/package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch b/package/boot/uboot-mediatek/patches/020-add-linksys-e8450.patch
deleted file mode 100644 (file)
index 711c1e6..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
---- /dev/null
-+++ b/configs/mt7622_linksys_e8450_defconfig
-@@ -0,0 +1,130 @@
-+CONFIG_ARM=y
-+CONFIG_POSITION_INDEPENDENT=y
-+CONFIG_ARCH_MEDIATEK=y
-+CONFIG_SYS_TEXT_BASE=0x41e00000
-+CONFIG_SYS_MALLOC_F_LEN=0x4000
-+CONFIG_USE_DEFAULT_ENV_FILE=y
-+CONFIG_BOARD_LATE_INIT=y
-+CONFIG_BOOTP_SEND_HOSTNAME=y
-+CONFIG_DEFAULT_ENV_FILE="linksys_e8450_env"
-+CONFIG_NR_DRAM_BANKS=1
-+CONFIG_DEFAULT_DEVICE_TREE="mt7622-linksys-e8450-ubi"
-+CONFIG_SMBIOS_PRODUCT_NAME=""
-+CONFIG_AUTOBOOT_KEYED=y
-+CONFIG_BOOTDELAY=30
-+CONFIG_AUTOBOOT_MENU_SHOW=y
-+CONFIG_CFB_CONSOLE_ANSI=y
-+CONFIG_BUTTON=y
-+CONFIG_BUTTON_GPIO=y
-+CONFIG_CMD_ENV_FLAGS=y
-+CONFIG_FIT=y
-+CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
-+CONFIG_LED=y
-+CONFIG_LED_BLINK=y
-+CONFIG_LED_GPIO=y
-+CONFIG_LOGLEVEL=7
-+CONFIG_LOG=y
-+CONFIG_DEFAULT_FDT_FILE="mt7622-linksys-e8450"
-+CONFIG_SYS_PROMPT="MT7622> "
-+CONFIG_CMD_BOOTMENU=y
-+CONFIG_CMD_BOOTP=y
-+CONFIG_CMD_BUTTON=y
-+CONFIG_CMD_CDP=y
-+CONFIG_CMD_DHCP=y
-+CONFIG_CMD_DNS=y
-+CONFIG_CMD_ECHO=y
-+CONFIG_CMD_ENV_READMEM=y
-+CONFIG_CMD_ERASEENV=y
-+CONFIG_CMD_EXT4=y
-+CONFIG_CMD_FAT=y
-+CONFIG_CMD_FS_GENERIC=y
-+CONFIG_CMD_FS_UUID=y
-+CONFIG_CMD_GPIO=y
-+CONFIG_CMD_GPT=y
-+CONFIG_CMD_HASH=y
-+CONFIG_CMD_ITEST=y
-+CONFIG_CMD_LED=y
-+CONFIG_CMD_LICENSE=y
-+CONFIG_CMD_LINK_LOCAL=y
-+CONFIG_CMD_MBR=y
-+CONFIG_CMD_MTD=y
-+CONFIG_CMD_MTDPART=y
-+CONFIG_CMD_NAND=y
-+CONFIG_CMD_PCI=y
-+CONFIG_CMD_SF_TEST=y
-+CONFIG_CMD_PING=y
-+CONFIG_CMD_PXE=y
-+CONFIG_CMD_SMC=y
-+CONFIG_CMD_TFTPBOOT=y
-+CONFIG_CMD_TFTPSRV=y
-+CONFIG_CMD_UBI=y
-+CONFIG_CMD_UBI_RENAME=y
-+CONFIG_CMD_UBIFS=y
-+CONFIG_CMD_ASKENV=y
-+CONFIG_CMD_PART=y
-+CONFIG_CMD_PSTORE=y
-+CONFIG_CMD_RARP=y
-+CONFIG_CMD_SETEXPR=y
-+CONFIG_CMD_SLEEP=y
-+CONFIG_CMD_SNTP=y
-+CONFIG_CMD_SOURCE=y
-+CONFIG_CMD_USB=y
-+CONFIG_CMD_UUID=y
-+CONFIG_DISPLAY_CPUINFO=y
-+CONFIG_DM_REGULATOR=y
-+CONFIG_DM_REGULATOR_FIXED=y
-+CONFIG_DM_REGULATOR_GPIO=y
-+CONFIG_DM_USB=y
-+CONFIG_HUSH_PARSER=y
-+CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
-+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
-+CONFIG_ENV_IS_IN_UBI=y
-+CONFIG_ENV_UBI_PART="ubi"
-+CONFIG_ENV_UBI_VOLUME="ubootenv"
-+CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2"
-+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
-+CONFIG_VERSION_VARIABLE=y
-+CONFIG_PARTITION_UUIDS=y
-+CONFIG_NETCONSOLE=y
-+CONFIG_REGMAP=y
-+CONFIG_SYSCON=y
-+CONFIG_CLK=y
-+CONFIG_DM_MTD=y
-+CONFIG_PHY_FIXED=y
-+CONFIG_DM_ETH=y
-+CONFIG_MEDIATEK_ETH=y
-+CONFIG_PCI=y
-+CONFIG_MTD=y
-+CONFIG_MTD_RAW_NAND=y
-+CONFIG_MTD_UBI_FASTMAP=y
-+CONFIG_DM_PCI=y
-+CONFIG_PCIE_MEDIATEK=y
-+CONFIG_PINCTRL=y
-+CONFIG_PINCONF=y
-+CONFIG_PINCTRL_MT7622=y
-+CONFIG_POWER_DOMAIN=y
-+CONFIG_PRE_CONSOLE_BUFFER=y
-+CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
-+CONFIG_MTK_POWER_DOMAIN=y
-+CONFIG_RAM=y
-+CONFIG_DM_SERIAL=y
-+CONFIG_MTK_SERIAL=y
-+CONFIG_SPI=y
-+CONFIG_DM_SPI=y
-+CONFIG_MTK_SNFI_SPI=y
-+CONFIG_MTK_SPI_NAND=y
-+CONFIG_NAND_SUPPORT=y
-+CONFIG_SYSRESET_WATCHDOG=y
-+CONFIG_TIMER=y
-+CONFIG_MTK_TIMER=y
-+CONFIG_WDT_MTK=y
-+CONFIG_LZO=y
-+CONFIG_ZSTD=y
-+CONFIG_HEXDUMP=y
-+CONFIG_RANDOM_UUID=y
-+CONFIG_REGEX=y
-+CONFIG_USB=y
-+CONFIG_USB_HOST=y
-+CONFIG_USB_XHCI_HCD=y
-+CONFIG_USB_XHCI_MTK=y
-+CONFIG_USB_STORAGE=y
---- /dev/null
-+++ b/arch/arm/dts/mt7622-linksys-e8450-ubi.dts
-@@ -0,0 +1,206 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Copyright (c) 2019 MediaTek Inc.
-+ * Author: Sam Shih <sam.shih@mediatek.com>
-+ */
-+
-+/dts-v1/;
-+#include "mt7622.dtsi"
-+#include "mt7622-u-boot.dtsi"
-+
-+/ {
-+      #address-cells = <1>;
-+      #size-cells = <1>;
-+      model = "mt7622-linksys-e8450-ubi";
-+      compatible = "mediatek,mt7622", "linksys,e8450-ubi";
-+      chosen {
-+              stdout-path = &uart0;
-+              tick-timer = &timer0;
-+      };
-+
-+      aliases {
-+              spi0 = &snfi;
-+      };
-+
-+      gpio-keys {
-+              compatible = "gpio-keys";
-+
-+              factory {
-+                      label = "reset";
-+                      gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
-+              };
-+
-+              wps {
-+                      label = "wps";
-+                      gpios = <&gpio 102 GPIO_ACTIVE_LOW>;
-+              };
-+      };
-+
-+      gpio-leds {
-+              compatible = "gpio-leds";
-+
-+              led_power: power_blue {
-+                      label = "power:blue";
-+                      gpios = <&gpio 95 GPIO_ACTIVE_LOW>;
-+                      default-state = "on";
-+              };
-+
-+              power_orange {
-+                      label = "power:orange";
-+                      gpios = <&gpio 96 GPIO_ACTIVE_LOW>;
-+                      default-state = "off";
-+              };
-+
-+              inet_blue {
-+                      label = "inet:blue";
-+                      gpios = <&gpio 97 GPIO_ACTIVE_LOW>;
-+                      default-state = "off";
-+              };
-+
-+              inet_orange {
-+                      label = "inet:orange";
-+                      gpios = <&gpio 98 GPIO_ACTIVE_LOW>;
-+                      default-state = "off";
-+              };
-+      };
-+
-+      memory@40000000 {
-+              device_type = "memory";
-+              reg = <0x40000000 0x20000000>;
-+      };
-+
-+      reg_1p8v: regulator-1p8v {
-+              compatible = "regulator-fixed";
-+              regulator-name = "fixed-1.8V";
-+              regulator-min-microvolt = <1800000>;
-+              regulator-max-microvolt = <1800000>;
-+              regulator-boot-on;
-+              regulator-always-on;
-+      };
-+
-+      reg_3p3v: regulator-3p3v {
-+              compatible = "regulator-fixed";
-+              regulator-name = "fixed-3.3V";
-+              regulator-min-microvolt = <3300000>;
-+              regulator-max-microvolt = <3300000>;
-+              regulator-boot-on;
-+              regulator-always-on;
-+      };
-+
-+      reg_5v: regulator-5v {
-+              compatible = "regulator-fixed";
-+              regulator-name = "fixed-5V";
-+              regulator-min-microvolt = <5000000>;
-+              regulator-max-microvolt = <5000000>;
-+              regulator-boot-on;
-+              regulator-always-on;
-+      };
-+};
-+
-+&pcie {
-+      pinctrl-names = "default";
-+      pinctrl-0 = <&pcie0_pins>, <&pcie1_pins>;
-+      status = "okay";
-+
-+      pcie@0,0 {
-+              status = "okay";
-+      };
-+
-+      pcie@1,0 {
-+              status = "okay";
-+      };
-+};
-+
-+&pinctrl {
-+      pcie0_pins: pcie0-pins {
-+              mux {
-+                      function = "pcie";
-+                      groups = "pcie0_pad_perst",
-+                               "pcie0_1_waken",
-+                               "pcie0_1_clkreq";
-+              };
-+      };
-+
-+      pcie1_pins: pcie1-pins {
-+              mux {
-+                      function = "pcie";
-+                      groups = "pcie1_pad_perst",
-+                               "pcie1_0_waken",
-+                               "pcie1_0_clkreq";
-+              };
-+      };
-+
-+      snfi_pins: snfi-pins {
-+              mux {
-+                      function = "flash";
-+                      groups = "snfi";
-+              };
-+      };
-+
-+      uart0_pins: uart0 {
-+              mux {
-+                      function = "uart";
-+                      groups = "uart0_0_tx_rx" ;
-+              };
-+      };
-+
-+      watchdog_pins: watchdog-default {
-+              mux {
-+                      function = "watchdog";
-+                      groups = "watchdog";
-+              };
-+      };
-+};
-+
-+&snfi {
-+      pinctrl-names = "default";
-+      pinctrl-0 = <&snfi_pins>;
-+      status = "okay";
-+
-+      mediatek,bmt-v2;
-+
-+      spi-flash@0{
-+              compatible = "jedec,spi-nor";
-+              reg = <0>;
-+              u-boot,dm-pre-reloc;
-+      };
-+};
-+
-+&nandc {
-+      status = "okay";
-+};
-+
-+&uart0 {
-+      pinctrl-names = "default";
-+      pinctrl-0 = <&uart0_pins>;
-+      status = "okay";
-+};
-+
-+&watchdog {
-+      pinctrl-names = "default";
-+      pinctrl-0 = <&watchdog_pins>;
-+      status = "okay";
-+};
-+
-+&eth {
-+      status = "okay";
-+      mediatek,gmac-id = <0>;
-+      phy-mode = "sgmii";
-+      mediatek,switch = "mt7531";
-+      reset-gpios = <&gpio 54 GPIO_ACTIVE_HIGH>;
-+
-+      fixed-link {
-+              speed = <1000>;
-+              full-duplex;
-+      };
-+};
-+
-+&ssusb {
-+      vusb33-supply = <&reg_3p3v>;
-+      vbus-supply = <&reg_5v>;
-+      status = "okay";
-+};
-+
-+&u3phy {
-+      status = "okay";
-+};
---- a/arch/arm/dts/Makefile
-+++ b/arch/arm/dts/Makefile
-@@ -963,6 +963,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
-       mt7622-rfb.dtb \
-       mt7623a-unielec-u7623-02-emmc.dtb \
-       mt7622-bananapi-bpi-r64.dtb \
-+      mt7622-linksys-e8450-ubi.dtb \
-       mt7623n-bananapi-bpi-r2.dtb \
-       mt7629-rfb.dtb \
-       mt8512-bm1-emmc.dtb \
---- a/drivers/mtd/nandx/core/nand/device_spi.c
-+++ b/drivers/mtd/nandx/core/nand/device_spi.c
-@@ -150,6 +150,21 @@ static struct device_spi spi_nand[] = {
-               &spi_extend_cmds, 0xff, 0xff
-       },
-       {
-+              NAND_DEVICE("FM35X1GA",
-+                          NAND_PACK_ID(0xe5, 0x71, 0, 0, 0, 0, 0, 0),
-+                          2, 0, 3, 3,
-+                          1, 1, 1, 1024, KB(128), KB(2), 64, 1,
-+                          &spi_cmds, &spi_addressing, &spi_status[0],
-+                          &spi_endurance, &spi_array_timing),
-+              {
-+                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
-+                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
-+                      NAND_SPI_STATUS(0xc0, 4, 5),
-+                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
-+              },
-+              &spi_extend_cmds, 0xff, 0xff
-+      },
-+      {
-               NAND_DEVICE("NO-DEVICE",
-                           NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0,
-                           0, 0, 0, 0, 0, 0, 0, 1,
---- /dev/null
-+++ b/linksys_e8450_env
-@@ -0,0 +1,57 @@
-+mtdparts=nand0:512k(bl2),1280k(fip),1024k(factory),256k(reserved),-(ubi)
-+ethaddr_factory=nand read 0x40080000 0x220000 0x20000 && env readmem -b ethaddr 0x4009fff4 0x6 ; setenv ethaddr_factory
-+ipaddr=192.168.1.1
-+serverip=192.168.1.254
-+loadaddr=0x4007ff28
-+bootcmd=run boot_ubi
-+bootdelay=0
-+bootfile=openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery.itb
-+bootfile_bl2=openwrt-mediatek-mt7622-linksys_e8450-ubi-preloader.bin
-+bootfile_fip=openwrt-mediatek-mt7622-linksys_e8450-ubi-bl31-uboot.fip
-+bootfile_upg=openwrt-mediatek-mt7622-linksys_e8450-ubi-squashfs-sysupgrade.itb
-+bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu
-+bootmenu_default=0
-+bootmenu_delay=0
-+bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )\e[0m
-+bootmenu_0=0. Initialize environment.=run _firstboot
-+bootmenu_0d=0. Run default boot command.=run boot_default
-+bootmenu_1=1. Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
-+bootmenu_3=2. Boot production system from flash.=run boot_production ; run bootmenu_confirm_return
-+bootmenu_2=3. Boot recovery system from flash.=run boot_recovery ; run bootmenu_confirm_return
-+bootmenu_5=4. Load production system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_4=5. Load recovery system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
-+bootmenu_6=\e[31m6. Load BL31+U-Boot FIP via TFTP then write to flash.\e[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return
-+bootmenu_7=\e[31m7. Load BL2 preloader via TFTP then write to flash.\e[0m=run boot_tftp_write_preloader ; run bootmenu_confirm_return
-+bootmenu_8=8. Reboot.=reset
-+bootmenu_9=9. Reset all settings to factory defaults.=run reset_factory ; reset
-+boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
-+boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
-+boot_production=led power:blue on ; run ubi_read_production && bootm $loadaddr
-+boot_production_or_recovery=run boot_production ; run boot_recovery
-+boot_recovery=led power:blue off ; led power:orange on ; run check_recovery
-+boot_serial_write_fip=loadx $loadaddr 115200 && run boot_write_fip
-+boot_serial_write_preloader=loadx $loadaddr 115200 && run boot_write_preloader
-+boot_tftp_forever=led inet:blue on ; while true ; do run boot_tftp_recovery ; led inet:blue off ; led inet:orange on ; sleep 1 ; done
-+boot_tftp_production=tftpboot $loadaddr $bootfile_upg && iminfo $loadaddr && ubi part ubi && run ubi_write_production ubi_prepare_rootfs ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && ubi part ubi && run ubi_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
-+boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
-+boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run boot_write_fip
-+boot_tftp_write_preloader=tftpboot $loadaddr $bootfile_bl2 && run boot_write_preloader
-+boot_ubi=ubi part ubi && run boot_production_or_recovery
-+boot_write_fip=nand erase 0x80000 0x140000 && nand write $loadaddr 0x80000 0x140000
-+boot_write_preloader=nand erase 0x0 0x80000 && nand write $loadaddr 0x0 0x20000 && nand write $loadaddr 0x20000 0x20000 && nand write $loadaddr 0x40000 0x20000 && nand write $loadaddr 0x60000 0x20000
-+check_recovery=run ubi_read_recovery ; if iminfo $loadaddr ; then bootm $loadaddr ; else ubi remove recovery ; fi
-+check_ubi=ubi part ubi || run ubi_format
-+reset_factory=ubi part ubi ; ubi write 0x0 ubootenv 0x0 ; ubi write 0x0 ubootenv2 0x0 ; ubi remove rootfs_data
-+ubi_format=ubi detach ; nand erase 0x300000 0x7D00000 && ubi part ubi ; reset
-+ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi
-+ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs
-+ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery
-+ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data
-+ubi_write_production=run ubi_remove_rootfs ; ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize
-+ubi_write_recovery=run ubi_remove_rootfs ; ubi check recovery && ubi remove recovery; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize
-+_create_env=ubi create ubootenv 0x100000 dynamic ; ubi create ubootenv2 0x100000 dynamic
-+_init_env=setenv _init_env ; if ubi check ubootenv && ubi check ubootenv2 ; then else run _create_env ; fi ; setenv _create_env ; saveenv || run ubi_format ; saveenv || run ubi_format
-+_firstboot=setenv _firstboot ; led power:orange on ; run _switch_to_menu ; run ethaddr_factory ; run check_ubi ; run _init_env ; run boot_first
-+_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
-+_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
diff --git a/package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch b/package/boot/uboot-mediatek/patches/100-increase-CONFIG_SYS_BOOTM_LEN.patch
deleted file mode 100644 (file)
index 811e848..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/include/configs/mt7622.h
-+++ b/include/configs/mt7622.h
-@@ -37,7 +37,7 @@
- #endif
- #define CONFIG_SYS_MAXARGS            8
--#define CONFIG_SYS_BOOTM_LEN          SZ_64M
-+#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)
diff --git a/package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch b/package/boot/uboot-mediatek/patches/100-scripts-remove-dependency-on-swig.patch
new file mode 100644 (file)
index 0000000..0505589
--- /dev/null
@@ -0,0 +1,24 @@
+From b137ca16b54c67d76714ea5a0138741959b0dc29 Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Mon, 13 Jul 2020 23:37:37 +0200
+Subject: [PATCH] scripts: remove dependency on swig
+
+Don't build the libfdt tool, as it has a dependency on swig (which
+OpenWrt does not ship).
+
+This requires more hacks, as of-platdata generation does not work
+without it.
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ scripts/dtc/Makefile | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/scripts/dtc/Makefile
++++ b/scripts/dtc/Makefile
+@@ -18,5 +18,3 @@ HOSTCFLAGS_dtc-parser.tab.o := -I$(src)
+ # dependencies on generated files need to be listed explicitly
+ $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h
+-# Added for U-Boot
+-subdir-$(CONFIG_PYLIBFDT) += pylibfdt
diff --git a/package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch b/package/boot/uboot-mediatek/patches/200-cmd-add-imsz-and-imszb.patch
new file mode 100644 (file)
index 0000000..a96345e
--- /dev/null
@@ -0,0 +1,132 @@
+--- a/cmd/bootm.c
++++ b/cmd/bootm.c
+@@ -228,6 +228,65 @@ U_BOOT_CMD(
+ /* iminfo - print header info for a requested image */
+ /*******************************************************************/
+ #if defined(CONFIG_CMD_IMI)
++#define SECTOR_SHIFT 9
++static int image_totalsize(struct cmd_tbl *cmdtp, int flag, int argc,
++                         char *const argv[], short int in_blocks)
++{
++      ulong addr;
++      void *fit;
++      int bsize, tsize;
++      char buf[16];
++
++      if (argc >= 2)
++              addr = simple_strtoul(argv[1], NULL, 16);
++      else
++              addr = image_load_addr;
++
++      fit = (void *)map_sysmem(addr, 0);
++      tsize = fit_get_totalsize(fit);
++      unmap_sysmem(fit);
++      if (tsize == 0)
++              return 1;
++
++      bsize = (tsize >> SECTOR_SHIFT) + ((tsize & ((1 << SECTOR_SHIFT) - 1))?1:0);
++
++      if (!in_blocks)
++              snprintf(buf, sizeof(buf), "%x", tsize);
++      else
++              snprintf(buf, sizeof(buf), "%x", bsize);
++
++      if (argc >= 3)
++              return env_set(argv[2], buf);
++      else
++              printf("%s\n", buf);
++
++      return 0;
++}
++
++static int do_imsz(struct cmd_tbl *cmdtp, int flag, int argc,
++                   char *const argv[])
++{
++      return image_totalsize(cmdtp, flag, argc, argv, 0);
++}
++
++static int do_imszb(struct cmd_tbl *cmdtp, int flag, int argc,
++                   char *const argv[])
++{
++      return image_totalsize(cmdtp, flag, argc, argv, 1);
++}
++
++U_BOOT_CMD(
++      imsz,   CONFIG_SYS_MAXARGS,     1,      do_imsz,
++      "get image total size (in bytes)",
++      "addr [maxhdrlen] [varname]\n"
++);
++
++U_BOOT_CMD(
++      imszb,  CONFIG_SYS_MAXARGS,     1,      do_imszb,
++      "get image total size (in blocks)",
++      "addr [maxhdrlen] [varname]\n"
++);
++
+ static int do_iminfo(struct cmd_tbl *cmdtp, int flag, int argc,
+                    char *const argv[])
+ {
+--- a/common/image-fit.c
++++ b/common/image-fit.c
+@@ -1970,6 +1970,51 @@ static const char *fit_get_image_type_pr
+       return "unknown";
+ }
++size_t fit_get_totalsize(const void *fit)
++{
++      int ret, ndepth, noffset, images_noffset;
++      size_t data_size, hdrsize, img_total, max_size = 0;
++      const void *data;
++
++      ret = fdt_check_header(fit);
++      if (ret) {
++              debug("Wrong FIT format: not a flattened device tree (err=%d)\n",
++                        ret);
++              return 0;
++      }
++
++      hdrsize = fdt_totalsize(fit);
++
++      /* simple FIT with internal images */
++      if (hdrsize > 0x1000)
++              return hdrsize;
++
++      images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
++      if (images_noffset < 0) {
++              printf("Can't find images parent node '%s' (%s)\n",
++              FIT_IMAGES_PATH, fdt_strerror(images_noffset));
++              return 0;
++      }
++
++      for (ndepth = 0,
++           noffset = fdt_next_node(fit, images_noffset, &ndepth);
++           (noffset >= 0) && (ndepth > 0);
++           noffset = fdt_next_node(fit, noffset, &ndepth)) {
++              if (ndepth == 1) {
++                      ret = fit_image_get_data_and_size(fit, noffset, &data, &data_size);
++                      if (ret)
++                              return 0;
++
++                      img_total = data_size + (data - fit);
++
++                      max_size = (max_size > img_total) ? max_size : img_total;
++              }
++      }
++
++      return max_size;
++}
++
++
+ int fit_image_load(bootm_headers_t *images, ulong addr,
+                  const char **fit_unamep, const char **fit_uname_configp,
+                  int arch, int image_type, int bootstage_id,
+--- a/include/image.h
++++ b/include/image.h
+@@ -1041,6 +1041,7 @@ int fit_parse_subimage(const char *spec,
+               ulong *addr, const char **image_name);
+ int fit_get_subimage_count(const void *fit, int images_noffset);
++size_t fit_get_totalsize(const void *fit);
+ void fit_print_contents(const void *fit);
+ void fit_image_print(const void *fit, int noffset, const char *p);
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
new file mode 100644 (file)
index 0000000..82d97f7
--- /dev/null
@@ -0,0 +1,192 @@
+From 26d4e2e58bf0007db74b47c783785c3305ea1fa0 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 19 Jan 2021 10:58:48 +0800
+Subject: [PATCH 17/23] 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 <weijie.gao@mediatek.com>
+---
+ cmd/bootmenu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 70 insertions(+), 7 deletions(-)
+
+--- a/cmd/bootmenu.c
++++ b/cmd/bootmenu.c
+@@ -11,6 +11,7 @@
+ #include <menu.h>
+ #include <watchdog.h>
+ #include <malloc.h>
++#include <linux/ctype.h>
+ #include <linux/delay.h>
+ #include <linux/string.h>
+@@ -38,6 +39,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 {
+@@ -46,8 +48,27 @@ enum bootmenu_key {
+       KEY_DOWN,
+       KEY_SELECT,
+       KEY_QUIT,
++      KEY_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'
++};
++
++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;
++}
++
+ static char *bootmenu_getoption(unsigned short int n)
+ {
+       char name[MAX_ENV_SIZE];
+@@ -82,7 +103,7 @@ static void bootmenu_print_entry(void *d
+ }
+ static 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;
+@@ -115,6 +136,19 @@ static void bootmenu_autoboot_loop(struc
+                               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;
+                       }
+@@ -136,10 +170,16 @@ static void bootmenu_autoboot_loop(struc
+ }
+ static 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();
+@@ -165,6 +205,14 @@ static void bootmenu_loop(struct bootmen
+               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:
+@@ -216,16 +264,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) {
+@@ -239,6 +288,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)
+@@ -294,6 +349,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)
+@@ -311,12 +367,19 @@ static struct bootmenu_data *bootmenu_cr
+                       goto cleanup;
+               len = sep-option;
+-              entry->title = malloc(len + 1);
++              entry->title = malloc(len + 4);
+               if (!entry->title) {
+                       free(entry);
+                       goto cleanup;
+               }
+-              memcpy(entry->title, option, len);
++
++              if (i < ARRAY_SIZE(choice_chars)) {
++                      len = sprintf(entry->title, "%c. %.*s", choice_chars[i],
++                                    len, option);
++              } else {
++                      len = sprintf(entry->title, "   %.*s", len, option);
++              }
++
+               entry->title[len] = 0;
+               len = strlen(sep + 1);
+@@ -353,7 +416,7 @@ static struct bootmenu_data *bootmenu_cr
+               if (!entry)
+                       goto cleanup;
+-              entry->title = strdup("U-Boot console");
++              entry->title = strdup("0. U-Boot console");
+               if (!entry->title) {
+                       free(entry);
+                       goto cleanup;
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
new file mode 100644 (file)
index 0000000..3f98f13
--- /dev/null
@@ -0,0 +1,32 @@
+--- a/cmd/bootmenu.c
++++ b/cmd/bootmenu.c
+@@ -39,6 +39,7 @@ struct bootmenu_data {
+       int active;                     /* active menu entry */
+       int count;                      /* total count of menu entries */
+       struct bootmenu_entry *first;   /* first menu entry */
++      char *mtitle;                   /* custom menu title */
+       bool last_choiced;
+ };
+@@ -471,7 +472,12 @@ static void menu_display_statusline(stru
+       printf(ANSI_CURSOR_POSITION, 1, 1);
+       puts(ANSI_CLEAR_LINE);
+       printf(ANSI_CURSOR_POSITION, 2, 1);
+-      puts("  *** U-Boot Boot Menu ***");
++
++      if (menu->mtitle)
++              puts(menu->mtitle);
++      else
++              puts("  *** U-Boot Boot Menu ***");
++
+       puts(ANSI_CLEAR_LINE_TO_END);
+       printf(ANSI_CURSOR_POSITION, 3, 1);
+       puts(ANSI_CLEAR_LINE);
+@@ -525,6 +531,7 @@ static void bootmenu_show(int delay)
+               return;
+       }
++      bootmenu->mtitle = env_get("bootmenu_title");
+       for (iter = bootmenu->first; iter; iter = iter->next) {
+               if (!menu_item_add(menu, iter->key, iter))
+                       goto cleanup;
diff --git a/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch b/package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch
new file mode 100644 (file)
index 0000000..705b749
--- /dev/null
@@ -0,0 +1,116 @@
+--- a/cmd/Kconfig
++++ b/cmd/Kconfig
+@@ -465,6 +465,12 @@ config CMD_ENV_EXISTS
+         Check if a variable is defined in the environment for use in
+         shell scripting.
++config CMD_ENV_READMEM
++      bool "env readmem"
++      default y
++      help
++        Store memory content into environment variable.
++
+ config CMD_ENV_CALLBACK
+       bool "env callbacks - print callbacks and their associated variables"
+       help
+--- a/cmd/nvedit.c
++++ b/cmd/nvedit.c
+@@ -473,6 +473,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in
+ }
+ #endif
++#if defined(CONFIG_CMD_ENV_READMEM)
++int do_env_readmem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
++{
++      char varstr[CONFIG_SYS_CBSIZE];
++      const void *buf;
++      char *local_args[4];
++      ulong addr, bytes = 6;
++      int hexdump = 0;
++
++      /*
++       * Check the syntax:
++       *
++       * readmem [-b] name address [size]
++       */
++      if (argc < 3)
++              return CMD_RET_USAGE;
++
++      local_args[0] = argv[0];
++
++      if (!strncmp(argv[1], "-b", 3))
++              hexdump = 1;
++
++      local_args[1] = argv[hexdump + 1];
++      local_args[2] = varstr;
++      local_args[3] = NULL;
++
++      addr = simple_strtoul(argv[hexdump + 2], NULL, 16);
++
++      if (!hexdump)
++              bytes = simple_strtoul(argv[hexdump + 3], NULL, 16);
++
++      if (bytes < 1)
++              return 1;
++
++      if ((hexdump * 3) * bytes >= CONFIG_SYS_CBSIZE)
++              return 1;
++
++      buf = map_sysmem(addr, bytes);
++      if (!buf)
++              return 1;
++
++      if (hexdump) {
++              sprintf(varstr, "%pM", buf);
++      } else {
++              memcpy(varstr, buf, bytes);
++              varstr[bytes] = '\0';
++      }
++      unmap_sysmem(buf);
++
++      /* Continue calling setenv code */
++      return _do_env_set(flag, 3, local_args, H_INTERACTIVE);
++}
++#endif
++
+ #if defined(CONFIG_CMD_ENV_CALLBACK)
+ static int print_static_binding(const char *var_name, const char *callback_name,
+                               void *priv)
+@@ -1377,6 +1431,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, "", ""),
++#if defined(CONFIG_CMD_ENV_READMEM)
++      U_BOOT_CMD_MKENT(readmem, CONFIG_SYS_MAXARGS, 3, do_env_readmem, "", ""),
++#endif
+ #if defined(CONFIG_CMD_RUN)
+       U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
+ #endif
+@@ -1465,6 +1522,9 @@ static char env_help_text[] =
+ #if defined(CONFIG_CMD_NVEDIT_EFI)
+       "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n"
+ #endif
++#if defined(CONFIG_CMD_ENV_READMEM)
++      "env readmem [-b] name address size - read variable from memory\n"
++#endif
+ #if defined(CONFIG_CMD_RUN)
+       "env run var [...] - run commands in an environment variable\n"
+ #endif
+@@ -1574,6 +1634,17 @@ U_BOOT_CMD(
+ );
+ #endif
++#if defined(CONFIG_CMD_ENV_READMEM)
++U_BOOT_CMD_COMPLETE(
++      readmem,        CONFIG_SYS_MAXARGS,     3,      do_env_readmem,
++      "get environment variable from memory address",
++      "name [-b] address size\n"
++      "    - store memory address to env variable\n"
++      "      \"-b\": read binary ethaddr",
++      var_complete
++);
++#endif
++
+ #if defined(CONFIG_CMD_RUN)
+ U_BOOT_CMD_COMPLETE(
+       run,    CONFIG_SYS_MAXARGS,     1,      do_run,
diff --git a/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch b/package/boot/uboot-mediatek/patches/300-mt7622-generic-reset-button-ignore-env.patch
new file mode 100644 (file)
index 0000000..aeb4c49
--- /dev/null
@@ -0,0 +1,37 @@
+--- a/board/mediatek/mt7622/mt7622_rfb.c
++++ b/board/mediatek/mt7622/mt7622_rfb.c
+@@ -6,10 +6,17 @@
+ #include <common.h>
+ #include <config.h>
++#include <dm.h>
++#include <button.h>
++
+ #include <env.h>
+ #include <init.h>
+ #include <asm/global_data.h>
++#ifndef CONFIG_RESET_BUTTON_LABEL
++#define CONFIG_RESET_BUTTON_LABEL "reset"
++#endif
++
+ DECLARE_GLOBAL_DATA_PTR;
+ int board_init(void)
+@@ -20,7 +27,15 @@ int board_init(void)
+ int board_late_init(void)
+ {
+-      gd->env_valid = 1; //to load environment variable from persistent store
++      struct udevice *dev;
++      int ret;
++
++      ret = !!button_get_by_label(CONFIG_RESET_BUTTON_LABEL, &dev);
++
++      if (!ret)
++              ret = !button_get_state(dev);
++
++      gd->env_valid = ret; //to load environment variable from persistent store
+       env_relocate();
+       return 0;
+ }
diff --git a/package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch b/package/boot/uboot-mediatek/patches/400-update-bpir2-defconfig.patch
new file mode 100644 (file)
index 0000000..ec0fed8
--- /dev/null
@@ -0,0 +1,19 @@
+--- a/configs/mt7623n_bpir2_defconfig
++++ b/configs/mt7623n_bpir2_defconfig
+@@ -51,6 +51,16 @@ CONFIG_SYSRESET=y
+ CONFIG_SYSRESET_WATCHDOG=y
+ CONFIG_TIMER=y
+ CONFIG_MTK_TIMER=y
++CONFIG_CMD_BOOTZ=y
++CONFIG_OF_LIBFDT_OVERLAY=y
++#enables savenenv-command
++CONFIG_ENV_IS_IN_FAT=y
++CONFIG_ENV_FAT_INTERFACE="mmc"
++CONFIG_ENV_FAT_DEVICE_AND_PART=":2"
++CONFIG_ENV_FAT_FILE="uboot.env"
++CONFIG_CMD_ASKENV=y
++CONFIG_ENV_SIZE=0x2000
++CONFIG_CMD_SETEXPR=y
+ CONFIG_WDT_MTK=y
+ CONFIG_LZMA=y
+ # CONFIG_EFI_GRUB_ARM32_WORKAROUND is not set
diff --git a/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch b/package/boot/uboot-mediatek/patches/401-update-u7623-defconfig.patch
new file mode 100644 (file)
index 0000000..37d1b6a
--- /dev/null
@@ -0,0 +1,15 @@
+--- a/configs/mt7623a_unielec_u7623_02_defconfig
++++ b/configs/mt7623a_unielec_u7623_02_defconfig
+@@ -52,3 +52,12 @@ CONFIG_TIMER=y
+ CONFIG_MTK_TIMER=y
+ CONFIG_WDT_MTK=y
+ CONFIG_LZMA=y
++CONFIG_CMD_BOOTZ=y
++CONFIG_OF_LIBFDT_OVERLAY=y
++#enables savenenv-command
++CONFIG_ENV_IS_IN_FAT=y
++CONFIG_ENV_FAT_INTERFACE="mmc"
++CONFIG_ENV_FAT_DEVICE_AND_PART="0:2"
++CONFIG_ENV_FAT_FILE="uboot.env"
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_SETEXPR=y
diff --git a/package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch b/package/boot/uboot-mediatek/patches/402-update-bananapi-bpi-r64-device-tree.patch
new file mode 100644 (file)
index 0000000..781a685
--- /dev/null
@@ -0,0 +1,71 @@
+--- a/arch/arm/dts/mt7622-bananapi-bpi-r64.dts
++++ b/arch/arm/dts/mt7622-bananapi-bpi-r64.dts
+@@ -20,6 +20,7 @@
+       aliases {
+               spi0 = &snfi;
++              ethernet0 = &eth;
+       };
+       memory@40000000 {
+@@ -27,6 +28,42 @@
+               reg = <0x40000000 0x40000000>;
+       };
++      gpio-keys {
++              compatible = "gpio-keys";
++
++              reset {
++                      label = "reset";
++                      gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
++              };
++
++              wps {
++                      label = "wps";
++                      gpios = <&gpio 102 GPIO_ACTIVE_LOW>;
++              };
++      };
++
++      leds {
++              compatible = "gpio-leds";
++/*
++ *            red {
++ *                    label = "bpi-r64:pio:red";
++ *                    gpios = <&gpio 88 GPIO_ACTIVE_HIGH>;
++ *                    default-state = "off";
++ *            };
++ */
++              green {
++                      label = "bpi-r64:pio:green";
++                      gpios = <&gpio 89 GPIO_ACTIVE_HIGH>;
++                      default-state = "off";
++              };
++
++              blue {
++                      label = "bpi-r64:pio:blue";
++                      gpios = <&gpio 85 GPIO_ACTIVE_LOW>;
++                      default-state = "off";
++              };
++      };
++
+       reg_1p8v: regulator-1p8v {
+               compatible = "regulator-fixed";
+               regulator-name = "fixed-1.8V";
+@@ -199,7 +236,7 @@
+       status = "okay";
+       bus-width = <8>;
+       max-frequency = <50000000>;
+-      cap-sd-highspeed;
++      cap-mmc-highspeed;
+       vmmc-supply = <&reg_3p3v>;
+       vqmmc-supply = <&reg_3p3v>;
+       non-removable;
+@@ -210,7 +247,7 @@
+       pinctrl-0 = <&mmc1_pins_default>;
+       status = "okay";
+       bus-width = <4>;
+-      max-frequency = <50000000>;
++      max-frequency = <12000000>;
+       cap-sd-highspeed;
+       r_smpl = <1>;
+       vmmc-supply = <&reg_3p3v>;
diff --git a/package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch b/package/boot/uboot-mediatek/patches/403-add-bananapi_bpi-r64_defconfigs.patch
new file mode 100644 (file)
index 0000000..2fb2d3a
--- /dev/null
@@ -0,0 +1,358 @@
+--- /dev/null
++++ b/configs/mt7622_bananapi_bpi-r64-sdmmc_defconfig
+@@ -0,0 +1,124 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_SYS_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_USE_DEFAULT_ENV_FILE=y
++CONFIG_BOARD_LATE_INIT=y
++CONFIG_BOOTP_SEND_HOSTNAME=y
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=25000000
++CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64"
++CONFIG_DEBUG_UART=y
++CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-sdmmc_env"
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_SMBIOS_PRODUCT_NAME=""
++CONFIG_AUTOBOOT_KEYED=y
++CONFIG_BOOTDELAY=30
++CONFIG_AUTOBOOT_MENU_SHOW=y
++CONFIG_CFB_CONSOLE_ANSI=y
++CONFIG_BUTTON=y
++CONFIG_BUTTON_GPIO=y
++CONFIG_CMD_ENV_FLAGS=y
++CONFIG_FIT=y
++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
++CONFIG_LED=y
++CONFIG_LED_BLINK=y
++CONFIG_LED_GPIO=y
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_DEFAULT_FDT_FILE="mt7622-bananapi-bpi-r64"
++CONFIG_SYS_PROMPT="MT7622> "
++CONFIG_CMD_BOOTMENU=y
++CONFIG_CMD_BOOTP=y
++CONFIG_CMD_BUTTON=y
++CONFIG_CMD_CDP=y
++CONFIG_CMD_DHCP=y
++CONFIG_CMD_DNS=y
++CONFIG_CMD_ECHO=y
++CONFIG_CMD_ENV_READMEM=y
++CONFIG_CMD_ERASEENV=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_FS_UUID=y
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_GPT=y
++CONFIG_CMD_HASH=y
++CONFIG_CMD_ITEST=y
++CONFIG_CMD_LED=y
++CONFIG_CMD_LICENSE=y
++CONFIG_CMD_LINK_LOCAL=y
++# CONFIG_CMD_MBR is not set
++CONFIG_CMD_MMC=y
++CONFIG_CMD_PCI=y
++CONFIG_CMD_SF_TEST=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_PXE=y
++CONFIG_CMD_SMC=y
++CONFIG_CMD_TFTPBOOT=y
++CONFIG_CMD_TFTPSRV=y
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_PART=y
++# CONFIG_CMD_PSTORE is not set
++CONFIG_CMD_RARP=y
++CONFIG_CMD_SETEXPR=y
++CONFIG_CMD_SLEEP=y
++CONFIG_CMD_SNTP=y
++CONFIG_CMD_SOURCE=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_UUID=y
++CONFIG_DISPLAY_CPUINFO=y
++CONFIG_DM_MMC=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_REGULATOR_GPIO=y
++CONFIG_DM_USB=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_ENV_IS_IN_MMC=y
++CONFIG_SYS_MMC_ENV_DEV=1
++CONFIG_ENV_OFFSET=0x400000
++CONFIG_ENV_OFFSET_REDUND=0x480000
++CONFIG_ENV_SIZE=0x80000
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_VERSION_VARIABLE=y
++CONFIG_PARTITION_UUIDS=y
++CONFIG_NETCONSOLE=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_PHY_FIXED=y
++CONFIG_DM_ETH=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PCI=y
++CONFIG_DM_PCI=y
++CONFIG_PCIE_MEDIATEK=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7622=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_PRE_CONSOLE_BUFFER=y
++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_MMC=y
++CONFIG_MMC_DEFAULT_DEV=1
++CONFIG_MMC_MTK=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_WDT_MTK=y
++CONFIG_LZO=y
++CONFIG_ZSTD=y
++CONFIG_HEXDUMP=y
++CONFIG_RANDOM_UUID=y
++CONFIG_REGEX=y
++CONFIG_USB=y
++CONFIG_USB_HOST=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
+--- /dev/null
++++ b/bananapi_bpi-r64-sdmmc_env
+@@ -0,0 +1,54 @@
++ipaddr=192.168.1.1
++serverip=192.168.1.254
++loadaddr=0x4007ff28
++bootcmd=run boot_sdmmc
++bootargs=root=/dev/mmcblk1p6
++bootdelay=0
++bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb
++bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb
++bootfile_emmcbl3=openwrt-mediatek-mt7622-bananapi_bpi-r64-boot-emmc.img
++bootfile_emmcbl2=openwrt-mediatek-mt7622-bananapi_bpi-r64-bl2-emmc.bin
++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
++bootmenu_default=0
++bootmenu_delay=0
++bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[SD card]\e[0m
++bootmenu_0=Initialize environment.=run _firstboot
++bootmenu_0d=Run default boot command.=run boot_default
++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
++bootmenu_2=Boot production system from SD card.=run boot_production ; run bootmenu_confirm_return
++bootmenu_3=Boot recovery system from SD card.=run boot_recovery ; run bootmenu_confirm_return
++bootmenu_4=Load production system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_5=Load recovery system via TFTP then write to SD card.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_6=\e[31mInstall bootloader and recovery to eMMC.\e[0m=run emmc_init ; run bootmenu_confirm_return
++bootmenu_7=Reboot.=reset
++bootmenu_8=Reset all settings to factory defaults.=run reset_factory ; reset
++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
++boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
++boot_production=led bpi-r64:pio:green on ; run sdmmc_read_production && bootm $loadaddr
++boot_recovery=led bpi-r64:pio:green off ; run sdmmc_read_recovery && bootm $loadaddr
++boot_sdmmc=run boot_production ; run boot_recovery
++boot_tftp_forever=led bpi-r64:pio:blue on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && run sdmmc_write_production ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && run sdmmc_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
++emmc_write_bl2=mmc dev 0 1 && mmc partconf 0 1 1 1 && mmc erase 0x0 0x400 && mmc write $loadaddr 0x0 0x100 ; mmc partconf 0 1 1 0
++emmc_write_hdr=mmc dev 0 0 && mmc erase 0x0 0x40 && mmc write $loadaddr 0x0 0x40
++emmc_write_bl3=mmc dev 0 0 && mmc erase 0x1000 0x800 && mmc write $loadaddr 0x1000 0x800
++emmc_write_recovery=iminfo $loadaddr && mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_write_vol
++emmc_init=run sdmmc_read_emmc_bl2 && run emmc_write_bl2 && run sdmmc_read_emmc_hdr && run emmc_write_hdr && run sdmmc_read_emmc_bl3 && run emmc_write_bl3 && run sdmmc_read_recovery && run emmc_write_recovery ; env default bootcmd ; saveenv ; saveenv
++sdmmc_write_production=iminfo $fileaddr && mmc dev 1 && part start mmc 1 $part_default part_addr && part size mmc 1 $part_default part_size && run mmc_write_vol
++sdmmc_write_recovery=iminfo $fileaddr && mmc dev 1 && part start mmc 1 $part_recovery part_addr && part size mmc 1 $part_recovery part_size && run mmc_write_vol
++sdmmc_read_production=mmc dev 1 && part start mmc 1 $part_default part_addr && part size mmc 1 $part_default part_size && run mmc_read_vol
++sdmmc_read_recovery=mmc dev 1 && part start mmc 1 $part_recovery part_addr && part size mmc 1 $part_recovery part_size && run mmc_read_vol
++sdmmc_read_emmc_hdr=mmc dev 1 && mmc read $loadaddr 0x100 0x40
++sdmmc_read_emmc_bl2=mmc dev 1 && mmc read $loadaddr 0x200 0x100
++sdmmc_read_emmc_bl3=mmc dev 1 && mmc read $loadaddr 0x800 0x800
++mmc_write_vol=imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc erase 0x$part_addr 0x$part_size && mmc write $loadaddr 0x$part_addr 0x$image_size
++mmc_read_vol=mmc read $loadaddr $part_addr 0x8 && imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc read $loadaddr 0x$part_addr 0x$image_size
++part_default=production
++part_recovery=recovery
++reset_factory=eraseenv && reset
++_init_env=setenv _init_env ; setenv _create_env ; saveenv ; saveenv
++_firstboot=setenv _firstboot ; led bpi-r64:pio:blue on ; run _switch_to_menu ; run _init_env ; run boot_first
++_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
+--- /dev/null
++++ b/configs/mt7622_bananapi_bpi-r64-emmc_defconfig
+@@ -0,0 +1,125 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_SYS_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_USE_DEFAULT_ENV_FILE=y
++CONFIG_BOARD_LATE_INIT=y
++CONFIG_BOOTP_SEND_HOSTNAME=y
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=25000000
++CONFIG_DEFAULT_DEVICE_TREE="mt7622-bananapi-bpi-r64"
++CONFIG_DEBUG_UART=y
++CONFIG_DEFAULT_ENV_FILE="bananapi_bpi-r64-emmc_env"
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_SMBIOS_PRODUCT_NAME=""
++CONFIG_AUTOBOOT_KEYED=y
++CONFIG_BOOTDELAY=30
++CONFIG_AUTOBOOT_MENU_SHOW=y
++CONFIG_CFB_CONSOLE_ANSI=y
++CONFIG_BUTTON=y
++CONFIG_BUTTON_GPIO=y
++CONFIG_CMD_ENV_FLAGS=y
++CONFIG_FIT=y
++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
++CONFIG_LED=y
++CONFIG_LED_BLINK=y
++CONFIG_LED_GPIO=y
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_DEFAULT_FDT_FILE="mt7622-bananapi-bpi-r64"
++CONFIG_SYS_PROMPT="MT7622> "
++CONFIG_CMD_BOOTMENU=y
++CONFIG_CMD_BOOTP=y
++CONFIG_CMD_BUTTON=y
++CONFIG_CMD_CDP=y
++CONFIG_CMD_DHCP=y
++CONFIG_CMD_DNS=y
++CONFIG_CMD_ECHO=y
++CONFIG_CMD_ENV_READMEM=y
++CONFIG_CMD_ERASEENV=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_FS_UUID=y
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_GPT=y
++CONFIG_CMD_HASH=y
++CONFIG_CMD_ITEST=y
++CONFIG_CMD_LED=y
++CONFIG_CMD_LICENSE=y
++CONFIG_CMD_LINK_LOCAL=y
++# CONFIG_CMD_MBR is not set
++CONFIG_CMD_MMC=y
++CONFIG_CMD_PCI=y
++CONFIG_CMD_SF_TEST=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_PXE=y
++CONFIG_CMD_SMC=y
++CONFIG_CMD_TFTPBOOT=y
++CONFIG_CMD_TFTPSRV=y
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_PART=y
++# CONFIG_CMD_PSTORE is not set
++CONFIG_CMD_RARP=y
++CONFIG_CMD_SETEXPR=y
++CONFIG_CMD_SLEEP=y
++CONFIG_CMD_SNTP=y
++CONFIG_CMD_SOURCE=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_UUID=y
++CONFIG_DISPLAY_CPUINFO=y
++CONFIG_DM_MMC=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_REGULATOR_GPIO=y
++CONFIG_DM_USB=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_ENV_IS_IN_MMC=y
++CONFIG_SYS_MMC_ENV_DEV=0
++CONFIG_ENV_OFFSET=0x400000
++CONFIG_ENV_OFFSET_REDUND=0x480000
++CONFIG_ENV_SIZE=0x80000
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_VERSION_VARIABLE=y
++CONFIG_PARTITION_UUIDS=y
++CONFIG_NETCONSOLE=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_PHY_FIXED=y
++CONFIG_DM_ETH=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PCI=y
++CONFIG_DM_PCI=y
++CONFIG_PCIE_MEDIATEK=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7622=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_PRE_CONSOLE_BUFFER=y
++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_MMC=y
++CONFIG_MMC_DEFAULT_DEV=0
++CONFIG_MMC_MTK=y
++CONFIG_MMC_SUPPORTS_TUNING=y
++CONFIG_SUPPORT_EMMC_BOOT=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_WDT_MTK=y
++CONFIG_LZO=y
++CONFIG_ZSTD=y
++CONFIG_HEXDUMP=y
++CONFIG_RANDOM_UUID=y
++CONFIG_REGEX=y
++CONFIG_USB=y
++CONFIG_USB_HOST=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
+--- /dev/null
++++ b/bananapi_bpi-r64-emmc_env
+@@ -0,0 +1,43 @@
++ipaddr=192.168.1.1
++serverip=192.168.1.254
++loadaddr=0x4007ff28
++bootcmd=run boot_emmc
++bootargs=root=/dev/mmcblk0p5
++bootdelay=0
++bootfile=openwrt-mediatek-mt7622-bananapi_bpi-r64-initramfs-recovery.itb
++bootfile_upg=openwrt-mediatek-mt7622-bananapi_bpi-r64-squashfs-sysupgrade.itb
++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
++bootmenu_default=0
++bootmenu_delay=0
++bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )  \e[0;36m[eMMC]\e[0m
++bootmenu_0=Initialize environment.=run _firstboot
++bootmenu_0d=Run default boot command.=run boot_default
++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
++bootmenu_2=Boot production system from eMMC.=run boot_production ; run bootmenu_confirm_return
++bootmenu_3=Boot recovery system from eMMC.=run boot_recovery ; run bootmenu_confirm_return
++bootmenu_4=Load production system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_5=Load recovery system via TFTP then write to eMMC.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_6=Reboot.=reset
++bootmenu_7=Reset all settings to factory defaults.=run reset_factory ; reset
++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
++boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
++boot_production=led bpi-r64:pio:green on ; run emmc_read_production && bootm $loadaddr
++boot_recovery=led bpi-r64:pio:green off ; run emmc_read_recovery && bootm $loadaddr
++boot_emmc=run boot_production ; run boot_recovery
++boot_tftp_forever=led bpi-r64:pio:blue on ; while true ; do run boot_tftp_recovery ; sleep 1 ; done
++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && run emmc_write_production ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && run emmc_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
++emmc_write_production=iminfo $fileaddr && mmc dev 0 && part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_write_vol
++emmc_write_recovery=iminfo $fileaddr && mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_write_vol
++emmc_read_production=mmc dev 0 && part start mmc 0 $part_default part_addr && part size mmc 0 $part_default part_size && run mmc_read_vol
++emmc_read_recovery=mmc dev 0 && part start mmc 0 $part_recovery part_addr && part size mmc 0 $part_recovery part_size && run mmc_read_vol
++mmc_write_vol=imszb $fileaddr image_size && test 0x$image_size -le 0x$part_size && mmc erase 0x$part_addr 0x$part_size && mmc write $fileaddr 0x$part_addr 0x$image_size
++mmc_read_vol=mmc read $loadaddr $part_addr 0x8 && imszb $loadaddr image_size && test 0x$image_size -le 0x$part_size && mmc read $loadaddr 0x$part_addr 0x$image_size
++part_default=production
++part_recovery=recovery
++reset_factory=eraseenv && reset
++_init_env=setenv _init_env ; setenv _create_env ; saveenv ; saveenv
++_firstboot=setenv _firstboot ; led bpi-r64:pio:blue on ; run _switch_to_menu ; run _init_env ; run boot_first
++_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"
diff --git a/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch b/package/boot/uboot-mediatek/patches/410-add-linksys-e8450.patch
new file mode 100644 (file)
index 0000000..e78641e
--- /dev/null
@@ -0,0 +1,399 @@
+--- /dev/null
++++ b/configs/mt7622_linksys_e8450_defconfig
+@@ -0,0 +1,128 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_SYS_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_USE_DEFAULT_ENV_FILE=y
++CONFIG_BOARD_LATE_INIT=y
++CONFIG_BOOTP_SEND_HOSTNAME=y
++CONFIG_DEFAULT_ENV_FILE="linksys_e8450_env"
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=25000000
++CONFIG_DEFAULT_DEVICE_TREE="mt7622-linksys-e8450-ubi"
++CONFIG_DEBUG_UART=y
++CONFIG_SMBIOS_PRODUCT_NAME=""
++CONFIG_AUTOBOOT_KEYED=y
++CONFIG_BOOTDELAY=30
++CONFIG_AUTOBOOT_MENU_SHOW=y
++CONFIG_CFB_CONSOLE_ANSI=y
++CONFIG_BUTTON=y
++CONFIG_BUTTON_GPIO=y
++CONFIG_CMD_ENV_FLAGS=y
++CONFIG_FIT=y
++CONFIG_FIT_ENABLE_SHA256_SUPPORT=y
++CONFIG_LED=y
++CONFIG_LED_BLINK=y
++CONFIG_LED_GPIO=y
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_DEFAULT_FDT_FILE="mt7622-linksys-e8450"
++CONFIG_SYS_PROMPT="MT7622> "
++CONFIG_CMD_BOOTMENU=y
++CONFIG_CMD_BOOTP=y
++CONFIG_CMD_BUTTON=y
++CONFIG_CMD_CDP=y
++CONFIG_CMD_DHCP=y
++CONFIG_CMD_DNS=y
++CONFIG_CMD_ECHO=y
++CONFIG_CMD_ENV_READMEM=y
++CONFIG_CMD_ERASEENV=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_FS_UUID=y
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_GPT=y
++CONFIG_CMD_HASH=y
++CONFIG_CMD_ITEST=y
++CONFIG_CMD_LED=y
++CONFIG_CMD_LICENSE=y
++CONFIG_CMD_LINK_LOCAL=y
++# CONFIG_CMD_MBR is not set
++CONFIG_CMD_MTD=y
++CONFIG_CMD_MTDPART=y
++CONFIG_CMD_PCI=y
++CONFIG_CMD_SF_TEST=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_PXE=y
++CONFIG_CMD_SMC=y
++CONFIG_CMD_TFTPBOOT=y
++CONFIG_CMD_TFTPSRV=y
++CONFIG_CMD_UBI=y
++CONFIG_CMD_UBI_RENAME=y
++CONFIG_CMD_UBIFS=y
++CONFIG_CMD_ASKENV=y
++CONFIG_CMD_PART=y
++# CONFIG_CMD_PSTORE is not set
++CONFIG_CMD_RARP=y
++CONFIG_CMD_SETEXPR=y
++CONFIG_CMD_SLEEP=y
++CONFIG_CMD_SNTP=y
++CONFIG_CMD_SOURCE=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_UUID=y
++CONFIG_DISPLAY_CPUINFO=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_DM_REGULATOR_GPIO=y
++CONFIG_DM_USB=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_ENV_IS_IN_UBI=y
++CONFIG_ENV_UBI_PART="ubi"
++CONFIG_ENV_UBI_VOLUME="ubootenv"
++CONFIG_ENV_UBI_VOLUME_REDUND="ubootenv2"
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_VERSION_VARIABLE=y
++CONFIG_PARTITION_UUIDS=y
++CONFIG_NETCONSOLE=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++CONFIG_DM_MTD=y
++CONFIG_PHY_FIXED=y
++CONFIG_DM_ETH=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PCI=y
++CONFIG_MTD=y
++CONFIG_MTD_UBI_FASTMAP=y
++CONFIG_DM_PCI=y
++CONFIG_PCIE_MEDIATEK=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7622=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_PRE_CONSOLE_BUFFER=y
++CONFIG_PRE_CON_BUF_ADDR=0x4007EF00
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_SPI=y
++CONFIG_DM_SPI=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_WDT_MTK=y
++CONFIG_LZO=y
++CONFIG_ZSTD=y
++CONFIG_HEXDUMP=y
++CONFIG_RANDOM_UUID=y
++CONFIG_REGEX=y
++CONFIG_USB=y
++CONFIG_USB_HOST=y
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
+--- /dev/null
++++ b/arch/arm/dts/mt7622-linksys-e8450-ubi.dts
+@@ -0,0 +1,195 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2019 MediaTek Inc.
++ * Author: Sam Shih <sam.shih@mediatek.com>
++ */
++
++/dts-v1/;
++#include "mt7622.dtsi"
++#include "mt7622-u-boot.dtsi"
++
++/ {
++      #address-cells = <1>;
++      #size-cells = <1>;
++      model = "mt7622-linksys-e8450-ubi";
++      compatible = "mediatek,mt7622", "linksys,e8450-ubi";
++      chosen {
++              stdout-path = &uart0;
++              tick-timer = &timer0;
++      };
++
++      aliases {
++              spi0 = &snand;
++      };
++
++      gpio-keys {
++              compatible = "gpio-keys";
++
++              factory {
++                      label = "reset";
++                      gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
++              };
++
++              wps {
++                      label = "wps";
++                      gpios = <&gpio 102 GPIO_ACTIVE_LOW>;
++              };
++      };
++
++      gpio-leds {
++              compatible = "gpio-leds";
++
++              led_power: power_blue {
++                      label = "power:blue";
++                      gpios = <&gpio 95 GPIO_ACTIVE_LOW>;
++                      default-state = "on";
++              };
++
++              power_orange {
++                      label = "power:orange";
++                      gpios = <&gpio 96 GPIO_ACTIVE_LOW>;
++                      default-state = "off";
++              };
++
++              inet_blue {
++                      label = "inet:blue";
++                      gpios = <&gpio 97 GPIO_ACTIVE_LOW>;
++                      default-state = "off";
++              };
++
++              inet_orange {
++                      label = "inet:orange";
++                      gpios = <&gpio 98 GPIO_ACTIVE_LOW>;
++                      default-state = "off";
++              };
++      };
++
++      memory@40000000 {
++              device_type = "memory";
++              reg = <0x40000000 0x20000000>;
++      };
++
++      reg_1p8v: regulator-1p8v {
++              compatible = "regulator-fixed";
++              regulator-name = "fixed-1.8V";
++              regulator-min-microvolt = <1800000>;
++              regulator-max-microvolt = <1800000>;
++              regulator-boot-on;
++              regulator-always-on;
++      };
++
++      reg_3p3v: regulator-3p3v {
++              compatible = "regulator-fixed";
++              regulator-name = "fixed-3.3V";
++              regulator-min-microvolt = <3300000>;
++              regulator-max-microvolt = <3300000>;
++              regulator-boot-on;
++              regulator-always-on;
++      };
++
++      reg_5v: regulator-5v {
++              compatible = "regulator-fixed";
++              regulator-name = "fixed-5V";
++              regulator-min-microvolt = <5000000>;
++              regulator-max-microvolt = <5000000>;
++              regulator-boot-on;
++              regulator-always-on;
++      };
++};
++
++&pcie {
++      pinctrl-names = "default";
++      pinctrl-0 = <&pcie0_pins>, <&pcie1_pins>;
++      status = "okay";
++
++      pcie@0,0 {
++              status = "okay";
++      };
++
++      pcie@1,0 {
++              status = "okay";
++      };
++};
++
++&pinctrl {
++      pcie0_pins: pcie0-pins {
++              mux {
++                      function = "pcie";
++                      groups = "pcie0_pad_perst",
++                               "pcie0_1_waken",
++                               "pcie0_1_clkreq";
++              };
++      };
++
++      pcie1_pins: pcie1-pins {
++              mux {
++                      function = "pcie";
++                      groups = "pcie1_pad_perst",
++                               "pcie1_0_waken",
++                               "pcie1_0_clkreq";
++              };
++      };
++
++      snfi_pins: snfi-pins {
++              mux {
++                      function = "flash";
++                      groups = "snfi";
++              };
++      };
++
++      uart0_pins: uart0 {
++              mux {
++                      function = "uart";
++                      groups = "uart0_0_tx_rx" ;
++              };
++      };
++
++      watchdog_pins: watchdog-default {
++              mux {
++                      function = "watchdog";
++                      groups = "watchdog";
++              };
++      };
++};
++
++&snand {
++      pinctrl-names = "default";
++      pinctrl-0 = <&snfi_pins>;
++      status = "okay";
++      quad-spi;
++};
++
++&uart0 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart0_pins>;
++      status = "okay";
++};
++
++&watchdog {
++      pinctrl-names = "default";
++      pinctrl-0 = <&watchdog_pins>;
++      status = "okay";
++};
++
++&eth {
++      status = "okay";
++      mediatek,gmac-id = <0>;
++      phy-mode = "sgmii";
++      mediatek,switch = "mt7531";
++      reset-gpios = <&gpio 54 GPIO_ACTIVE_HIGH>;
++
++      fixed-link {
++              speed = <1000>;
++              full-duplex;
++      };
++};
++
++&ssusb {
++      vusb33-supply = <&reg_3p3v>;
++      vbus-supply = <&reg_5v>;
++      status = "okay";
++};
++
++&u3phy {
++      status = "okay";
++};
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -996,6 +996,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
+       mt7622-rfb.dtb \
+       mt7623a-unielec-u7623-02-emmc.dtb \
+       mt7622-bananapi-bpi-r64.dtb \
++      mt7622-linksys-e8450-ubi.dtb \
+       mt7623n-bananapi-bpi-r2.dtb \
+       mt7629-rfb.dtb \
+       mt8512-bm1-emmc.dtb \
+--- /dev/null
++++ b/linksys_e8450_env
+@@ -0,0 +1,57 @@
++mtdparts=spi-nand0:512k(bl2),1280k(fip),1024k(factory),256k(reserved),-(ubi)
++ethaddr_factory=mtd read spi-nand0 0x40080000 0x220000 0x20000 && env readmem -b ethaddr 0x4009fff4 0x6 ; setenv ethaddr_factory
++ipaddr=192.168.1.1
++serverip=192.168.1.254
++loadaddr=0x4007ff28
++bootcmd=run boot_ubi
++bootdelay=0
++bootfile=openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery.itb
++bootfile_bl2=openwrt-mediatek-mt7622-linksys_e8450-ubi-preloader.bin
++bootfile_fip=openwrt-mediatek-mt7622-linksys_e8450-ubi-bl31-uboot.fip
++bootfile_upg=openwrt-mediatek-mt7622-linksys_e8450-ubi-squashfs-sysupgrade.itb
++bootmenu_confirm_return=askenv - Press ENTER to return to menu ; bootmenu 60
++bootmenu_default=0
++bootmenu_delay=0
++bootmenu_title=      \e[0;34m( ( ( \e[1;39mOpenWrt\e[0;34m ) ) )\e[0m
++bootmenu_0=Initialize environment.=run _firstboot
++bootmenu_0d=Run default boot command.=run boot_default
++bootmenu_1=Boot system via TFTP.=run boot_tftp ; run bootmenu_confirm_return
++bootmenu_2=Boot production system from flash.=run boot_production ; run bootmenu_confirm_return
++bootmenu_3=Boot recovery system from flash.=run boot_recovery ; run bootmenu_confirm_return
++bootmenu_4=Load production system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_production ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_5=Load recovery system via TFTP then write to flash.=setenv noboot 1 ; run boot_tftp_recovery ; setenv noboot ; run bootmenu_confirm_return
++bootmenu_6=\e[31mLoad BL31+U-Boot FIP via TFTP then write to flash.\e[0m=run boot_tftp_write_fip ; run bootmenu_confirm_return
++bootmenu_7=\e[31mLoad BL2 preloader via TFTP then write to flash.\e[0m=run boot_tftp_write_preloader ; run bootmenu_confirm_return
++bootmenu_8=Reboot.=reset
++bootmenu_9=Reset all settings to factory defaults.=run reset_factory ; reset
++boot_first=if button reset ; then run boot_tftp_forever ; fi ; setenv flag_recover 1 ; bootmenu
++boot_default=if env exists flag_recover ; then else run bootcmd ; fi ; run boot_recovery ; run boot_tftp_forever
++boot_production=led power:blue on ; run ubi_read_production && bootm $loadaddr
++boot_production_or_recovery=run boot_production ; run boot_recovery
++boot_recovery=led power:blue off ; led power:orange on ; run check_recovery
++boot_serial_write_fip=loadx $loadaddr 115200 && run boot_write_fip
++boot_serial_write_preloader=loadx $loadaddr 115200 && run boot_write_preloader
++boot_tftp_forever=led inet:blue on ; while true ; do run boot_tftp_recovery ; led inet:blue off ; led inet:orange on ; sleep 1 ; done
++boot_tftp_production=tftpboot $loadaddr $bootfile_upg && iminfo $loadaddr && ubi part ubi && run ubi_write_production ubi_prepare_rootfs ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp_recovery=tftpboot $loadaddr $bootfile && iminfo $loadaddr && ubi part ubi && run ubi_write_recovery ; if env exists noboot ; then else bootm $loadaddr ; fi
++boot_tftp=tftpboot $loadaddr $bootfile && bootm $loadaddr
++boot_tftp_write_fip=tftpboot $loadaddr $bootfile_fip && run boot_write_fip
++boot_tftp_write_preloader=tftpboot $loadaddr $bootfile_bl2 && run boot_write_preloader
++boot_ubi=ubi part ubi && run boot_production_or_recovery
++boot_write_fip=mtd erase spi-nand0 0x80000 0x140000 && mtd write spi-nand0 $loadaddr 0x80000 0x140000
++boot_write_preloader=mtd erase spi-nand0 0x0 0x80000 && mtd write spi-nand0 $loadaddr 0x0 0x20000 && mtd write spi-nand0 $loadaddr 0x20000 0x20000 && mtd write spi-nand0 $loadaddr 0x40000 0x20000 && mtd write spi-nand0 $loadaddr 0x60000 0x20000
++check_recovery=run ubi_read_recovery ; if iminfo $loadaddr ; then bootm $loadaddr ; else ubi remove recovery ; fi
++check_ubi=ubi part ubi || run ubi_format
++reset_factory=ubi part ubi ; ubi write 0x0 ubootenv 0x0 ; ubi write 0x0 ubootenv2 0x0 ; ubi remove rootfs_data
++ubi_format=ubi detach ; mtd erase spi-nand0 0x300000 0x7D00000 && ubi part ubi ; reset
++ubi_prepare_rootfs=if ubi check rootfs_data ; then else if env exists rootfs_data_max ; then ubi create rootfs_data $rootfs_data_max dynamic || ubi create rootfs_data - dynamic ; else ubi create rootfs_data - dynamic ; fi ; fi
++ubi_read_production=ubi read $loadaddr fit && iminfo $loadaddr && run ubi_prepare_rootfs
++ubi_read_recovery=ubi check recovery && ubi read $loadaddr recovery
++ubi_remove_rootfs=ubi check rootfs_data && ubi remove rootfs_data
++ubi_write_production=ubi check fit && ubi remove fit ; run ubi_remove_rootfs ; ubi create fit $filesize dynamic && ubi write $loadaddr fit $filesize
++ubi_write_recovery=ubi check recovery && ubi remove recovery ; run ubi_remove_rootfs ; ubi create recovery $filesize dynamic && ubi write $loadaddr recovery $filesize
++_create_env=ubi create ubootenv 0x100000 dynamic ; ubi create ubootenv2 0x100000 dynamic
++_init_env=setenv _init_env ; if ubi check ubootenv && ubi check ubootenv2 ; then else run _create_env ; fi ; setenv _create_env ; saveenv || run ubi_format ; saveenv || run ubi_format
++_firstboot=setenv _firstboot ; led power:orange on ; run _switch_to_menu ; run ethaddr_factory ; run check_ubi ; run _init_env ; run boot_first
++_switch_to_menu=setenv _switch_to_menu ; setenv bootdelay 3 ; setenv bootmenu_delay 3 ; setenv bootmenu_0 $bootmenu_0d ; setenv bootmenu_0d ; run _bootmenu_update_title
++_bootmenu_update_title=setenv _bootmenu_update_title ; setenv bootmenu_title "$bootmenu_title       \e[33m$ver\e[0m"