uboot-mediatek: additions from MTK SDK
authorDaniel Golle <daniel@makrotopia.org>
Sun, 7 Aug 2022 10:06:56 +0000 (12:06 +0200)
committerDaniel Golle <daniel@makrotopia.org>
Sun, 28 Aug 2022 19:33:15 +0000 (20:33 +0100)
 * updated SNAND/SNFI driver brings support for MT7981
 * add support for MediaTek NAND Memory bad Block Management (NMBM)
   (not used for any boards atm, but could be useful in future)
 * wire up NMBM support for MT7622, MT7629, MT7981 and MT7986
 * replace some local patches with updated version from SDK
 * bring some legacy precompiler symbols which haven't been converted
   into Kconfig symbols in U-Boot 2022.07, remove when bumbping to
   U-Boot 2022.10:
   100-28-include-configs-mt7986-h-from-SDK.patch

Source: https://github.com/mtk-openwrt/u-boot
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
53 files changed:
package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-15-mtd-mtk-snand-add-support-for-SPL.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-16-env-add-support-for-generic-MTD-device.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch [deleted file]
package/boot/uboot-mediatek/patches/000-mtk-20-configs-mt7622-enable-environment-for-mt7622_rfb.patch
package/boot/uboot-mediatek/patches/001-mtk-0100-arm-dts-mt7622-remove-default-pinctrl-of-uart0.patch
package/boot/uboot-mediatek/patches/002-0011-arm-dts-mt7622-force-high-speed-mode-for-uart.patch
package/boot/uboot-mediatek/patches/002-0018-arm-dts-mt7622-add-i2c-support.patch
package/boot/uboot-mediatek/patches/002-0029-tools-mtk_image-split-the-code-of-generating-NAND-he.patch
package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/110-no-kwbimage.patch
package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch [deleted file]
package/boot/uboot-mediatek/patches/211-cmd-bootmenu-custom-title.patch
package/boot/uboot-mediatek/patches/220-cmd-env-readmem.patch
package/boot/uboot-mediatek/patches/301-mt7622-generic-reset-button-ignore-env.patch
package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch [deleted file]
package/boot/uboot-mediatek/patches/412-add-ubnt-unifi-6-lr.patch
package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch [deleted file]

diff --git a/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch b/package/boot/uboot-mediatek/patches/000-mtk-01-Revert-clk-Add-debugging-for-return-values.patch
deleted file mode 100644 (file)
index 3b68c52..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-From 34ed9f6d3018d32c7c015e57c9985d3c4c07b706 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/12] Revert "clk: Add debugging for return values"
-
-This reverts commit 5c5992cb90cf9ca4d51e38d9a95a13c293904df5.
----
- drivers/clk/clk-uclass.c | 16 +++++-----------
- 1 file changed, 5 insertions(+), 11 deletions(-)
-
---- a/drivers/clk/clk-uclass.c
-+++ b/drivers/clk/clk-uclass.c
-@@ -88,7 +88,7 @@ static int clk_get_by_index_tail(int ret
-       if (ret) {
-               debug("%s: uclass_get_device_by_of_offset failed: err=%d\n",
-                     __func__, ret);
--              return log_msg_ret("get", ret);
-+              return ret;
-       }
-       clk->dev = dev_clk;
-@@ -101,15 +101,14 @@ static int clk_get_by_index_tail(int ret
-               ret = clk_of_xlate_default(clk, args);
-       if (ret) {
-               debug("of_xlate() failed: %d\n", ret);
--              return log_msg_ret("xlate", ret);
-+              return ret;
-       }
-       return clk_request(dev_clk, clk);
- err:
-       debug("%s: Node '%s', property '%s', failed to request CLK index %d: %d\n",
-             __func__, ofnode_get_name(node), list_name, index, ret);
--
--      return log_msg_ret("prop", ret);
-+      return ret;
- }
- static int clk_get_by_indexed_prop(struct udevice *dev, const char *prop_name,
-@@ -128,7 +127,7 @@ static int clk_get_by_indexed_prop(struc
-       if (ret) {
-               debug("%s: fdtdec_parse_phandle_with_args failed: err=%d\n",
-                     __func__, ret);
--              return log_ret(ret);
-+              return ret;
-       }
-@@ -469,7 +468,6 @@ void clk_free(struct clk *clk)
- ulong clk_get_rate(struct clk *clk)
- {
-       const struct clk_ops *ops;
--      int ret;
-       debug("%s(clk=%p)\n", __func__, clk);
-       if (!clk_valid(clk))
-@@ -479,11 +477,7 @@ ulong clk_get_rate(struct clk *clk)
-       if (!ops->get_rate)
-               return -ENOSYS;
--      ret = ops->get_rate(clk);
--      if (ret)
--              return log_ret(ret);
--
--      return 0;
-+      return ops->get_rate(clk);
- }
- struct clk *clk_get_parent(struct clk *clk)
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/000-mtk-09-board-mediatek-add-more-network-configurations.patch
deleted file mode 100644 (file)
index 50c2ac2..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-From 938ba7ed996a86c9cc7af08b69df57b8b4c09510 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 02/12] 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
-@@ -30,6 +30,7 @@
- /* Ethernet */
- #define CONFIG_IPADDR                 192.168.1.1
--#define CONFIG_SERVERIP                       192.168.1.3
-+#define CONFIG_SERVERIP                       192.168.1.2
-+#define CONFIG_NETMASK                        255.255.255.0
- #endif
---- a/include/configs/mt7623.h
-+++ b/include/configs/mt7623.h
-@@ -45,6 +45,7 @@
- /* Ethernet */
- #define CONFIG_IPADDR                 192.168.1.1
- #define CONFIG_SERVERIP                       192.168.1.2
-+#define CONFIG_NETMASK                        255.255.255.0
- #ifdef CONFIG_DISTRO_DEFAULTS
---- a/include/configs/mt7629.h
-+++ b/include/configs/mt7629.h
-@@ -45,5 +45,6 @@
- /* Ethernet */
- #define CONFIG_IPADDR                 192.168.1.1
- #define CONFIG_SERVERIP                       192.168.1.2
-+#define CONFIG_NETMASK                        255.255.255.0
- #endif
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch b/package/boot/uboot-mediatek/patches/000-mtk-14-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch
deleted file mode 100644 (file)
index 1caa7ff..0000000
+++ /dev/null
@@ -1,3696 +0,0 @@
-From d6c5309185aae3d9ecf80eae8b248522d11a6136 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 04/12] drivers: mtd: add support for MediaTek SPI-NAND flash
- controller
-
-Add mtd driver for MediaTek SPI-NAND flash controller
-
-This driver is written from scratch, and uses standard mtd framework, not
-the nand framework which only applies for raw parallel nand flashes so that
-this driver can have a smaller size in binary.
-
-Signed-off-by: Weijie Gao <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
-@@ -158,6 +158,8 @@ config SYS_MAX_FLASH_BANKS_DETECT
-        to reduce the effective number of flash bank, between 0 and
-        CONFIG_SYS_MAX_FLASH_BANKS
-+source "drivers/mtd/mtk-snand/Kconfig"
-+
- source "drivers/mtd/nand/Kconfig"
- config SYS_NAND_MAX_CHIPS
---- a/drivers/mtd/Makefile
-+++ b/drivers/mtd/Makefile
-@@ -39,3 +39,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR
- obj-$(CONFIG_SPL_UBI) += ubispl/
- endif
-+
-+obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/
---- /dev/null
-+++ b/drivers/mtd/mtk-snand/Kconfig
-@@ -0,0 +1,21 @@
-+#
-+# Copyright (C) 2020 MediaTek Inc. All rights reserved.
-+# Author: Weijie Gao <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,524 @@
-+// 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)
-+              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
deleted file mode 100644 (file)
index 4a06acc..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-From b7fb0e0674db12bcf53df4b107a17c80758ee5d3 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 05/12] mtd: mtk-snand: add support for SPL
-
-Add support to initialize SPI-NAND in SPL.
-Add implementation for SPL NAND loader.
-
-Signed-off-by: Weijie Gao <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
deleted file mode 100644 (file)
index f2e9167..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-From a26620ec83fa3077f0c261046e82091f7455736f 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 06/12] 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
-@@ -48,6 +48,7 @@ DECLARE_GLOBAL_DATA_PTR;
-       defined(CONFIG_ENV_IS_IN_MMC)           || \
-       defined(CONFIG_ENV_IS_IN_FAT)           || \
-       defined(CONFIG_ENV_IS_IN_EXT4)          || \
-+      defined(CONFIG_ENV_IS_IN_MTD)           || \
-       defined(CONFIG_ENV_IS_IN_NAND)          || \
-       defined(CONFIG_ENV_IS_IN_NVRAM)         || \
-       defined(CONFIG_ENV_IS_IN_ONENAND)       || \
-@@ -62,7 +63,7 @@ DECLARE_GLOBAL_DATA_PTR;
- #if   !defined(ENV_IS_IN_DEVICE)              && \
-       !defined(CONFIG_ENV_IS_NOWHERE)
--# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\
-+# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\
- NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
- #endif
---- a/env/Kconfig
-+++ b/env/Kconfig
-@@ -37,7 +37,7 @@ config ENV_IS_NOWHERE
-                    !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
-                    !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
-                    !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
--                   !ENV_IS_IN_UBI
-+                   !ENV_IS_IN_UBI && !ENV_IS_IN_MTD
-       help
-         Define this if you don't want to or can't have an environment stored
-         on a storage medium. In this case the environment will still exist
-@@ -226,6 +226,27 @@ config ENV_IS_IN_MMC
-         This value is also in units of bytes, but must also be aligned to
-         an MMC sector boundary.
-+config ENV_IS_IN_MTD
-+      bool "Environment in a MTD device"
-+      depends on !CHAIN_OF_TRUST
-+      depends on MTD
-+      help
-+        Define this if you have a MTD device which you want to use for
-+        the environment.
-+
-+        - CONFIG_ENV_MTD_NAME:
-+        - CONFIG_ENV_OFFSET:
-+        - CONFIG_ENV_SIZE:
-+
-+        These three #defines specify the MTD device where the environment
-+        is stored, offset and size of the environment area within the MTD
-+        device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary.
-+
-+        - CONFIG_ENV_SIZE_REDUND:
-+
-+        This #define specify the maximum size allowed for read/write/erase
-+        with skipped bad blocks starting from ENV_OFFSET.
-+
- config ENV_IS_IN_NAND
-       bool "Environment in a NAND device"
-       depends on !CHAIN_OF_TRUST
-@@ -531,10 +552,16 @@ config ENV_ADDR_REDUND
-         Offset from the start of the device (or partition) of the redundant
-         environment location.
-+config ENV_MTD_NAME
-+      string "Name of the MTD device storing the environment"
-+      depends on ENV_IS_IN_MTD
-+      help
-+        Name of the MTD device that stores the environment
-+
- config ENV_OFFSET
-       hex "Environment offset"
-       depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
--                  ENV_IS_IN_SPI_FLASH
-+                  ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
-       default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC
-       default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
-       default 0xF0000 if ARCH_SUNXI
-@@ -581,6 +608,12 @@ config ENV_SECT_SIZE
-       help
-         Size of the sector containing the environment.
-+config ENV_SIZE_REDUND
-+      hex "Redundant environment size"
-+      depends on ENV_IS_IN_MTD
-+      help
-+        The maximum size allowed for read/write/erase with skipped bad blocks.
-+
- config ENV_UBI_PART
-       string "UBI partition name"
-       depends on ENV_IS_IN_UBI
---- a/env/Makefile
-+++ b/env/Makefile
-@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE)
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o
-+obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o
- obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o
---- a/env/env.c
-+++ b/env/env.c
-@@ -69,6 +69,9 @@ static enum env_location env_locations[]
- #ifdef CONFIG_ENV_IS_IN_MMC
-       ENVL_MMC,
- #endif
-+#ifdef CONFIG_ENV_IS_IN_MTD
-+      ENVL_MTD,
-+#endif
- #ifdef CONFIG_ENV_IS_IN_NAND
-       ENVL_NAND,
- #endif
---- /dev/null
-+++ b/env/mtd.c
-@@ -0,0 +1,256 @@
-+/* SPDX-License-Identifier: GPL-2.0 */
-+/*
-+ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
-+ *
-+ * Author: Weijie Gao <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
-@@ -130,6 +130,7 @@ enum env_location {
-       ENVL_FAT,
-       ENVL_FLASH,
-       ENVL_MMC,
-+      ENVL_MTD,
-       ENVL_NAND,
-       ENVL_NVRAM,
-       ENVL_ONENAND,
---- a/tools/Makefile
-+++ b/tools/Makefile
-@@ -41,6 +41,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y
-+ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y
- ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch b/package/boot/uboot-mediatek/patches/000-mtk-17-board-mt7629-add-support-for-booting-from-SPI-NAND.patch
deleted file mode 100644 (file)
index 4ee3d6f..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-From 3757223c3354b9feeffcbe916eb18eb8873bd133 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 07/12] board: mt7629: add support for booting from SPI-NAND
-
-Add support for mt7629 to boot from SPI-NAND.
-Add a new defconfig for mt7629+spi-nand configuration.
-
-Signed-off-by: Weijie Gao <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>;
---- /dev/null
-+++ b/configs/mt7629_nand_rfb_defconfig
-@@ -0,0 +1,111 @@
-+CONFIG_ARM=y
-+CONFIG_SYS_ARCH_TIMER=y
-+CONFIG_SYS_THUMB_BUILD=y
-+CONFIG_ARCH_MEDIATEK=y
-+CONFIG_SYS_TEXT_BASE=0x41e00000
-+CONFIG_SYS_MALLOC_F_LEN=0x4000
-+CONFIG_NR_DRAM_BANKS=1
-+CONFIG_ENV_SIZE=0x20000
-+CONFIG_ENV_OFFSET=0x100000
-+CONFIG_SPL_TEXT_BASE=0x201000
-+CONFIG_TARGET_MT7629=y
-+CONFIG_BOOT_FROM_SNAND_2K_64=y
-+CONFIG_SPL_SERIAL_SUPPORT=y
-+CONFIG_SPL_DRIVERS_MISC_SUPPORT=y
-+CONFIG_SPL_STACK_R_ADDR=0x40800000
-+CONFIG_SPL_PAYLOAD="u-boot.img"
-+CONFIG_BUILD_TARGET="u-boot-mtk.bin"
-+CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb"
-+CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin"
-+CONFIG_FIT=y
-+CONFIG_FIT_VERBOSE=y
-+CONFIG_BOOTDELAY=3
-+CONFIG_DEFAULT_FDT_FILE="mt7629-rfb"
-+CONFIG_SYS_CONSOLE_IS_IN_ENV=y
-+CONFIG_SYS_STDIO_DEREGISTER=y
-+# CONFIG_DISPLAY_BOARDINFO is not set
-+CONFIG_SPL_SYS_MALLOC_SIMPLE=y
-+CONFIG_SPL_STACK_R=y
-+CONFIG_SPL_MTD_SUPPORT=y
-+CONFIG_SPL_NAND_SUPPORT=y
-+CONFIG_SPL_WATCHDOG_SUPPORT=y
-+CONFIG_HUSH_PARSER=y
-+CONFIG_SYS_PROMPT="U-Boot> "
-+CONFIG_CMD_BOOTMENU=y
-+# CONFIG_BOOTM_NETBSD is not set
-+# CONFIG_BOOTM_PLAN9 is not set
-+# CONFIG_BOOTM_RTEMS is not set
-+# CONFIG_BOOTM_VXWORKS is not set
-+# CONFIG_CMD_ELF is not set
-+# CONFIG_CMD_XIMG is not set
-+CONFIG_CMD_BIND=y
-+CONFIG_CMD_DM=y
-+# CONFIG_CMD_FLASH is not set
-+CONFIG_CMD_GPIO=y
-+CONFIG_CMD_MTD=y
-+CONFIG_CMD_USB=y
-+# CONFIG_CMD_SETEXPR is not set
-+# CONFIG_CMD_NFS is not set
-+CONFIG_CMD_PING=y
-+CONFIG_CMD_FAT=y
-+CONFIG_CMD_FS_GENERIC=y
-+CONFIG_CMD_LOG=y
-+CONFIG_EFI_PARTITION=y
-+# CONFIG_SPL_PARTITION_UUIDS is not set
-+CONFIG_PARTITION_TYPE_GUID=y
-+CONFIG_OF_SPL_REMOVE_PROPS="interrupt-parent assigned-clocks assigned-clock-parents"
-+CONFIG_ENV_OVERWRITE=y
-+CONFIG_ENV_IS_IN_MTD=y
-+CONFIG_ENV_MTD_NAME="spi-nand0"
-+CONFIG_ENV_SIZE_REDUND=0x40000
-+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
-+CONFIG_NET_RANDOM_ETHADDR=y
-+CONFIG_SPL_DM_SEQ_ALIAS=y
-+CONFIG_REGMAP=y
-+CONFIG_SPL_REGMAP=y
-+CONFIG_SYSCON=y
-+CONFIG_SPL_SYSCON=y
-+CONFIG_BLK=y
-+CONFIG_CLK=y
-+CONFIG_SPL_CLK=y
-+# CONFIG_MMC is not set
-+CONFIG_MTD=y
-+CONFIG_DM_MTD=y
-+CONFIG_MTK_SPI_NAND=y
-+CONFIG_MTK_SPI_NAND_MTD=y
-+CONFIG_SPL_MTK_SPI_NAND=y
-+CONFIG_DM_ETH=y
-+CONFIG_MEDIATEK_ETH=y
-+CONFIG_PHY=y
-+CONFIG_PHY_MTK_TPHY=y
-+CONFIG_PINCTRL=y
-+CONFIG_PINCONF=y
-+CONFIG_SPL_PINCTRL=y
-+CONFIG_SPL_PINCONF=y
-+CONFIG_PINCTRL_MT7629=y
-+CONFIG_POWER_DOMAIN=y
-+CONFIG_MTK_POWER_DOMAIN=y
-+CONFIG_DM_REGULATOR=y
-+CONFIG_DM_REGULATOR_FIXED=y
-+CONFIG_RAM=y
-+CONFIG_SPL_RAM=y
-+CONFIG_DM_SERIAL=y
-+CONFIG_MTK_SERIAL=y
-+CONFIG_SPI=y
-+CONFIG_DM_SPI=y
-+CONFIG_SPI_MEM=y
-+CONFIG_MTK_SNFI_SPI=y
-+CONFIG_SYSRESET=y
-+CONFIG_SPL_SYSRESET=y
-+CONFIG_SYSRESET_WATCHDOG=y
-+CONFIG_USB=y
-+CONFIG_DM_USB=y
-+# CONFIG_SPL_DM_USB is not set
-+CONFIG_USB_XHCI_HCD=y
-+CONFIG_USB_XHCI_MTK=y
-+CONFIG_USB_STORAGE=y
-+CONFIG_WDT_MTK=y
-+CONFIG_FAT_WRITE=y
-+CONFIG_LZMA=y
-+CONFIG_SPL_LZMA=y
-+# CONFIG_EFI_LOADER is not set
---- a/include/configs/mt7629.h
-+++ b/include/configs/mt7629.h
-@@ -25,12 +25,19 @@
- /* Defines for SPL */
- #define CONFIG_SPL_STACK              0x106000
-+#ifdef CONFIG_MT7629_BOOT_FROM_SNAND
-+#define CONFIG_SPL_MAX_SIZE           SZ_128K
-+#define CONFIG_SPL_MAX_FOOTPRINT      SZ_128K
-+#define CONFIG_SPL_PAD_TO             0x20000
-+#define CONFIG_SYS_NAND_U_BOOT_OFFS   CONFIG_SPL_PAD_TO
-+#else
- #define CONFIG_SPL_MAX_SIZE           SZ_64K
- #define CONFIG_SPL_MAX_FOOTPRINT      SZ_64K
- #define CONFIG_SPL_PAD_TO             0x10000
- #define CONFIG_SPI_ADDR                       0x30000000
- #define CONFIG_SYS_UBOOT_BASE         (CONFIG_SPI_ADDR + CONFIG_SPL_PAD_TO)
-+#endif
- /* SPL -> Uboot */
- #define CONFIG_SYS_INIT_SP_ADDR               (CONFIG_SYS_TEXT_BASE + SZ_2M - \
diff --git a/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/000-mtk-18-board-mt7622-use-new-spi-nand-driver.patch
deleted file mode 100644 (file)
index 84101d0..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-From 6bcd65ed47844e747ff6db066b092632f1760256 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 08/12] 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
-@@ -16,6 +16,7 @@ CONFIG_LOG=y
- CONFIG_SYS_PROMPT="MT7622> "
- CONFIG_CMD_BOOTMENU=y
- CONFIG_CMD_MMC=y
-+CONFIG_CMD_MTD=y
- CONFIG_CMD_PCI=y
- CONFIG_CMD_SF_TEST=y
- CONFIG_CMD_PING=y
-@@ -28,6 +29,10 @@ CONFIG_SYSCON=y
- CONFIG_CLK=y
- CONFIG_MMC_HS200_SUPPORT=y
- CONFIG_MMC_MTK=y
-+CONFIG_MTD=y
-+CONFIG_DM_MTD=y
-+CONFIG_MTK_SPI_NAND=y
-+CONFIG_MTK_SPI_NAND_MTD=y
- CONFIG_DM_SPI_FLASH=y
- CONFIG_SPI_FLASH_EON=y
- CONFIG_SPI_FLASH_GIGADEVICE=y
index cc5fb573247ca0c5d34510305f42fac527c97355..cf2e48d5c4887fd26e08cc244876395e7e7386a8 100644 (file)
@@ -21,7 +21,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
  CONFIG_DEBUG_UART_BASE=0x11002000
  CONFIG_DEBUG_UART_CLOCK=25000000
  CONFIG_SYS_LOAD_ADDR=0x4007ff28
-@@ -22,6 +24,9 @@ CONFIG_CMD_SF_TEST=y
+@@ -21,6 +23,9 @@ CONFIG_CMD_SF_TEST=y
  CONFIG_CMD_PING=y
  CONFIG_CMD_SMC=y
  CONFIG_ENV_OVERWRITE=y
index 63e189f56ceb43f93abb21ce87d85a0303ccc0ec..603896f2605a6e5466abcae2c0adb4d00796202b 100644 (file)
@@ -34,7 +34,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
  
 --- a/arch/arm/dts/mt7622-rfb.dts
 +++ b/arch/arm/dts/mt7622-rfb.dts
-@@ -196,8 +196,6 @@
+@@ -189,8 +189,6 @@
  };
  
  &uart0 {
index 085d66776fd85f17c7a17c4d899f2a62a410e419..7ed6083c8956e63fea2a40fad16c67f53ec59c11 100644 (file)
@@ -16,7 +16,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 
 --- a/arch/arm/dts/mt7622.dtsi
 +++ b/arch/arm/dts/mt7622.dtsi
-@@ -191,6 +191,7 @@
+@@ -175,6 +175,7 @@
                status = "disabled";
                assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>;
                assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>;
index 415b37ba559e9120ac1b63bf513ba827348df632..378078882e5a2b742a22b3a626ff79146b090984 100644 (file)
@@ -29,7 +29,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
  };
  
  &snfi {
-@@ -249,3 +257,13 @@
+@@ -242,3 +250,13 @@
  &u3phy {
         status = "okay";
  };
@@ -45,7 +45,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 +};
 --- a/arch/arm/dts/mt7622.dtsi
 +++ b/arch/arm/dts/mt7622.dtsi
-@@ -440,4 +440,28 @@
+@@ -424,4 +424,28 @@
                status = "disabled";
        };
  
index 8ecfda2d464c7c8546bcb7110eed2a8df1d7772a..9a5332f6950bcb5973a05d72cf91bed4759bbac2 100644 (file)
@@ -24,7 +24,7 @@ Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
 
 --- a/tools/Makefile
 +++ b/tools/Makefile
-@@ -148,6 +148,7 @@ dumpimage-mkimage-objs := aisimage.o \
+@@ -147,6 +147,7 @@ dumpimage-mkimage-objs := aisimage.o \
                        gpimage.o \
                        gpimage-common.o \
                        mtk_image.o \
diff --git a/package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch b/package/boot/uboot-mediatek/patches/003-mtd-spi-nor-ids-Add-support-for-flashes-tested-by-xi.patch
new file mode 100644 (file)
index 0000000..e54b46f
--- /dev/null
@@ -0,0 +1,182 @@
+From baef13ec9d592a27b5d3bf03967bfd2bebd65157 Mon Sep 17 00:00:00 2001
+From: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
+Date: Wed, 25 May 2022 10:47:12 +0530
+Subject: [PATCH] mtd: spi-nor-ids: Add support for flashes tested by xilinx
+
+Add support for various flashes from below manufacturers which are tested
+by xilinx for years.
+
+EON:
+       en25q128b
+GIGA:
+       gd25lx256e
+ISSI:
+       is25lp008
+       is25lp016
+       is25lp01g
+       is25wp008
+       is25wp016
+       is25wp01g
+       is25wx256
+MACRONIX:
+       mx25u51245f
+       mx66u1g45g
+       mx66l2g45g
+MICRON:
+       mt35xl512aba
+       mt35xu01g
+SPANSION:
+       s70fs01gs_256k
+SST:
+       sst26wf016b
+WINBOND:
+       w25q16dw
+       w25q16jv
+       w25q512jv
+       w25q32bv
+       w25h02jv
+
+Signed-off-by: Ashok Reddy Soma <ashok.reddy.soma@xilinx.com>
+Link: https://lore.kernel.org/r/1653455832-14763-1-git-send-email-ashok.reddy.soma@xilinx.com
+Signed-off-by: Michal Simek <michal.simek@amd.com>
+---
+ drivers/mtd/spi/spi-nor-ids.c | 37 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 37 insertions(+)
+
+--- a/drivers/mtd/spi/spi-nor-ids.c
++++ b/drivers/mtd/spi/spi-nor-ids.c
+@@ -82,6 +82,7 @@ const struct flash_info spi_nor_ids[] =
+       /* EON -- en25xxx */
+       { INFO("en25q32b",   0x1c3016, 0, 64 * 1024,   64, 0) },
+       { INFO("en25q64",    0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
++      { INFO("en25q128b",  0x1c3018, 0, 64 * 1024,  256, 0) },
+       { INFO("en25qh128",  0x1c7018, 0, 64 * 1024,  256, 0) },
+       { INFO("en25s64",    0x1c3817, 0, 64 * 1024,  128, SECT_4K) },
+ #endif
+@@ -127,11 +128,17 @@ const struct flash_info spi_nor_ids[] =
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
++      {
++              INFO("gd25lx256e", 0xc86819, 0, 64 * 1024, 512,
++                   SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES)
++      },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_ISSI          /* ISSI */
+       /* ISSI */
+       { INFO("is25lq040b", 0x9d4013, 0, 64 * 1024,   8,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("is25lp008", 0x9d6014, 0, 64 * 1024,  16, SPI_NOR_QUAD_READ) },
++      { INFO("is25lp016", 0x9d6015, 0, 64 * 1024,  32, SPI_NOR_QUAD_READ) },
+       { INFO("is25lp032",     0x9d6016, 0, 64 * 1024,  64, 0) },
+       { INFO("is25lp064",     0x9d6017, 0, 64 * 1024, 128, 0) },
+       { INFO("is25lp128",  0x9d6018, 0, 64 * 1024, 256,
+@@ -140,6 +147,10 @@ const struct flash_info spi_nor_ids[] =
+                       SECT_4K | SPI_NOR_DUAL_READ) },
+       { INFO("is25lp512",  0x9d601a, 0, 64 * 1024, 1024,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("is25lp01g",  0x9d601b, 0, 64 * 1024, 2048,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("is25wp008", 0x9d7014, 0, 64 * 1024,  16, SPI_NOR_QUAD_READ) },
++      { INFO("is25wp016", 0x9d7015, 0, 64 * 1024,  32, SPI_NOR_QUAD_READ) },
+       { INFO("is25wp032",  0x9d7016, 0, 64 * 1024,  64,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("is25wp064",  0x9d7017, 0, 64 * 1024, 128,
+@@ -151,6 +162,10 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_4B_OPCODES) },
+       { INFO("is25wp512",  0x9d701a, 0, 64 * 1024, 1024,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("is25wp01g",  0x9d701b, 0, 64 * 1024, 2048,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("is25wx256",  0x9d5b19, 0, 128 * 1024, 256,
++                      SECT_4K | USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_MACRONIX      /* MACRONIX */
+       /* Macronix */
+@@ -176,8 +191,11 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("mx25l25655e", 0xc22619, 0, 64 * 1024, 512, 0) },
+       { INFO("mx66l51235l", 0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mx66u51235f", 0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25u51245f", 0xc2953a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66u1g45g",  0xc2253b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mx66u2g45g",  0xc2253c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mx66l1g45g",  0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("mx66l2g45g",  0xc2201c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mx25l1633e", 0xc22415, 0, 64 * 1024,   32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) },
+       { INFO("mx25r6435f", 0xc22817, 0, 64 * 1024,   128,  SECT_4K) },
+       { INFO("mx66uw2g345g", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+@@ -208,8 +226,10 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("mt25qu02g",   0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
+       { INFO("mt25ql02g",   0x20ba22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE | SPI_NOR_4B_OPCODES) },
+ #ifdef CONFIG_SPI_FLASH_MT35XU
++      { INFO("mt35xl512aba", 0x2c5a1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
+       { INFO("mt35xu512aba", 0x2c5b1a, 0,  128 * 1024,  512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
+ #endif /* CONFIG_SPI_FLASH_MT35XU */
++      { INFO6("mt35xu01g",  0x2c5b1b, 0x104100, 128 * 1024,  1024, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mt35xu02g",  0x2c5b1c, 0, 128 * 1024,  2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_SPANSION      /* SPANSION */
+@@ -225,6 +245,7 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("s25fl512s_256k",  0x010220, 0x4d00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+       { INFO("s25fl512s_64k",  0x010220, 0x4d01, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+       { INFO("s25fl512s_512k", 0x010220, 0x4f00, 256 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
++      { INFO("s70fs01gs_256k", 0x010221, 0x4d00, 256 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("s25sl12800", 0x012018, 0x0300, 256 * 1024,  64, 0) },
+       { INFO("s25sl12801", 0x012018, 0x0301,  64 * 1024, 256, 0) },
+       { INFO6("s25fl128s",  0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | USE_CLSR) },
+@@ -275,6 +296,7 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("sst25wf040",  0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
+       { INFO("sst25wf080",  0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
+       { INFO("sst26vf064b", 0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("sst26wf016b", 0xbf2641, 0, 64 * 1024,  32, SECT_4K) },
+       { INFO("sst26wf016",  0xbf2651, 0, 64 * 1024,  32, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
+       { INFO("sst26wf032",  0xbf2622, 0, 64 * 1024,  64, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
+       { INFO("sst26wf064",  0xbf2643, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_HAS_SST26LOCK) },
+@@ -312,11 +334,19 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("w25q20ew", 0xef6012, 0, 64 * 1024,  4, SECT_4K) },
+       { INFO("w25q32", 0xef4016, 0, 64 * 1024,  64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       {
++              INFO("w25q16dw", 0xef6015, 0, 64 * 1024,  32,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
++      },
++      {
+               INFO("w25q32dw", 0xef6016, 0, 64 * 1024,  64,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       {
++              INFO("w25q16jv", 0xef7015, 0, 64 * 1024,  32,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
++      },
++      {
+               INFO("w25q32jv", 0xef7016, 0, 64 * 1024,  64,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+@@ -363,6 +393,11 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       {
++              INFO("w25q512jv", 0xef7119, 0, 64 * 1024, 512,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
+               INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+@@ -370,6 +405,7 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("w25q80", 0xef5014, 0, 64 * 1024,  16, SECT_4K) },
+       { INFO("w25q80bl", 0xef4014, 0, 64 * 1024,  16, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25q16cl", 0xef4015, 0, 64 * 1024,  32, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("w25q32bv", 0xef4016, 0, 64 * 1024,  64, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25q64cv", 0xef4017, 0, 64 * 1024,  128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25q128", 0xef4018, 0, 64 * 1024, 256,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+@@ -378,6 +414,7 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_XMC
+       /* XMC (Wuhan Xinxin Semiconductor Manufacturing Corp.) */
diff --git a/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-core-Add-support-for-Macronix-Octal-flas.patch
new file mode 100644 (file)
index 0000000..b0299ac
--- /dev/null
@@ -0,0 +1,614 @@
+From 4290ed7835e0a76792b8e554ae79e3f6d52ac800 Mon Sep 17 00:00:00 2001
+From: JaimeLiao <jaimeliao.tw@gmail.com>
+Date: Mon, 18 Jul 2022 14:49:22 +0800
+Subject: [PATCH] mtd: spi-nor-core: Add support for Macronix Octal flash
+
+Adding Macronix Octal flash for Octal DTR support.
+
+The octaflash series can be divided into the following types:
+
+MX25 series : Serial NOR Flash.
+MX66 series : Serial NOR Flash with stacked die.(Size larger than 1Gb)
+LM/UM series : Up to 250MHz clock frequency with both DTR/STR operation.
+LW/UW series : Support simultaneous Read-while-Write operation in multiple
+               bank architecture. Read-while-write feature which means read
+               data one bank while another bank is programing or erasing.
+
+MX25LM : 3.0V Octal I/O
+ -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7841/MX25LM51245G,%203V,%20512Mb,%20v1.1.pdf
+
+MX25UM : 1.8V Octal I/O
+ -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7525/MX25UM51245G%20Extreme%20Speed,%201.8V,%20512Mb,%20v1.0.pdf
+
+MX66LM : 3.0V Octal I/O with stacked die
+ -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7929/MX66LM1G45G,%203V,%201Gb,%20v1.1.pdf
+
+MX66UM : 1.8V Octal I/O with stacked die
+ -https://www.mxic.com.tw/Lists/Datasheet/Attachments/7721/MX66UM1G45G,%201.8V,%201Gb,%20v1.1.pdf
+
+MX25LW : 3.0V Octal I/O with Read-while-Write
+MX25UW : 1.8V Octal I/O with Read-while-Write
+MX66LW : 3.0V Octal I/O with Read-while-Write and stack die
+MX66UW : 1.8V Octal I/O with Read-while-Write and stack die
+
+About LW/UW series, please contact us freely if you have any
+questions. For adding Octal NOR Flash IDs, we have validated
+each Flash on plateform zynq-picozed.
+
+As below are the SFDP table dump.
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2943c
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx66uw2g345gx0
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw2g345gx0
+zynq> hexdump mx66uw2g345gx0
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 7fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7987 0001 1284 e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 001f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2853b
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx66lm1g45g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66lm1g45g
+zynq> hexdump mx66lm1g45g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 6987 0001 1282 e200 02cc 3867
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 6666
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000
+0000130 3514 001c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2853a
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25lm51245g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lm51245g
+zynq> hexdump mx25lm51245g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7989 0001 128d e200 02cc 4467
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 6666
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000
+0000130 3514 001c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2863a
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25lw51245g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lw51245g
+zynq> hexdump mx25lw51245g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 0000 0000 0000 0000
+0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 6666
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000
+0000130 3514 001c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28539
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25lm25645g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25lm25645g
+zynq> hexdump mx25lm25645g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 6987 0001 1282 d200 02cc 3867
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 6666
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000
+0000130 3514 001c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2843c
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx66uw2g345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw2g345g
+zynq> hexdump mx66uw2g345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 7fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7987 0001 1284 e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 001f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2803b
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx66um1g45g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66um1g45g
+zynq> hexdump mx66um1g45g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7989 0001 128d e200 02cc 4467
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 3514 809c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2813b
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx66uw1g45g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx66uw1g45g
+zynq> hexdump mx66uw1g45g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 3fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2813a
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw51245g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw51245g
+zynq> hexdump mx25uw51245g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 7777
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0000 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c2843a
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw51345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw51345g
+zynq> hexdump mx25uw51345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 1fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f e200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28039
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25um25645g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25um25645g
+zynq> random: fast init done
+zynq> hexdump mx25um25645g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7987 0001 1284 d200 02cc 3867
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 3514 809c 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28139
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw25645g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw25645g
+zynq> hexdump mx25uw25645g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7989 0001 128d d200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28339
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25um25345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25um25345g
+zynq> hexdump mx25um25345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 6987 0001 1282 d200 02cc 3867
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0904 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28439
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw25345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw25345g
+zynq> hexdump mx25uw25345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 0fff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7987 0001 1284 d200 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28138
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw12845g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw12845g
+zynq> hexdump mx25uw12845g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 0000 0000 0000 0000
+0000040 20e5 ff8a ffff 07ff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f c900 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28438
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw12345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw12345g
+zynq> hexdump mx25uw12345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 07ff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f c900 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28137
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw6445g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw6445g
+zynq> hexdump mx25uw6445g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 03ff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 7989 0001 128d c400 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 a37c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
+c28437
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
+macronix
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
+mx25uw6345g
+zynq> cat /sys/bus/spi/devices/spi0.0/spi-nor/sfdp > mx25uw6345g
+zynq> hexdump mx25uw6345g
+0000000 4653 5044 0108 fd04 0700 1401 0040 ff00
+0000010 0187 1c01 0090 ff00 000a 0801 0100 ff00
+0000020 0005 0501 0120 ff00 0084 0201 0134 ff00
+0000030 0000 0000 0000 0000 ffff ffff ffff ffff
+0000040 20e5 ff8a ffff 03ff ff00 ff00 ff00 ff00
+0000050 ffee ffff ffff ff00 ffff ff00 200c d810
+0000060 ff00 ff00 798b 0001 128f c400 04cc 4667
+0000070 b030 b030 bdf4 5cd5 0000 ff00 1010 2000
+0000080 0000 0000 0000 237c 0048 0000 0000 8888
+0000090 0000 0000 0000 4000 d10f f3ff d10f f3ff
+00000a0 0500 9000 0500 b100 2b00 9500 2b00 9600
+00000b0 7172 b803 7172 b803 0000 0000 a390 8218
+00000c0 c000 9669 0000 0000 0000 0000 7172 9800
+00000d0 7172 b800 7172 9900 0000 0000 7172 9800
+00000e0 7172 f800 7172 9900 7172 f900 0000 0000
+00000f0 0000 0000 1501 d001 7172 d806 0000 5086
+0000100 0000 0106 0000 0000 0002 0301 0200 0000
+0000110 0000 0106 0000 0000 0000 0672 0200 0000
+0000120 ee00 69c0 7272 7171 d800 f6f7 0a00 0000
+0000130 4514 8098 0643 000f dc21 ffff ffff ffff
+0000140 ffff ffff ffff ffff ffff ffff ffff ffff
+
+Signed-off-by: JaimeLiao <jaimeliao.tw@gmail.com>
+Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
+---
+ drivers/mtd/spi/spi-nor-ids.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi/spi-nor-ids.c
++++ b/drivers/mtd/spi/spi-nor-ids.c
+@@ -198,7 +198,24 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("mx66l2g45g",  0xc2201c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
+       { INFO("mx25l1633e", 0xc22415, 0, 64 * 1024,   32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) },
+       { INFO("mx25r6435f", 0xc22817, 0, 64 * 1024,   128,  SECT_4K) },
+-      { INFO("mx66uw2g345g", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66uw2g345gx0", 0xc2943c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66lm1g45g",    0xc2853b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25lm51245g",   0xc2853a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25lw51245g",   0xc2863a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25lm25645g",   0xc28539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66uw2g345g",   0xc2843c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66um1g45g",    0xc2803b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx66uw1g45g",    0xc2813b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw51245g",   0xc2813a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw51345g",   0xc2843a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25um25645g",   0xc28039, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw25645g",   0xc28139, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25um25345g",   0xc28339, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw25345g",   0xc28439, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw12845g",   0xc28138, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw12345g",   0xc28438, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw6445g",    0xc28137, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
++      { INFO("mx25uw6345g",    0xc28437, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_OCTAL_DTR_READ | SPI_NOR_4B_OPCODES) },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_STMICRO               /* STMICRO */
diff --git a/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch b/package/boot/uboot-mediatek/patches/004-mtd-spi-nor-ids-add-winbond-w25q512nw-family-support.patch
new file mode 100644 (file)
index 0000000..b881200
--- /dev/null
@@ -0,0 +1,39 @@
+From 47ed8b22fd561b65e8541919becc76ab3d86f7a3 Mon Sep 17 00:00:00 2001
+From: Jae Hyun Yoo <quic_jaehyoo@quicinc.com>
+Date: Fri, 8 Jul 2022 12:03:19 -0700
+Subject: [PATCH] mtd: spi-nor-ids: add winbond w25q512nw family support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add Winbond w25q512nwq/n and w25q512nwm support.
+
+datasheet:
+https://www.winbond.com/resource-files/W25Q512NW%20RevB%2007192021.pdf
+
+Signed-off-by: Jae Hyun Yoo <quic_jaehyoo@quicinc.com>
+Reviewed-by: Cédric Le Goater <clg@kaod.org>
+Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
+---
+ drivers/mtd/spi/spi-nor-ids.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/mtd/spi/spi-nor-ids.c
++++ b/drivers/mtd/spi/spi-nor-ids.c
+@@ -415,6 +415,16 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       {
++              INFO("w25q512nwq", 0xef6020, 0, 64 * 1024, 1024,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
++              INFO("w25q512nwm", 0xef8020, 0, 64 * 1024, 1024,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
+               INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
diff --git a/package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch b/package/boot/uboot-mediatek/patches/100-00-clk-remove-log_ret-from-clk_get_rate.patch
new file mode 100644 (file)
index 0000000..30dcffd
--- /dev/null
@@ -0,0 +1,43 @@
+From 19f2aa053d5531a9ca0ece04dca172a522d58b90 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Fri, 29 Jul 2022 11:32:28 +0800
+Subject: [PATCH 32/71] clk: remove log_ret from clk_get_rate
+
+The return value of clk_get_rate is ulong, an unsigned type. The size of
+ulong depends on the cpu architecture, i.e. 4 bytes on 32-bit CPUs and
+8 bytes on 64-bit CPUs.
+
+However log_ret only accepts and returns value in int type, a fixed 4-byte
+type. This may truncate the real clock value and cause unexpected error on
+64-bit platforms.
+
+This patch removes log_ret to solve this issue.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/clk/clk-uclass.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+--- a/drivers/clk/clk-uclass.c
++++ b/drivers/clk/clk-uclass.c
+@@ -469,7 +469,6 @@ void clk_free(struct clk *clk)
+ ulong clk_get_rate(struct clk *clk)
+ {
+       const struct clk_ops *ops;
+-      int ret;
+       debug("%s(clk=%p)\n", __func__, clk);
+       if (!clk_valid(clk))
+@@ -479,11 +478,7 @@ ulong clk_get_rate(struct clk *clk)
+       if (!ops->get_rate)
+               return -ENOSYS;
+-      ret = ops->get_rate(clk);
+-      if (ret)
+-              return log_ret(ret);
+-
+-      return 0;
++      return ops->get_rate(clk);
+ }
+ struct clk *clk_get_parent(struct clk *clk)
diff --git a/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch b/package/boot/uboot-mediatek/patches/100-01-board-mediatek-add-more-network-configurations.patch
new file mode 100644 (file)
index 0000000..9591463
--- /dev/null
@@ -0,0 +1,70 @@
+From 97df847f8f895cc2692bb0e4e933269c275da378 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 35/71] 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 +
+ include/configs/mt7981.h | 5 +++++
+ include/configs/mt7986.h | 5 +++++
+ 5 files changed, 14 insertions(+), 1 deletion(-)
+
+--- a/include/configs/mt7622.h
++++ b/include/configs/mt7622.h
+@@ -30,6 +30,7 @@
+ /* Ethernet */
+ #define CONFIG_IPADDR                 192.168.1.1
+-#define CONFIG_SERVERIP                       192.168.1.3
++#define CONFIG_SERVERIP                       192.168.1.2
++#define CONFIG_NETMASK                        255.255.255.0
+ #endif
+--- a/include/configs/mt7623.h
++++ b/include/configs/mt7623.h
+@@ -45,6 +45,7 @@
+ /* Ethernet */
+ #define CONFIG_IPADDR                 192.168.1.1
+ #define CONFIG_SERVERIP                       192.168.1.2
++#define CONFIG_NETMASK                        255.255.255.0
+ #ifdef CONFIG_DISTRO_DEFAULTS
+--- a/include/configs/mt7629.h
++++ b/include/configs/mt7629.h
+@@ -45,5 +45,6 @@
+ /* Ethernet */
+ #define CONFIG_IPADDR                 192.168.1.1
+ #define CONFIG_SERVERIP                       192.168.1.2
++#define CONFIG_NETMASK                        255.255.255.0
+ #endif
+--- a/include/configs/mt7981.h
++++ b/include/configs/mt7981.h
+@@ -23,4 +23,9 @@
+ /* DRAM */
+ #define CONFIG_SYS_SDRAM_BASE         0x40000000
++/* Ethernet */
++#define CONFIG_IPADDR                 192.168.1.1
++#define CONFIG_SERVERIP                       192.168.1.2
++#define CONFIG_NETMASK                        255.255.255.0
++
+ #endif
+--- a/include/configs/mt7986.h
++++ b/include/configs/mt7986.h
+@@ -23,4 +23,9 @@
+ /* DRAM */
+ #define CONFIG_SYS_SDRAM_BASE         0x40000000
++/* Ethernet */
++#define CONFIG_IPADDR                 192.168.1.1
++#define CONFIG_SERVERIP                       192.168.1.2
++#define CONFIG_NETMASK                        255.255.255.0
++
+ #endif
diff --git a/package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch b/package/boot/uboot-mediatek/patches/100-02-drivers-mtd-add-support-for-MediaTek-SPI-NAND-flash-.patch
new file mode 100644 (file)
index 0000000..e2316c6
--- /dev/null
@@ -0,0 +1,4000 @@
+From f7704275957852cd4c4632d6da126979ef24b83a 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 36/71] drivers: mtd: add support for MediaTek SPI-NAND flash
+ controller
+
+Add mtd driver for MediaTek SPI-NAND flash controller
+
+This driver is written from scratch, and uses standard mtd framework, not
+the nand framework which only applies for raw parallel nand flashes so that
+this driver can have a smaller size in binary.
+
+Signed-off-by: Weijie Gao <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 |  271 ++++
+ drivers/mtd/mtk-snand/mtk-snand-ecc.c |  395 +++++
+ drivers/mtd/mtk-snand/mtk-snand-ids.c |  511 +++++++
+ drivers/mtd/mtk-snand/mtk-snand-mtd.c |  535 +++++++
+ drivers/mtd/mtk-snand/mtk-snand-os.c  |   39 +
+ drivers/mtd/mtk-snand/mtk-snand-os.h  |  120 ++
+ drivers/mtd/mtk-snand/mtk-snand.c     | 1933 +++++++++++++++++++++++++
+ drivers/mtd/mtk-snand/mtk-snand.h     |   77 +
+ 12 files changed, 3917 insertions(+)
+ create mode 100644 drivers/mtd/mtk-snand/Kconfig
+ create mode 100644 drivers/mtd/mtk-snand/Makefile
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-def.h
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ecc.c
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-ids.c
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-mtd.c
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.c
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-os.h
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand.c
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand.h
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -158,6 +158,8 @@ config SYS_MAX_FLASH_BANKS_DETECT
+        to reduce the effective number of flash bank, between 0 and
+        CONFIG_SYS_MAX_FLASH_BANKS
++source "drivers/mtd/mtk-snand/Kconfig"
++
+ source "drivers/mtd/nand/Kconfig"
+ config SYS_NAND_MAX_CHIPS
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -39,3 +39,5 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPOR
+ obj-$(CONFIG_SPL_UBI) += ubispl/
+ endif
++
++obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/Kconfig
+@@ -0,0 +1,21 @@
++#
++# Copyright (C) 2020 MediaTek Inc. All rights reserved.
++# Author: Weijie Gao <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,271 @@
++/* 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;
++
++      uint16_t latch_lat;
++      uint16_t sample_delay;
++};
++
++enum mtk_ecc_regs {
++      ECC_DECDONE,
++};
++
++struct mtk_ecc_soc_data {
++      const uint8_t *ecc_caps;
++      uint32_t num_ecc_cap;
++      const uint32_t *regs;
++      uint16_t mode_shift;
++      uint8_t errnum_bits;
++      uint8_t errnum_shift;
++};
++
++struct mtk_snand {
++      struct mtk_snand_plat_dev *pdev;
++
++      void __iomem *nfi_base;
++      void __iomem *ecc_base;
++
++      enum mtk_snand_soc soc;
++      const struct mtk_snand_soc_data *nfi_soc;
++      const struct mtk_ecc_soc_data *ecc_soc;
++      bool snfi_quad_spi;
++      bool quad_spi_op;
++
++      const char *model;
++      uint64_t size;
++      uint64_t die_size;
++      uint32_t erasesize;
++      uint32_t writesize;
++      uint32_t oobsize;
++
++      uint32_t num_dies;
++      snand_select_die_t select_die;
++
++      uint8_t opcode_rfc;
++      uint8_t opcode_pl;
++      uint8_t dummy_rfc;
++      uint8_t mode_rfc;
++      uint8_t mode_pl;
++
++      uint32_t writesize_mask;
++      uint32_t writesize_shift;
++      uint32_t erasesize_mask;
++      uint32_t erasesize_shift;
++      uint64_t die_mask;
++      uint32_t die_shift;
++
++      uint32_t spare_per_sector;
++      uint32_t raw_sector_size;
++      uint32_t ecc_strength;
++      uint32_t ecc_steps;
++      uint32_t ecc_bytes;
++      uint32_t ecc_parity_bits;
++
++      uint8_t *page_cache;    /* Used by read/write page */
++      uint8_t *buf_cache;     /* Used by block bad/markbad & auto_oob */
++      int *sect_bf;           /* Used by ECC correction */
++};
++
++enum mtk_snand_log_category {
++      SNAND_LOG_NFI,
++      SNAND_LOG_SNFI,
++      SNAND_LOG_ECC,
++      SNAND_LOG_CHIP,
++
++      __SNAND_LOG_CAT_MAX
++};
++
++int mtk_ecc_setup(struct mtk_snand *snf, void *fmdaddr, uint32_t max_ecc_bytes,
++                uint32_t msg_size);
++int mtk_snand_ecc_encoder_start(struct mtk_snand *snf);
++void mtk_snand_ecc_encoder_stop(struct mtk_snand *snf);
++int mtk_snand_ecc_decoder_start(struct mtk_snand *snf);
++void mtk_snand_ecc_decoder_stop(struct mtk_snand *snf);
++int mtk_ecc_wait_decoder_done(struct mtk_snand *snf);
++int mtk_ecc_check_decode_error(struct mtk_snand *snf);
++int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect);
++
++int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen,
++                   uint8_t *in, uint32_t inlen);
++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val);
++
++int mtk_snand_log(struct mtk_snand_plat_dev *pdev,
++                enum mtk_snand_log_category cat, const char *fmt, ...);
++
++#define snand_log_nfi(pdev, fmt, ...) \
++      mtk_snand_log(pdev, SNAND_LOG_NFI, fmt, ##__VA_ARGS__)
++
++#define snand_log_snfi(pdev, fmt, ...) \
++      mtk_snand_log(pdev, SNAND_LOG_SNFI, fmt, ##__VA_ARGS__)
++
++#define snand_log_ecc(pdev, fmt, ...) \
++      mtk_snand_log(pdev, SNAND_LOG_ECC, fmt, ##__VA_ARGS__)
++
++#define snand_log_chip(pdev, fmt, ...) \
++      mtk_snand_log(pdev, SNAND_LOG_CHIP, fmt, ##__VA_ARGS__)
++
++/* ffs64 */
++static inline int mtk_snand_ffs64(uint64_t x)
++{
++      if (!x)
++              return 0;
++
++      if (!(x & 0xffffffff))
++              return ffs((uint32_t)(x >> 32)) + 32;
++
++      return ffs((uint32_t)(x & 0xffffffff));
++}
++
++/* NFI dummy commands */
++#define NFI_CMD_DUMMY_READ            0x00
++#define NFI_CMD_DUMMY_WRITE           0x80
++
++/* SPI-NAND opcodes */
++#define SNAND_CMD_RESET                       0xff
++#define SNAND_CMD_BLOCK_ERASE         0xd8
++#define SNAND_CMD_READ_FROM_CACHE_QUAD        0xeb
++#define SNAND_CMD_WINBOND_SELECT_DIE  0xc2
++#define SNAND_CMD_READ_FROM_CACHE_DUAL        0xbb
++#define SNAND_CMD_READID              0x9f
++#define SNAND_CMD_READ_FROM_CACHE_X4  0x6b
++#define SNAND_CMD_READ_FROM_CACHE_X2  0x3b
++#define SNAND_CMD_PROGRAM_LOAD_X4     0x32
++#define SNAND_CMD_SET_FEATURE         0x1f
++#define SNAND_CMD_READ_TO_CACHE               0x13
++#define SNAND_CMD_PROGRAM_EXECUTE     0x10
++#define SNAND_CMD_GET_FEATURE         0x0f
++#define SNAND_CMD_READ_FROM_CACHE     0x0b
++#define SNAND_CMD_WRITE_ENABLE                0x06
++#define SNAND_CMD_PROGRAM_LOAD                0x02
++
++/* SPI-NAND feature addresses */
++#define SNAND_FEATURE_MICRON_DIE_ADDR 0xd0
++#define SNAND_MICRON_DIE_SEL_1                BIT(6)
++
++#define SNAND_FEATURE_STATUS_ADDR     0xc0
++#define SNAND_STATUS_OIP              BIT(0)
++#define SNAND_STATUS_WEL              BIT(1)
++#define SNAND_STATUS_ERASE_FAIL               BIT(2)
++#define SNAND_STATUS_PROGRAM_FAIL     BIT(3)
++
++#define SNAND_FEATURE_CONFIG_ADDR     0xb0
++#define SNAND_FEATURE_QUAD_ENABLE     BIT(0)
++#define SNAND_FEATURE_ECC_EN          BIT(4)
++
++#define SNAND_FEATURE_PROTECT_ADDR    0xa0
++
++#endif /* _MTK_SNAND_DEF_H_ */
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/mtk-snand-ecc.c
+@@ -0,0 +1,395 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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 i, regi, fi, errnum;
++      uint32_t errnum_shift = snf->ecc_soc->errnum_shift;
++      uint32_t errnum_mask = (1 << snf->ecc_soc->errnum_bits) - 1;
++      int ret = 0;
++
++      for (i = 0; i < snf->ecc_steps; i++) {
++              regi = i / 4;
++              fi = i % 4;
++
++              errnum = ecc_read32(snf, ECC_DECENUM(regi));
++              errnum = (errnum >> (fi * errnum_shift)) & errnum_mask;
++
++              if (errnum <= snf->ecc_strength) {
++                      snf->sect_bf[i] = errnum;
++              } else {
++                      snf->sect_bf[i] = -1;
++                      ret = -EBADMSG;
++              }
++      }
++
++      return ret;
++}
++
++static int mtk_ecc_check_buf_bitflips(struct mtk_snand *snf, const void *buf,
++                                    size_t len, uint32_t bitflips)
++{
++      const uint8_t *buf8 = buf;
++      const uint32_t *buf32;
++      uint32_t d, weight;
++
++      while (len && ((uintptr_t)buf8) % sizeof(uint32_t)) {
++              weight = hweight8(*buf8);
++              bitflips += BITS_PER_BYTE - weight;
++              buf8++;
++              len--;
++
++              if (bitflips > snf->ecc_strength)
++                      return -EBADMSG;
++      }
++
++      buf32 = (const uint32_t *)buf8;
++      while (len >= sizeof(uint32_t)) {
++              d = *buf32;
++
++              if (d != ~0) {
++                      weight = hweight32(d);
++                      bitflips += sizeof(uint32_t) * BITS_PER_BYTE - weight;
++              }
++
++              buf32++;
++              len -= sizeof(uint32_t);
++
++              if (bitflips > snf->ecc_strength)
++                      return -EBADMSG;
++      }
++
++      buf8 = (const uint8_t *)buf32;
++      while (len) {
++              weight = hweight8(*buf8);
++              bitflips += BITS_PER_BYTE - weight;
++              buf8++;
++              len--;
++
++              if (bitflips > snf->ecc_strength)
++                      return -EBADMSG;
++      }
++
++      return bitflips;
++}
++
++static int mtk_ecc_check_parity_bitflips(struct mtk_snand *snf, const void *buf,
++                                       uint32_t bits, uint32_t bitflips)
++{
++      uint32_t len, i;
++      uint8_t b;
++      int rc;
++
++      len = bits >> 3;
++      bits &= 7;
++
++      rc = mtk_ecc_check_buf_bitflips(snf, buf, len, bitflips);
++      if (!bits || rc < 0)
++              return rc;
++
++      bitflips = rc;
++
++      /* We want a precise count of bits */
++      b = ((const uint8_t *)buf)[len];
++      for (i = 0; i < bits; i++) {
++              if (!(b & BIT(i)))
++                      bitflips++;
++      }
++
++      if (bitflips > snf->ecc_strength)
++              return -EBADMSG;
++
++      return bitflips;
++}
++
++static void mtk_ecc_reset_parity(void *buf, uint32_t bits)
++{
++      uint32_t len;
++
++      len = bits >> 3;
++      bits &= 7;
++
++      memset(buf, 0xff, len);
++
++      /* Only reset bits protected by ECC to 1 */
++      if (bits)
++              ((uint8_t *)buf)[len] |= GENMASK(bits - 1, 0);
++}
++
++int mtk_ecc_fixup_empty_sector(struct mtk_snand *snf, uint32_t sect)
++{
++      uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size;
++      uint8_t *oob = snf->page_cache + snf->writesize;
++      uint8_t *data_ptr, *fdm_ptr, *ecc_ptr;
++      int bitflips = 0, ecc_bits, parity_bits;
++
++      parity_bits = fls(snf->nfi_soc->sector_size * 8);
++      ecc_bits = snf->ecc_strength * parity_bits;
++
++      data_ptr = snf->page_cache + sect * snf->nfi_soc->sector_size;
++      fdm_ptr = oob + sect * snf->nfi_soc->fdm_size;
++      ecc_ptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size +
++                sect * ecc_bytes;
++
++      /*
++       * Check whether DATA + FDM + ECC of a sector contains correctable
++       * bitflips
++       */
++      bitflips = mtk_ecc_check_buf_bitflips(snf, data_ptr,
++                                            snf->nfi_soc->sector_size,
++                                            bitflips);
++      if (bitflips < 0)
++              return -EBADMSG;
++
++      bitflips = mtk_ecc_check_buf_bitflips(snf, fdm_ptr,
++                                            snf->nfi_soc->fdm_ecc_size,
++                                            bitflips);
++      if (bitflips < 0)
++              return -EBADMSG;
++
++      bitflips = mtk_ecc_check_parity_bitflips(snf, ecc_ptr, ecc_bits,
++                                               bitflips);
++      if (bitflips < 0)
++              return -EBADMSG;
++
++      if (!bitflips)
++              return 0;
++
++      /* Reset the data of this sector to 0xff */
++      memset(data_ptr, 0xff, snf->nfi_soc->sector_size);
++      memset(fdm_ptr, 0xff, snf->nfi_soc->fdm_ecc_size);
++      mtk_ecc_reset_parity(ecc_ptr, ecc_bits);
++
++      return bitflips;
++}
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/mtk-snand-ids.c
+@@ -0,0 +1,511 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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,535 @@
++// 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_DONE;
++      } else {
++              instr->state = MTD_ERASE_FAILED;
++              ret = -EIO;
++      }
++
++      return ret;
++}
++
++static int mtk_snand_mtd_read_data(struct mtk_snand_mtd *msm, uint64_t addr,
++                                 struct mtd_oob_ops *ops)
++{
++      struct mtd_info *mtd = dev_get_uclass_priv(msm->dev);
++      size_t len, ooblen, maxooblen, chklen;
++      uint32_t col, ooboffs;
++      uint8_t *datcache, *oobcache;
++      bool ecc_failed = false, raw = ops->mode == MTD_OPS_RAW ? true : false;
++      int ret, max_bitflips = 0;
++
++      col = addr & mtd->writesize_mask;
++      addr &= ~mtd->writesize_mask;
++      maxooblen = mtd_oobavail(mtd, ops);
++      ooboffs = ops->ooboffs;
++      ooblen = ops->ooblen;
++      len = ops->len;
++
++      datcache = len ? msm->page_cache : NULL;
++      oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL;
++
++      ops->oobretlen = 0;
++      ops->retlen = 0;
++
++      while (len || ooblen) {
++              WATCHDOG_RESET();
++
++              if (ops->mode == MTD_OPS_AUTO_OOB)
++                      ret = mtk_snand_read_page_auto_oob(msm->snf, addr,
++                              datcache, oobcache, maxooblen, NULL, raw);
++              else
++                      ret = mtk_snand_read_page(msm->snf, addr, datcache,
++                              oobcache, raw);
++
++              if (ret < 0 && ret != -EBADMSG)
++                      return ret;
++
++              if (ret == -EBADMSG) {
++                      mtd->ecc_stats.failed++;
++                      ecc_failed = true;
++              } else {
++                      mtd->ecc_stats.corrected += ret;
++                      max_bitflips = max_t(int, ret, max_bitflips);
++              }
++
++              mtd->ecc_stats.corrected += ret;
++              max_bitflips = max_t(int, ret, max_bitflips);
++
++              if (len) {
++                      /* Move data */
++                      chklen = mtd->writesize - col;
++                      if (chklen > len)
++                              chklen = len;
++
++                      memcpy(ops->datbuf + ops->retlen, datcache + col,
++                             chklen);
++                      len -= chklen;
++                      col = 0; /* (col + chklen) %  */
++                      ops->retlen += chklen;
++              }
++
++              if (ooblen) {
++                      /* Move oob */
++                      chklen = maxooblen - ooboffs;
++                      if (chklen > ooblen)
++                              chklen = ooblen;
++
++                      memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs,
++                             chklen);
++                      ooblen -= chklen;
++                      ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
++                      ops->oobretlen += chklen;
++              }
++
++              addr += mtd->writesize;
++      }
++
++      return ecc_failed ? -EBADMSG : max_bitflips;
++}
++
++static int mtk_snand_mtd_read_oob(struct mtd_info *mtd, loff_t from,
++                                struct mtd_oob_ops *ops)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++      uint32_t maxooblen;
++
++      if (!ops->oobbuf && !ops->datbuf) {
++              if (ops->ooblen || ops->len)
++                      return -EINVAL;
++
++              return 0;
++      }
++
++      switch (ops->mode) {
++      case MTD_OPS_PLACE_OOB:
++      case MTD_OPS_AUTO_OOB:
++      case MTD_OPS_RAW:
++              break;
++      default:
++              pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode);
++              return -EINVAL;
++      }
++
++      maxooblen = mtd_oobavail(mtd, ops);
++
++      /* Do not allow read past end of device */
++      if (ops->datbuf && (from + ops->len) > mtd->size) {
++              pr_debug("%s: attempt to read beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(ops->ooboffs >= maxooblen)) {
++              pr_debug("%s: attempt to start read outside oob\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(from >= mtd->size ||
++          ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
++          (from >> mtd->writesize_shift)) * maxooblen)) {
++              pr_debug("%s: attempt to read beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      return mtk_snand_mtd_read_data(msm, from, ops);
++}
++
++static int mtk_snand_mtd_write_data(struct mtk_snand_mtd *msm, uint64_t addr,
++                                  struct mtd_oob_ops *ops)
++{
++      struct mtd_info *mtd = dev_get_uclass_priv(msm->dev);
++      size_t len, ooblen, maxooblen, chklen, oobwrlen;
++      uint32_t col, ooboffs;
++      uint8_t *datcache, *oobcache;
++      bool raw = ops->mode == MTD_OPS_RAW ? true : false;
++      int ret;
++
++      col = addr & mtd->writesize_mask;
++      addr &= ~mtd->writesize_mask;
++      maxooblen = mtd_oobavail(mtd, ops);
++      ooboffs = ops->ooboffs;
++      ooblen = ops->ooblen;
++      len = ops->len;
++
++      datcache = len ? msm->page_cache : NULL;
++      oobcache = ooblen ? msm->page_cache + mtd->writesize : NULL;
++
++      ops->oobretlen = 0;
++      ops->retlen = 0;
++
++      while (len || ooblen) {
++              WATCHDOG_RESET();
++
++              if (len) {
++                      /* Move data */
++                      chklen = mtd->writesize - col;
++                      if (chklen > len)
++                              chklen = len;
++
++                      memset(datcache, 0xff, col);
++                      memcpy(datcache + col, ops->datbuf + ops->retlen,
++                             chklen);
++                      memset(datcache + col + chklen, 0xff,
++                             mtd->writesize - col - chklen);
++                      len -= chklen;
++                      col = 0; /* (col + chklen) %  */
++                      ops->retlen += chklen;
++              }
++
++              oobwrlen = 0;
++              if (ooblen) {
++                      /* Move oob */
++                      chklen = maxooblen - ooboffs;
++                      if (chklen > ooblen)
++                              chklen = ooblen;
++
++                      memset(oobcache, 0xff, ooboffs);
++                      memcpy(oobcache + ooboffs,
++                             ops->oobbuf + ops->oobretlen, chklen);
++                      memset(oobcache + ooboffs + chklen, 0xff,
++                             mtd->oobsize - ooboffs - chklen);
++                      oobwrlen = chklen + ooboffs;
++                      ooblen -= chklen;
++                      ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
++                      ops->oobretlen += chklen;
++              }
++
++              if (ops->mode == MTD_OPS_AUTO_OOB)
++                      ret = mtk_snand_write_page_auto_oob(msm->snf, addr,
++                              datcache, oobcache, oobwrlen, NULL, raw);
++              else
++                      ret = mtk_snand_write_page(msm->snf, addr, datcache,
++                              oobcache, raw);
++
++              if (ret)
++                      return ret;
++
++              addr += mtd->writesize;
++      }
++
++      return 0;
++}
++
++static int mtk_snand_mtd_write_oob(struct mtd_info *mtd, loff_t to,
++                                 struct mtd_oob_ops *ops)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++      uint32_t maxooblen;
++
++      if (!ops->oobbuf && !ops->datbuf) {
++              if (ops->ooblen || ops->len)
++                      return -EINVAL;
++
++              return 0;
++      }
++
++      switch (ops->mode) {
++      case MTD_OPS_PLACE_OOB:
++      case MTD_OPS_AUTO_OOB:
++      case MTD_OPS_RAW:
++              break;
++      default:
++              pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode);
++              return -EINVAL;
++      }
++
++      maxooblen = mtd_oobavail(mtd, ops);
++
++      /* Do not allow write past end of device */
++      if (ops->datbuf && (to + ops->len) > mtd->size) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(ops->ooboffs >= maxooblen)) {
++              pr_debug("%s: attempt to start write outside oob\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(to >= mtd->size ||
++          ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
++          (to >> mtd->writesize_shift)) * maxooblen)) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      return mtk_snand_mtd_write_data(msm, to, ops);
++}
++
++static int mtk_snand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
++                            size_t *retlen, u_char *buf)
++{
++      struct mtd_oob_ops ops = {
++              .mode = MTD_OPS_PLACE_OOB,
++              .datbuf = buf,
++              .len = len,
++      };
++      int ret;
++
++      ret = mtk_snand_mtd_read_oob(mtd, from, &ops);
++
++      if (retlen)
++              *retlen = ops.retlen;
++
++      return ret;
++}
++
++static int mtk_snand_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
++                             size_t *retlen, const u_char *buf)
++{
++      struct mtd_oob_ops ops = {
++              .mode = MTD_OPS_PLACE_OOB,
++              .datbuf = (void *)buf,
++              .len = len,
++      };
++      int ret;
++
++      ret = mtk_snand_mtd_write_oob(mtd, to, &ops);
++
++      if (retlen)
++              *retlen = ops.retlen;
++
++      return ret;
++}
++
++static int mtk_snand_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++
++      return mtk_snand_block_isbad(msm->snf, offs);
++}
++
++static int mtk_snand_mtd_block_markbad(struct mtd_info *mtd, loff_t offs)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++
++      return mtk_snand_block_markbad(msm->snf, offs);
++}
++
++static int mtk_snand_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                 struct mtd_oob_region *oobecc)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++
++      if (section)
++              return -ERANGE;
++
++      oobecc->offset = msm->cinfo.fdm_size * msm->cinfo.num_sectors;
++      oobecc->length = mtd->oobsize - oobecc->offset;
++
++      return 0;
++}
++
++static int mtk_snand_ooblayout_free(struct mtd_info *mtd, int section,
++                                  struct mtd_oob_region *oobfree)
++{
++      struct mtk_snand_mtd *msm = mtd_to_msm(mtd);
++
++      if (section >= msm->cinfo.num_sectors)
++              return -ERANGE;
++
++      oobfree->length = msm->cinfo.fdm_size - 1;
++      oobfree->offset = section * msm->cinfo.fdm_size + 1;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops mtk_snand_ooblayout = {
++      .ecc = mtk_snand_ooblayout_ecc,
++      .rfree = mtk_snand_ooblayout_free,
++};
++
++static int mtk_snand_mtd_probe(struct udevice *dev)
++{
++      struct mtk_snand_mtd *msm = dev_get_priv(dev);
++      struct mtd_info *mtd = dev_get_uclass_priv(dev);
++      struct mtk_snand_platdata mtk_snand_pdata = {};
++      size_t namelen;
++      fdt_addr_t base;
++      int ret;
++
++      base = dev_read_addr_name(dev, "nfi");
++      if (base == FDT_ADDR_T_NONE)
++              return -EINVAL;
++      mtk_snand_pdata.nfi_base = map_sysmem(base, 0);
++
++      base = dev_read_addr_name(dev, "ecc");
++      if (base == FDT_ADDR_T_NONE)
++              return -EINVAL;
++      mtk_snand_pdata.ecc_base = map_sysmem(base, 0);
++
++      mtk_snand_pdata.soc = dev_get_driver_data(dev);
++      mtk_snand_pdata.quad_spi = dev_read_bool(dev, "quad-spi");
++
++      ret = mtk_snand_init(NULL, &mtk_snand_pdata, &msm->snf);
++      if (ret)
++              return ret;
++
++      mtk_snand_get_chip_info(msm->snf, &msm->cinfo);
++
++      msm->page_cache = malloc(msm->cinfo.pagesize + msm->cinfo.sparesize);
++      if (!msm->page_cache) {
++              printf("%s: failed to allocate memory for page cache\n",
++                     __func__);
++              ret = -ENOMEM;
++              goto errout1;
++      }
++
++      namelen = sizeof(snand_mtd_name_prefix) + 12;
++
++      mtd->name = malloc(namelen);
++      if (!mtd->name) {
++              printf("%s: failed to allocate memory for MTD name\n",
++                     __func__);
++              ret = -ENOMEM;
++              goto errout2;
++      }
++
++      msm->dev = dev;
++
++      snprintf(mtd->name, namelen, "%s%u", snand_mtd_name_prefix, snandidx++);
++
++      mtd->priv = msm;
++      mtd->dev = dev;
++      mtd->type = MTD_NANDFLASH;
++      mtd->flags = MTD_CAP_NANDFLASH;
++
++      mtd->size = msm->cinfo.chipsize;
++      mtd->erasesize = msm->cinfo.blocksize;
++      mtd->writesize = msm->cinfo.pagesize;
++      mtd->writebufsize = mtd->writesize;
++      mtd->oobsize = msm->cinfo.sparesize;
++      mtd->oobavail = msm->cinfo.num_sectors * (msm->cinfo.fdm_size - 1);
++
++      mtd->ooblayout = &mtk_snand_ooblayout;
++
++      mtd->ecc_strength = msm->cinfo.ecc_strength;
++      mtd->bitflip_threshold = (mtd->ecc_strength * 3) / 4;
++      mtd->ecc_step_size = msm->cinfo.sector_size;
++
++      mtd->_read = mtk_snand_mtd_read;
++      mtd->_write = mtk_snand_mtd_write;
++      mtd->_erase = mtk_snand_mtd_erase;
++      mtd->_read_oob = mtk_snand_mtd_read_oob;
++      mtd->_write_oob = mtk_snand_mtd_write_oob;
++      mtd->_block_isbad = mtk_snand_mtd_block_isbad;
++      mtd->_block_markbad = mtk_snand_mtd_block_markbad;
++
++      ret = add_mtd_device(mtd);
++      if (ret) {
++              printf("%s: failed to add SPI-NAND MTD device\n", __func__);
++              ret = -ENODEV;
++              goto errout3;
++      }
++
++      printf("SPI-NAND: %s (%lluMB)\n", msm->cinfo.model,
++             msm->cinfo.chipsize >> 20);
++
++      return 0;
++
++errout3:
++      free(mtd->name);
++
++errout2:
++      free(msm->page_cache);
++
++errout1:
++      mtk_snand_cleanup(msm->snf);
++
++      return ret;
++}
++
++static const struct udevice_id mtk_snand_ids[] = {
++      { .compatible = "mediatek,mt7622-snand", .data = SNAND_SOC_MT7622 },
++      { .compatible = "mediatek,mt7629-snand", .data = SNAND_SOC_MT7629 },
++      { .compatible = "mediatek,mt7981-snand", .data = SNAND_SOC_MT7981 },
++      { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 },
++      { /* sentinel */ },
++};
++
++U_BOOT_DRIVER(spinand) = {
++      .name = "mtk-snand",
++      .id = UCLASS_MTD,
++      .of_match = mtk_snand_ids,
++      .priv_auto = sizeof(struct mtk_snand_mtd),
++      .probe = mtk_snand_mtd_probe,
++};
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/mtk-snand-os.c
+@@ -0,0 +1,39 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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,1933 @@
++// 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_ADDRCNTR                  0x070
++#define SEC_CNTR                      GENMASK(16, 12)
++#define SEC_CNTR_S                    12
++#define NFI_SEC_CNTR(val)               (((val) & SEC_CNTR) >> SEC_CNTR_S)
++
++#define NFI_STRADDR                   0x080
++
++#define NFI_BYTELEN                   0x084
++#define BUS_SEC_CNTR(val)             (((val) & SEC_CNTR) >> SEC_CNTR_S)
++
++#define NFI_FDM0L                     0x0a0
++#define NFI_FDM0M                     0x0a4
++#define NFI_FDML(n)                   (NFI_FDM0L + (n) * 8)
++#define NFI_FDMM(n)                   (NFI_FDM0M + (n) * 8)
++
++#define NFI_DEBUG_CON1                        0x220
++#define WBUF_EN                               BIT(2)
++
++#define NFI_MASTERSTA                 0x224
++#define MAS_ADDR                      GENMASK(11, 9)
++#define MAS_RD                                GENMASK(8, 6)
++#define MAS_WR                                GENMASK(5, 3)
++#define MAS_RDDLY                     GENMASK(2, 0)
++#define NFI_MASTERSTA_MASK_7622               (MAS_ADDR | MAS_RD | MAS_WR | MAS_RDDLY)
++#define AHB_BUS_BUSY                  BIT(1)
++#define BUS_BUSY                      BIT(0)
++#define NFI_MASTERSTA_MASK_7981               (AHB_BUS_BUSY | BUS_BUSY)
++#define NFI_MASTERSTA_MASK_7986               (AHB_BUS_BUSY | BUS_BUSY)
++
++/* SNFI registers */
++#define SNF_MAC_CTL                   0x500
++#define MAC_XIO_SEL                   BIT(4)
++#define SF_MAC_EN                     BIT(3)
++#define SF_TRIG                               BIT(2)
++#define WIP_READY                     BIT(1)
++#define WIP                           BIT(0)
++
++#define SNF_MAC_OUTL                  0x504
++#define SNF_MAC_INL                   0x508
++
++#define SNF_RD_CTL2                   0x510
++#define DATA_READ_DUMMY_S             8
++#define DATA_READ_CMD_S                       0
++
++#define SNF_RD_CTL3                   0x514
++
++#define SNF_PG_CTL1                   0x524
++#define PG_LOAD_CMD_S                 8
++
++#define SNF_PG_CTL2                   0x528
++
++#define SNF_MISC_CTL                  0x538
++#define SW_RST                                BIT(28)
++#define FIFO_RD_LTC_S                 25
++#define PG_LOAD_X4_EN                 BIT(20)
++#define DATA_READ_MODE_S              16
++#define DATA_READ_MODE                        GENMASK(18, 16)
++#define   DATA_READ_MODE_X1           0
++#define   DATA_READ_MODE_X2           1
++#define   DATA_READ_MODE_X4           2
++#define   DATA_READ_MODE_DUAL         5
++#define   DATA_READ_MODE_QUAD         6
++#define LATCH_LAT_S                   8
++#define LATCH_LAT                     GENMASK(9, 8)
++#define PG_LOAD_CUSTOM_EN             BIT(7)
++#define DATARD_CUSTOM_EN              BIT(6)
++#define CS_DESELECT_CYC_S             0
++
++#define SNF_MISC_CTL2                 0x53c
++#define PROGRAM_LOAD_BYTE_NUM_S               16
++#define READ_DATA_BYTE_NUM_S          11
++
++#define SNF_DLY_CTL3                  0x548
++#define SFCK_SAM_DLY_S                        0
++
++#define SNF_STA_CTL1                  0x550
++#define CUS_PG_DONE                   BIT(28)
++#define CUS_READ_DONE                 BIT(27)
++#define SPI_STATE_S                   0
++#define SPI_STATE                     GENMASK(3, 0)
++
++#define SNF_CFG                               0x55c
++#define SPI_MODE                      BIT(0)
++
++#define SNF_GPRAM                     0x800
++#define SNF_GPRAM_SIZE                        0xa0
++
++#define SNFI_POLL_INTERVAL            1000000
++
++static const uint8_t mt7622_spare_sizes[] = { 16, 26, 27, 28 };
++
++static const uint8_t mt7981_spare_sizes[] = {
++      16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64,
++      67, 74
++};
++
++static const uint8_t mt7986_spare_sizes[] = {
++      16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64,
++      67, 74
++};
++
++static const struct mtk_snand_soc_data mtk_snand_socs[__SNAND_SOC_MAX] = {
++      [SNAND_SOC_MT7622] = {
++              .sector_size = 512,
++              .max_sectors = 8,
++              .fdm_size = 8,
++              .fdm_ecc_size = 1,
++              .fifo_size = 32,
++              .bbm_swap = false,
++              .empty_page_check = false,
++              .mastersta_mask = NFI_MASTERSTA_MASK_7622,
++              .spare_sizes = mt7622_spare_sizes,
++              .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes),
++              .latch_lat = 0,
++              .sample_delay = 40
++      },
++      [SNAND_SOC_MT7629] = {
++              .sector_size = 512,
++              .max_sectors = 8,
++              .fdm_size = 8,
++              .fdm_ecc_size = 1,
++              .fifo_size = 32,
++              .bbm_swap = true,
++              .empty_page_check = false,
++              .mastersta_mask = NFI_MASTERSTA_MASK_7622,
++              .spare_sizes = mt7622_spare_sizes,
++              .num_spare_size = ARRAY_SIZE(mt7622_spare_sizes),
++              .latch_lat = 0,
++              .sample_delay = 40
++      },
++      [SNAND_SOC_MT7981] = {
++              .sector_size = 1024,
++              .max_sectors = 16,
++              .fdm_size = 8,
++              .fdm_ecc_size = 1,
++              .fifo_size = 64,
++              .bbm_swap = true,
++              .empty_page_check = true,
++              .mastersta_mask = NFI_MASTERSTA_MASK_7981,
++              .spare_sizes = mt7981_spare_sizes,
++              .num_spare_size = ARRAY_SIZE(mt7981_spare_sizes),
++              .latch_lat = 0,
++              .sample_delay = 40
++      },
++      [SNAND_SOC_MT7986] = {
++              .sector_size = 1024,
++              .max_sectors = 16,
++              .fdm_size = 8,
++              .fdm_ecc_size = 1,
++              .fifo_size = 64,
++              .bbm_swap = true,
++              .empty_page_check = true,
++              .mastersta_mask = NFI_MASTERSTA_MASK_7986,
++              .spare_sizes = mt7986_spare_sizes,
++              .num_spare_size = ARRAY_SIZE(mt7986_spare_sizes),
++              .latch_lat = 0,
++              .sample_delay = 40
++      },
++};
++
++static inline uint32_t nfi_read32(struct mtk_snand *snf, uint32_t reg)
++{
++      return readl(snf->nfi_base + reg);
++}
++
++static inline void nfi_write32(struct mtk_snand *snf, uint32_t reg,
++                             uint32_t val)
++{
++      writel(val, snf->nfi_base + reg);
++}
++
++static inline void nfi_write16(struct mtk_snand *snf, uint32_t reg,
++                             uint16_t val)
++{
++      writew(val, snf->nfi_base + reg);
++}
++
++static inline void nfi_rmw32(struct mtk_snand *snf, uint32_t reg, uint32_t clr,
++                           uint32_t set)
++{
++      uint32_t val;
++
++      val = readl(snf->nfi_base + reg);
++      val &= ~clr;
++      val |= set;
++      writel(val, snf->nfi_base + reg);
++}
++
++static void nfi_write_data(struct mtk_snand *snf, uint32_t reg,
++                         const uint8_t *data, uint32_t len)
++{
++      uint32_t i, val = 0, es = sizeof(uint32_t);
++
++      for (i = reg; i < reg + len; i++) {
++              val |= ((uint32_t)*data++) << (8 * (i % es));
++
++              if (i % es == es - 1 || i == reg + len - 1) {
++                      nfi_write32(snf, i & ~(es - 1), val);
++                      val = 0;
++              }
++      }
++}
++
++static void nfi_read_data(struct mtk_snand *snf, uint32_t reg, uint8_t *data,
++                        uint32_t len)
++{
++      uint32_t i, val = 0, es = sizeof(uint32_t);
++
++      for (i = reg; i < reg + len; i++) {
++              if (i == reg || i % es == 0)
++                      val = nfi_read32(snf, i & ~(es - 1));
++
++              *data++ = (uint8_t)(val >> (8 * (i % es)));
++      }
++}
++
++static inline void do_bm_swap(uint8_t *bm1, uint8_t *bm2)
++{
++      uint8_t tmp = *bm1;
++      *bm1 = *bm2;
++      *bm2 = tmp;
++}
++
++static void mtk_snand_bm_swap_raw(struct mtk_snand *snf)
++{
++      uint32_t fdm_bbm_pos;
++
++      if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1)
++              return;
++
++      fdm_bbm_pos = (snf->ecc_steps - 1) * snf->raw_sector_size +
++                    snf->nfi_soc->sector_size;
++      do_bm_swap(&snf->page_cache[fdm_bbm_pos],
++                 &snf->page_cache[snf->writesize]);
++}
++
++static void mtk_snand_bm_swap(struct mtk_snand *snf)
++{
++      uint32_t buf_bbm_pos, fdm_bbm_pos;
++
++      if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1)
++              return;
++
++      buf_bbm_pos = snf->writesize -
++                    (snf->ecc_steps - 1) * snf->spare_per_sector;
++      fdm_bbm_pos = snf->writesize +
++                    (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size;
++      do_bm_swap(&snf->page_cache[fdm_bbm_pos],
++                 &snf->page_cache[buf_bbm_pos]);
++}
++
++static void mtk_snand_fdm_bm_swap_raw(struct mtk_snand *snf)
++{
++      uint32_t fdm_bbm_pos1, fdm_bbm_pos2;
++
++      if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1)
++              return;
++
++      fdm_bbm_pos1 = snf->nfi_soc->sector_size;
++      fdm_bbm_pos2 = (snf->ecc_steps - 1) * snf->raw_sector_size +
++                     snf->nfi_soc->sector_size;
++      do_bm_swap(&snf->page_cache[fdm_bbm_pos1],
++                 &snf->page_cache[fdm_bbm_pos2]);
++}
++
++static void mtk_snand_fdm_bm_swap(struct mtk_snand *snf)
++{
++      uint32_t fdm_bbm_pos1, fdm_bbm_pos2;
++
++      if (!snf->nfi_soc->bbm_swap || snf->ecc_steps == 1)
++              return;
++
++      fdm_bbm_pos1 = snf->writesize;
++      fdm_bbm_pos2 = snf->writesize +
++                     (snf->ecc_steps - 1) * snf->nfi_soc->fdm_size;
++      do_bm_swap(&snf->page_cache[fdm_bbm_pos1],
++                 &snf->page_cache[fdm_bbm_pos2]);
++}
++
++static int mtk_nfi_reset(struct mtk_snand *snf)
++{
++      uint32_t val, fifo_mask;
++      int ret;
++
++      nfi_write32(snf, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
++
++      ret = read16_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val,
++                                !(val & snf->nfi_soc->mastersta_mask), 0,
++                                SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "NFI master is still busy after reset\n");
++              return ret;
++      }
++
++      ret = read32_poll_timeout(snf->nfi_base + NFI_STA, val,
++                                !(val & (NFI_FSM | NFI_NAND_FSM)), 0,
++                                SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev, "Failed to reset NFI\n");
++              return ret;
++      }
++
++      fifo_mask = ((snf->nfi_soc->fifo_size - 1) << FIFO_RD_REMAIN_S) |
++                  ((snf->nfi_soc->fifo_size - 1) << FIFO_WR_REMAIN_S);
++      ret = read16_poll_timeout(snf->nfi_base + NFI_FIFOSTA, val,
++                                !(val & fifo_mask), 0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev, "NFI FIFOs are not empty\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int mtk_snand_mac_reset(struct mtk_snand *snf)
++{
++      int ret;
++      uint32_t val;
++
++      nfi_rmw32(snf, SNF_MISC_CTL, 0, SW_RST);
++
++      ret = read32_poll_timeout(snf->nfi_base + SNF_STA_CTL1, val,
++                                !(val & SPI_STATE), 0, SNFI_POLL_INTERVAL);
++      if (ret)
++              snand_log_snfi(snf->pdev, "Failed to reset SNFI MAC\n");
++
++      nfi_write32(snf, SNF_MISC_CTL, (2 << FIFO_RD_LTC_S) |
++                  (10 << CS_DESELECT_CYC_S) | (snf->nfi_soc->latch_lat << LATCH_LAT_S));
++
++      return ret;
++}
++
++static int mtk_snand_mac_trigger(struct mtk_snand *snf, uint32_t outlen,
++                               uint32_t inlen)
++{
++      int ret;
++      uint32_t val;
++
++      nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN);
++      nfi_write32(snf, SNF_MAC_OUTL, outlen);
++      nfi_write32(snf, SNF_MAC_INL, inlen);
++
++      nfi_write32(snf, SNF_MAC_CTL, SF_MAC_EN | SF_TRIG);
++
++      ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val,
++                                val & WIP_READY, 0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_snfi(snf->pdev, "Timed out waiting for WIP_READY\n");
++              goto cleanup;
++      }
++
++      ret = read32_poll_timeout(snf->nfi_base + SNF_MAC_CTL, val,
++                                !(val & WIP), 0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_snfi(snf->pdev,
++                             "Timed out waiting for WIP cleared\n");
++      }
++
++cleanup:
++      nfi_write32(snf, SNF_MAC_CTL, 0);
++
++      return ret;
++}
++
++int mtk_snand_mac_io(struct mtk_snand *snf, const uint8_t *out, uint32_t outlen,
++                   uint8_t *in, uint32_t inlen)
++{
++      int ret;
++
++      if (outlen + inlen > SNF_GPRAM_SIZE)
++              return -EINVAL;
++
++      mtk_snand_mac_reset(snf);
++
++      nfi_write_data(snf, SNF_GPRAM, out, outlen);
++
++      ret = mtk_snand_mac_trigger(snf, outlen, inlen);
++      if (ret)
++              return ret;
++
++      if (!inlen)
++              return 0;
++
++      nfi_read_data(snf, SNF_GPRAM + outlen, in, inlen);
++
++      return 0;
++}
++
++static int mtk_snand_get_feature(struct mtk_snand *snf, uint32_t addr)
++{
++      uint8_t op[2], val;
++      int ret;
++
++      op[0] = SNAND_CMD_GET_FEATURE;
++      op[1] = (uint8_t)addr;
++
++      ret = mtk_snand_mac_io(snf, op, sizeof(op), &val, 1);
++      if (ret)
++              return ret;
++
++      return val;
++}
++
++int mtk_snand_set_feature(struct mtk_snand *snf, uint32_t addr, uint32_t val)
++{
++      uint8_t op[3];
++
++      op[0] = SNAND_CMD_SET_FEATURE;
++      op[1] = (uint8_t)addr;
++      op[2] = (uint8_t)val;
++
++      return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0);
++}
++
++static int mtk_snand_poll_status(struct mtk_snand *snf, uint32_t wait_us)
++{
++      int val;
++      mtk_snand_time_t time_start, tmo;
++
++      time_start = timer_get_ticks();
++      tmo = timer_time_to_tick(wait_us);
++
++      do {
++              val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR);
++              if (!(val & SNAND_STATUS_OIP))
++                      return val & (SNAND_STATUS_ERASE_FAIL |
++                                    SNAND_STATUS_PROGRAM_FAIL);
++      } while (!timer_is_timeout(time_start, tmo));
++
++      return -ETIMEDOUT;
++}
++
++int mtk_snand_chip_reset(struct mtk_snand *snf)
++{
++      uint8_t op = SNAND_CMD_RESET;
++      int ret;
++
++      ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL);
++      if (ret < 0)
++              return ret;
++
++      return 0;
++}
++
++static int mtk_snand_config_feature(struct mtk_snand *snf, uint8_t clr,
++                                  uint8_t set)
++{
++      int val, newval;
++      int ret;
++
++      val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR);
++      if (val < 0) {
++              snand_log_chip(snf->pdev,
++                             "Failed to get configuration feature\n");
++              return val;
++      }
++
++      newval = (val & (~clr)) | set;
++
++      if (newval == val)
++              return 0;
++
++      ret = mtk_snand_set_feature(snf, SNAND_FEATURE_CONFIG_ADDR,
++                                  (uint8_t)newval);
++      if (val < 0) {
++              snand_log_chip(snf->pdev,
++                             "Failed to set configuration feature\n");
++              return ret;
++      }
++
++      val = mtk_snand_get_feature(snf, SNAND_FEATURE_CONFIG_ADDR);
++      if (val < 0) {
++              snand_log_chip(snf->pdev,
++                             "Failed to get configuration feature\n");
++              return val;
++      }
++
++      if (newval != val)
++              return -ENOTSUPP;
++
++      return 0;
++}
++
++static int mtk_snand_ondie_ecc_control(struct mtk_snand *snf, bool enable)
++{
++      int ret;
++
++      if (enable)
++              ret = mtk_snand_config_feature(snf, 0, SNAND_FEATURE_ECC_EN);
++      else
++              ret = mtk_snand_config_feature(snf, SNAND_FEATURE_ECC_EN, 0);
++
++      if (ret) {
++              snand_log_chip(snf->pdev, "Failed to %s On-Die ECC engine\n",
++                             enable ? "enable" : "disable");
++      }
++
++      return ret;
++}
++
++static int mtk_snand_qspi_control(struct mtk_snand *snf, bool enable)
++{
++      int ret;
++
++      if (enable) {
++              ret = mtk_snand_config_feature(snf, 0,
++                                             SNAND_FEATURE_QUAD_ENABLE);
++      } else {
++              ret = mtk_snand_config_feature(snf,
++                                             SNAND_FEATURE_QUAD_ENABLE, 0);
++      }
++
++      if (ret) {
++              snand_log_chip(snf->pdev, "Failed to %s quad spi\n",
++                             enable ? "enable" : "disable");
++      }
++
++      return ret;
++}
++
++static int mtk_snand_unlock(struct mtk_snand *snf)
++{
++      int ret;
++
++      ret = mtk_snand_set_feature(snf, SNAND_FEATURE_PROTECT_ADDR, 0);
++      if (ret) {
++              snand_log_chip(snf->pdev, "Failed to set protection feature\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static int mtk_snand_write_enable(struct mtk_snand *snf)
++{
++      uint8_t op = SNAND_CMD_WRITE_ENABLE;
++      int ret, val;
++
++      ret = mtk_snand_mac_io(snf, &op, 1, NULL, 0);
++      if (ret)
++              return ret;
++
++      val = mtk_snand_get_feature(snf, SNAND_FEATURE_STATUS_ADDR);
++      if (val < 0)
++              return ret;
++
++      if (val & SNAND_STATUS_WEL)
++              return 0;
++
++      snand_log_chip(snf->pdev, "Failed to send write-enable command\n");
++
++      return -ENOTSUPP;
++}
++
++static int mtk_snand_select_die(struct mtk_snand *snf, uint32_t dieidx)
++{
++      if (!snf->select_die)
++              return 0;
++
++      return snf->select_die(snf, dieidx);
++}
++
++static uint64_t mtk_snand_select_die_address(struct mtk_snand *snf,
++                                           uint64_t addr)
++{
++      uint32_t dieidx;
++
++      if (!snf->select_die)
++              return addr;
++
++      dieidx = addr >> snf->die_shift;
++
++      mtk_snand_select_die(snf, dieidx);
++
++      return addr & snf->die_mask;
++}
++
++static uint32_t mtk_snand_get_plane_address(struct mtk_snand *snf,
++                                          uint32_t page)
++{
++      uint32_t pages_per_block;
++
++      pages_per_block = 1 << (snf->erasesize_shift - snf->writesize_shift);
++
++      if (page & pages_per_block)
++              return 1 << (snf->writesize_shift + 1);
++
++      return 0;
++}
++
++static int mtk_snand_page_op(struct mtk_snand *snf, uint32_t page, uint8_t cmd)
++{
++      uint8_t op[4];
++
++      op[0] = cmd;
++      op[1] = (page >> 16) & 0xff;
++      op[2] = (page >> 8) & 0xff;
++      op[3] = page & 0xff;
++
++      return mtk_snand_mac_io(snf, op, sizeof(op), NULL, 0);
++}
++
++static void mtk_snand_read_fdm(struct mtk_snand *snf, uint8_t *buf)
++{
++      uint32_t vall, valm;
++      uint8_t *oobptr = buf;
++      int i, j;
++
++      for (i = 0; i < snf->ecc_steps; i++) {
++              vall = nfi_read32(snf, NFI_FDML(i));
++              valm = nfi_read32(snf, NFI_FDMM(i));
++
++              for (j = 0; j < snf->nfi_soc->fdm_size; j++)
++                      oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
++
++              oobptr += snf->nfi_soc->fdm_size;
++      }
++}
++
++static int mtk_snand_read_ecc_parity(struct mtk_snand *snf, uint32_t page,
++                                   uint32_t sect, uint8_t *oob)
++{
++      uint32_t ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size;
++      uint32_t coladdr, raw_offs, offs;
++      uint8_t op[4];
++
++      if (sizeof(op) + ecc_bytes > SNF_GPRAM_SIZE) {
++              snand_log_snfi(snf->pdev,
++                             "ECC parity size does not fit the GPRAM\n");
++              return -ENOTSUPP;
++      }
++
++      raw_offs = sect * snf->raw_sector_size + snf->nfi_soc->sector_size +
++                 snf->nfi_soc->fdm_size;
++      offs = snf->ecc_steps * snf->nfi_soc->fdm_size + sect * ecc_bytes;
++
++      /* Column address with plane bit */
++      coladdr = raw_offs | mtk_snand_get_plane_address(snf, page);
++
++      op[0] = SNAND_CMD_READ_FROM_CACHE;
++      op[1] = (coladdr >> 8) & 0xff;
++      op[2] = coladdr & 0xff;
++      op[3] = 0;
++
++      return mtk_snand_mac_io(snf, op, sizeof(op), oob + offs, ecc_bytes);
++}
++
++static int mtk_snand_check_ecc_result(struct mtk_snand *snf, uint32_t page)
++{
++      uint8_t *oob = snf->page_cache + snf->writesize;
++      int i, rc, ret = 0, max_bitflips = 0;
++
++      for (i = 0; i < snf->ecc_steps; i++) {
++              if (snf->sect_bf[i] >= 0) {
++                      if (snf->sect_bf[i] > max_bitflips)
++                              max_bitflips = snf->sect_bf[i];
++                      continue;
++              }
++
++              rc = mtk_snand_read_ecc_parity(snf, page, i, oob);
++              if (rc)
++                      return rc;
++
++              rc = mtk_ecc_fixup_empty_sector(snf, i);
++              if (rc < 0) {
++                      ret = -EBADMSG;
++
++                      snand_log_ecc(snf->pdev,
++                            "Uncorrectable bitflips in page %u sect %u\n",
++                            page, i);
++              } else if (rc) {
++                      snf->sect_bf[i] = rc;
++
++                      if (snf->sect_bf[i] > max_bitflips)
++                              max_bitflips = snf->sect_bf[i];
++
++                      snand_log_ecc(snf->pdev,
++                            "%u bitflip%s corrected in page %u sect %u\n",
++                            rc, rc > 1 ? "s" : "", page, i);
++              } else {
++                      snf->sect_bf[i] = 0;
++              }
++      }
++
++      return ret ? ret : max_bitflips;
++}
++
++static int mtk_snand_read_cache(struct mtk_snand *snf, uint32_t page, bool raw)
++{
++      uint32_t coladdr, rwbytes, mode, len, val;
++      uintptr_t dma_addr;
++      int ret;
++
++      /* Column address with plane bit */
++      coladdr = mtk_snand_get_plane_address(snf, page);
++
++      mtk_snand_mac_reset(snf);
++      mtk_nfi_reset(snf);
++
++      /* Command and dummy cycles */
++      nfi_write32(snf, SNF_RD_CTL2,
++                  ((uint32_t)snf->dummy_rfc << DATA_READ_DUMMY_S) |
++                  (snf->opcode_rfc << DATA_READ_CMD_S));
++
++      /* Column address */
++      nfi_write32(snf, SNF_RD_CTL3, coladdr);
++
++      /* Set read mode */
++      mode = (uint32_t)snf->mode_rfc << DATA_READ_MODE_S;
++      nfi_rmw32(snf, SNF_MISC_CTL, DATA_READ_MODE,
++                      mode | DATARD_CUSTOM_EN | (snf->nfi_soc->latch_lat << LATCH_LAT_S));
++
++      /* Set bytes to read */
++      rwbytes = snf->ecc_steps * snf->raw_sector_size;
++      nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) |
++                  rwbytes);
++
++      /* NFI read prepare */
++      mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN;
++      nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_CUST << CNFG_OP_MODE_S) |
++                  CNFG_DMA_BURST_EN | CNFG_READ_MODE | CNFG_DMA_MODE | mode);
++
++      nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S));
++
++      /* Prepare for DMA read */
++      len = snf->writesize + snf->oobsize;
++      ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, false);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "DMA map from device failed with %d\n", ret);
++              return ret;
++      }
++
++      nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr);
++
++      if (!raw)
++              mtk_snand_ecc_decoder_start(snf);
++
++      /* Prepare for custom read interrupt */
++      nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_READ);
++      irq_completion_init(snf->pdev);
++
++      /* Trigger NFI into custom mode */
++      nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_READ);
++
++      /* Start DMA read */
++      nfi_rmw32(snf, NFI_CON, 0, CON_BRD);
++      nfi_write16(snf, NFI_STRDATA, STR_DATA);
++
++      /* Wait for operation finished */
++      ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1,
++                                CUS_READ_DONE, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "DMA timed out for reading from cache\n");
++              goto cleanup;
++      }
++
++      /* Wait for BUS_SEC_CNTR returning expected value */
++      ret = read32_poll_timeout(snf->nfi_base + NFI_BYTELEN, val,
++                                BUS_SEC_CNTR(val) >= snf->ecc_steps,
++                                0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "Timed out waiting for BUS_SEC_CNTR\n");
++              goto cleanup;
++      }
++
++      /* Wait for bus becoming idle */
++      ret = read32_poll_timeout(snf->nfi_base + NFI_MASTERSTA, val,
++                                !(val & snf->nfi_soc->mastersta_mask),
++                                0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "Timed out waiting for bus becoming idle\n");
++              goto cleanup;
++      }
++
++      if (!raw) {
++              ret = mtk_ecc_wait_decoder_done(snf);
++              if (ret)
++                      goto cleanup;
++
++              mtk_snand_read_fdm(snf, snf->page_cache + snf->writesize);
++
++              mtk_ecc_check_decode_error(snf);
++              mtk_snand_ecc_decoder_stop(snf);
++
++              ret = mtk_snand_check_ecc_result(snf, page);
++      }
++
++cleanup:
++      /* DMA cleanup */
++      dma_mem_unmap(snf->pdev, dma_addr, len, false);
++
++      /* Stop read */
++      nfi_write32(snf, NFI_CON, 0);
++      nfi_write16(snf, NFI_CNFG, 0);
++
++      /* Clear SNF done flag */
++      nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE);
++      nfi_write32(snf, SNF_STA_CTL1, 0);
++
++      /* Disable interrupt */
++      nfi_read32(snf, NFI_INTR_STA);
++      nfi_write32(snf, NFI_INTR_EN, 0);
++
++      nfi_rmw32(snf, SNF_MISC_CTL, DATARD_CUSTOM_EN | LATCH_LAT, 0);
++
++      return ret;
++}
++
++static void mtk_snand_from_raw_page(struct mtk_snand *snf, void *buf, void *oob)
++{
++      uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size;
++      uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size;
++      uint8_t *bufptr = buf, *oobptr = oob, *raw_sector;
++
++      for (i = 0; i < snf->ecc_steps; i++) {
++              raw_sector = snf->page_cache + i * snf->raw_sector_size;
++
++              if (buf) {
++                      memcpy(bufptr, raw_sector, snf->nfi_soc->sector_size);
++                      bufptr += snf->nfi_soc->sector_size;
++              }
++
++              raw_sector += snf->nfi_soc->sector_size;
++
++              if (oob) {
++                      memcpy(oobptr, raw_sector, snf->nfi_soc->fdm_size);
++                      oobptr += snf->nfi_soc->fdm_size;
++                      raw_sector += snf->nfi_soc->fdm_size;
++
++                      memcpy(eccptr, raw_sector, ecc_bytes);
++                      eccptr += ecc_bytes;
++              }
++      }
++}
++
++static int mtk_snand_do_read_page(struct mtk_snand *snf, uint64_t addr,
++                                void *buf, void *oob, bool raw, bool format)
++{
++      uint64_t die_addr;
++      uint32_t page, dly_ctrl3;
++      int ret, retry_cnt = 0;
++
++      die_addr = mtk_snand_select_die_address(snf, addr);
++      page = die_addr >> snf->writesize_shift;
++
++      dly_ctrl3 = nfi_read32(snf, SNF_DLY_CTL3);
++
++      ret = mtk_snand_page_op(snf, page, SNAND_CMD_READ_TO_CACHE);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL);
++      if (ret < 0) {
++              snand_log_chip(snf->pdev, "Read to cache command timed out\n");
++              return ret;
++      }
++
++retry:
++      ret = mtk_snand_read_cache(snf, page, raw);
++      if (ret < 0 && ret != -EBADMSG)
++              return ret;
++
++      if (ret == -EBADMSG && retry_cnt < 16) {
++              nfi_write32(snf, SNF_DLY_CTL3, retry_cnt * 2);
++              retry_cnt++;
++              goto retry;
++      }
++
++      if (retry_cnt) {
++              if(ret == -EBADMSG) {
++                      nfi_write32(snf, SNF_DLY_CTL3, dly_ctrl3);
++                      snand_log_chip(snf->pdev,
++                                     "NFI calibration failed. Original sample delay: 0x%x\n",
++                                     dly_ctrl3);
++              } else {
++                      snand_log_chip(snf->pdev,
++                                     "NFI calibration passed. New sample delay: 0x%x\n",
++                                     nfi_read32(snf, SNF_DLY_CTL3));
++              }
++      }
++
++      if (raw) {
++              if (format) {
++                      mtk_snand_bm_swap_raw(snf);
++                      mtk_snand_fdm_bm_swap_raw(snf);
++                      mtk_snand_from_raw_page(snf, buf, oob);
++              } else {
++                      if (buf)
++                              memcpy(buf, snf->page_cache, snf->writesize);
++
++                      if (oob) {
++                              memset(oob, 0xff, snf->oobsize);
++                              memcpy(oob, snf->page_cache + snf->writesize,
++                                     snf->ecc_steps * snf->spare_per_sector);
++                      }
++              }
++      } else {
++              mtk_snand_bm_swap(snf);
++              mtk_snand_fdm_bm_swap(snf);
++
++              if (buf)
++                      memcpy(buf, snf->page_cache, snf->writesize);
++
++              if (oob) {
++                      memset(oob, 0xff, snf->oobsize);
++                      memcpy(oob, snf->page_cache + snf->writesize,
++                             snf->ecc_steps * snf->nfi_soc->fdm_size);
++              }
++      }
++
++      return ret;
++}
++
++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf,
++                      void *oob, bool raw)
++{
++      if (!snf || (!buf && !oob))
++              return -EINVAL;
++
++      if (addr >= snf->size)
++              return -EINVAL;
++
++      return mtk_snand_do_read_page(snf, addr, buf, oob, raw, true);
++}
++
++static void mtk_snand_write_fdm(struct mtk_snand *snf, const uint8_t *buf)
++{
++      uint32_t vall, valm, fdm_size = snf->nfi_soc->fdm_size;
++      const uint8_t *oobptr = buf;
++      int i, j;
++
++      for (i = 0; i < snf->ecc_steps; i++) {
++              vall = 0;
++              valm = 0;
++
++              for (j = 0; j < 8; j++) {
++                      if (j < 4)
++                              vall |= (j < fdm_size ? oobptr[j] : 0xff)
++                                              << (j * 8);
++                      else
++                              valm |= (j < fdm_size ? oobptr[j] : 0xff)
++                                              << ((j - 4) * 8);
++              }
++
++              nfi_write32(snf, NFI_FDML(i), vall);
++              nfi_write32(snf, NFI_FDMM(i), valm);
++
++              oobptr += fdm_size;
++      }
++}
++
++static int mtk_snand_program_load(struct mtk_snand *snf, uint32_t page,
++                                bool raw)
++{
++      uint32_t coladdr, rwbytes, mode, len, val;
++      uintptr_t dma_addr;
++      int ret;
++
++      /* Column address with plane bit */
++      coladdr = mtk_snand_get_plane_address(snf, page);
++
++      mtk_snand_mac_reset(snf);
++      mtk_nfi_reset(snf);
++
++      /* Write FDM registers if necessary */
++      if (!raw)
++              mtk_snand_write_fdm(snf, snf->page_cache + snf->writesize);
++
++      /* Command */
++      nfi_write32(snf, SNF_PG_CTL1, (snf->opcode_pl << PG_LOAD_CMD_S));
++
++      /* Column address */
++      nfi_write32(snf, SNF_PG_CTL2, coladdr);
++
++      /* Set write mode */
++      mode = snf->mode_pl ? PG_LOAD_X4_EN : 0;
++      nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_X4_EN, mode | PG_LOAD_CUSTOM_EN);
++
++      /* Set bytes to write */
++      rwbytes = snf->ecc_steps * snf->raw_sector_size;
++      nfi_write32(snf, SNF_MISC_CTL2, (rwbytes << PROGRAM_LOAD_BYTE_NUM_S) |
++                  rwbytes);
++
++      /* NFI write prepare */
++      mode = raw ? 0 : CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN;
++      nfi_write16(snf, NFI_CNFG, (CNFG_OP_MODE_PROGRAM << CNFG_OP_MODE_S) |
++                  CNFG_DMA_BURST_EN | CNFG_DMA_MODE | mode);
++
++      nfi_write32(snf, NFI_CON, (snf->ecc_steps << CON_SEC_NUM_S));
++
++      /* Prepare for DMA write */
++      len = snf->writesize + snf->oobsize;
++      ret = dma_mem_map(snf->pdev, snf->page_cache, &dma_addr, len, true);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "DMA map to device failed with %d\n", ret);
++              return ret;
++      }
++
++      nfi_write32(snf, NFI_STRADDR, (uint32_t)dma_addr);
++
++      if (!raw)
++              mtk_snand_ecc_encoder_start(snf);
++
++      /* Prepare for custom write interrupt */
++      nfi_write32(snf, NFI_INTR_EN, NFI_IRQ_INTR_EN | NFI_IRQ_CUS_PG);
++      irq_completion_init(snf->pdev);
++
++      /* Trigger NFI into custom mode */
++      nfi_write16(snf, NFI_CMD, NFI_CMD_DUMMY_WRITE);
++
++      /* Start DMA write */
++      nfi_rmw32(snf, NFI_CON, 0, CON_BWR);
++      nfi_write16(snf, NFI_STRDATA, STR_DATA);
++
++      /* Wait for operation finished */
++      ret = irq_completion_wait(snf->pdev, snf->nfi_base + SNF_STA_CTL1,
++                                CUS_PG_DONE, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "DMA timed out for program load\n");
++              goto cleanup;
++      }
++
++      /* Wait for NFI_SEC_CNTR returning expected value */
++      ret = read32_poll_timeout(snf->nfi_base + NFI_ADDRCNTR, val,
++                                NFI_SEC_CNTR(val) >= snf->ecc_steps,
++                                0, SNFI_POLL_INTERVAL);
++      if (ret) {
++              snand_log_nfi(snf->pdev,
++                            "Timed out waiting for BUS_SEC_CNTR\n");
++              goto cleanup;
++      }
++
++      if (!raw)
++              mtk_snand_ecc_encoder_stop(snf);
++
++cleanup:
++      /* DMA cleanup */
++      dma_mem_unmap(snf->pdev, dma_addr, len, true);
++
++      /* Stop write */
++      nfi_write32(snf, NFI_CON, 0);
++      nfi_write16(snf, NFI_CNFG, 0);
++
++      /* Clear SNF done flag */
++      nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_PG_DONE);
++      nfi_write32(snf, SNF_STA_CTL1, 0);
++
++      /* Disable interrupt */
++      nfi_read32(snf, NFI_INTR_STA);
++      nfi_write32(snf, NFI_INTR_EN, 0);
++
++      nfi_rmw32(snf, SNF_MISC_CTL, PG_LOAD_CUSTOM_EN, 0);
++
++      return ret;
++}
++
++static void mtk_snand_to_raw_page(struct mtk_snand *snf,
++                                const void *buf, const void *oob,
++                                bool empty_ecc)
++{
++      uint32_t i, ecc_bytes = snf->spare_per_sector - snf->nfi_soc->fdm_size;
++      const uint8_t *eccptr = oob + snf->ecc_steps * snf->nfi_soc->fdm_size;
++      const uint8_t *bufptr = buf, *oobptr = oob;
++      uint8_t *raw_sector;
++
++      memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize);
++      for (i = 0; i < snf->ecc_steps; i++) {
++              raw_sector = snf->page_cache + i * snf->raw_sector_size;
++
++              if (buf) {
++                      memcpy(raw_sector, bufptr, snf->nfi_soc->sector_size);
++                      bufptr += snf->nfi_soc->sector_size;
++              }
++
++              raw_sector += snf->nfi_soc->sector_size;
++
++              if (oob) {
++                      memcpy(raw_sector, oobptr, snf->nfi_soc->fdm_size);
++                      oobptr += snf->nfi_soc->fdm_size;
++                      raw_sector += snf->nfi_soc->fdm_size;
++
++                      if (empty_ecc)
++                              memset(raw_sector, 0xff, ecc_bytes);
++                      else
++                              memcpy(raw_sector, eccptr, ecc_bytes);
++                      eccptr += ecc_bytes;
++              }
++      }
++}
++
++static bool mtk_snand_is_empty_page(struct mtk_snand *snf, const void *buf,
++                                  const void *oob)
++{
++      const uint8_t *p = buf;
++      uint32_t i, j;
++
++      if (buf) {
++              for (i = 0; i < snf->writesize; i++) {
++                      if (p[i] != 0xff)
++                              return false;
++              }
++      }
++
++      if (oob) {
++              for (j = 0; j < snf->ecc_steps; j++) {
++                      p = oob + j * snf->nfi_soc->fdm_size;
++
++                      for (i = 0; i < snf->nfi_soc->fdm_ecc_size; i++) {
++                              if (p[i] != 0xff)
++                                      return false;
++                      }
++              }
++      }
++
++      return true;
++}
++
++static int mtk_snand_do_write_page(struct mtk_snand *snf, uint64_t addr,
++                                 const void *buf, const void *oob,
++                                 bool raw, bool format)
++{
++      uint64_t die_addr;
++      bool empty_ecc = false;
++      uint32_t page;
++      int ret;
++
++      die_addr = mtk_snand_select_die_address(snf, addr);
++      page = die_addr >> snf->writesize_shift;
++
++      if (!raw && mtk_snand_is_empty_page(snf, buf, oob)) {
++              /*
++               * If the data in the page to be ecc-ed is full 0xff,
++               * change to raw write mode
++               */
++              raw = true;
++              format = true;
++
++              /* fill ecc parity code region with 0xff */
++              empty_ecc = true;
++      }
++
++      if (raw) {
++              if (format) {
++                      mtk_snand_to_raw_page(snf, buf, oob, empty_ecc);
++                      mtk_snand_fdm_bm_swap_raw(snf);
++                      mtk_snand_bm_swap_raw(snf);
++              } else {
++                      memset(snf->page_cache, 0xff,
++                             snf->writesize + snf->oobsize);
++
++                      if (buf)
++                              memcpy(snf->page_cache, buf, snf->writesize);
++
++                      if (oob) {
++                              memcpy(snf->page_cache + snf->writesize, oob,
++                                     snf->ecc_steps * snf->spare_per_sector);
++                      }
++              }
++      } else {
++              memset(snf->page_cache, 0xff, snf->writesize + snf->oobsize);
++              if (buf)
++                      memcpy(snf->page_cache, buf, snf->writesize);
++
++              if (oob) {
++                      memcpy(snf->page_cache + snf->writesize, oob,
++                             snf->ecc_steps * snf->nfi_soc->fdm_size);
++              }
++
++              mtk_snand_fdm_bm_swap(snf);
++              mtk_snand_bm_swap(snf);
++      }
++
++      ret = mtk_snand_write_enable(snf);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_program_load(snf, page, raw);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_page_op(snf, page, SNAND_CMD_PROGRAM_EXECUTE);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL);
++      if (ret < 0) {
++              snand_log_chip(snf->pdev,
++                             "Page program command timed out on page %u\n",
++                             page);
++              return ret;
++      }
++
++      if (ret & SNAND_STATUS_PROGRAM_FAIL) {
++              snand_log_chip(snf->pdev,
++                             "Page program failed on page %u\n", page);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf,
++                       const void *oob, bool raw)
++{
++      if (!snf || (!buf && !oob))
++              return -EINVAL;
++
++      if (addr >= snf->size)
++              return -EINVAL;
++
++      return mtk_snand_do_write_page(snf, addr, buf, oob, raw, true);
++}
++
++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr)
++{
++      uint64_t die_addr;
++      uint32_t page, block;
++      int ret;
++
++      if (!snf)
++              return -EINVAL;
++
++      if (addr >= snf->size)
++              return -EINVAL;
++
++      die_addr = mtk_snand_select_die_address(snf, addr);
++      block = die_addr >> snf->erasesize_shift;
++      page = block << (snf->erasesize_shift - snf->writesize_shift);
++
++      ret = mtk_snand_write_enable(snf);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_page_op(snf, page, SNAND_CMD_BLOCK_ERASE);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_poll_status(snf, SNFI_POLL_INTERVAL);
++      if (ret < 0) {
++              snand_log_chip(snf->pdev,
++                             "Block erase command timed out on block %u\n",
++                             block);
++              return ret;
++      }
++
++      if (ret & SNAND_STATUS_ERASE_FAIL) {
++              snand_log_chip(snf->pdev,
++                             "Block erase failed on block %u\n", block);
++              return -EIO;
++      }
++
++      return 0;
++}
++
++static int mtk_snand_block_isbad_std(struct mtk_snand *snf, uint64_t addr)
++{
++      int ret;
++
++      ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true,
++                                   false);
++      if (ret && ret != -EBADMSG)
++              return ret;
++
++      return snf->buf_cache[0] != 0xff;
++}
++
++static int mtk_snand_block_isbad_mtk(struct mtk_snand *snf, uint64_t addr)
++{
++      int ret;
++
++      ret = mtk_snand_do_read_page(snf, addr, NULL, snf->buf_cache, true,
++                                   true);
++      if (ret && ret != -EBADMSG)
++              return ret;
++
++      return snf->buf_cache[0] != 0xff;
++}
++
++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr)
++{
++      if (!snf)
++              return -EINVAL;
++
++      if (addr >= snf->size)
++              return -EINVAL;
++
++      addr &= ~snf->erasesize_mask;
++
++      if (snf->nfi_soc->bbm_swap)
++              return mtk_snand_block_isbad_std(snf, addr);
++
++      return mtk_snand_block_isbad_mtk(snf, addr);
++}
++
++static int mtk_snand_block_markbad_std(struct mtk_snand *snf, uint64_t addr)
++{
++      /* Standard BBM position */
++      memset(snf->buf_cache, 0xff, snf->oobsize);
++      snf->buf_cache[0] = 0;
++
++      return mtk_snand_do_write_page(snf, addr, NULL, snf->buf_cache, true,
++                                     false);
++}
++
++static int mtk_snand_block_markbad_mtk(struct mtk_snand *snf, uint64_t addr)
++{
++      /* Write the whole page with zeros */
++      memset(snf->buf_cache, 0, snf->writesize + snf->oobsize);
++
++      return mtk_snand_do_write_page(snf, addr, snf->buf_cache,
++                                     snf->buf_cache + snf->writesize, true,
++                                     true);
++}
++
++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr)
++{
++      if (!snf)
++              return -EINVAL;
++
++      if (addr >= snf->size)
++              return -EINVAL;
++
++      addr &= ~snf->erasesize_mask;
++
++      if (snf->nfi_soc->bbm_swap)
++              return mtk_snand_block_markbad_std(snf, addr);
++
++      return mtk_snand_block_markbad_mtk(snf, addr);
++}
++
++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw,
++                     const uint8_t *oobbuf, size_t ooblen)
++{
++      size_t len = ooblen, sect_fdm_len;
++      const uint8_t *oob = oobbuf;
++      uint32_t step = 0;
++
++      if (!snf || !oobraw || !oob)
++              return -EINVAL;
++
++      while (len && step < snf->ecc_steps) {
++              sect_fdm_len = snf->nfi_soc->fdm_size - 1;
++              if (sect_fdm_len > len)
++                      sect_fdm_len = len;
++
++              memcpy(oobraw + step * snf->nfi_soc->fdm_size + 1, oob,
++                     sect_fdm_len);
++
++              len -= sect_fdm_len;
++              oob += sect_fdm_len;
++              step++;
++      }
++
++      return len;
++}
++
++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf,
++                         size_t ooblen, const uint8_t *oobraw)
++{
++      size_t len = ooblen, sect_fdm_len;
++      uint8_t *oob = oobbuf;
++      uint32_t step = 0;
++
++      if (!snf || !oobraw || !oob)
++              return -EINVAL;
++
++      while (len && step < snf->ecc_steps) {
++              sect_fdm_len = snf->nfi_soc->fdm_size - 1;
++              if (sect_fdm_len > len)
++                      sect_fdm_len = len;
++
++              memcpy(oob, oobraw + step * snf->nfi_soc->fdm_size + 1,
++                     sect_fdm_len);
++
++              len -= sect_fdm_len;
++              oob += sect_fdm_len;
++              step++;
++      }
++
++      return len;
++}
++
++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr,
++                               void *buf, void *oob, size_t ooblen,
++                               size_t *actualooblen, bool raw)
++{
++      int ret, oobremain;
++
++      if (!snf)
++              return -EINVAL;
++
++      if (!oob)
++              return mtk_snand_read_page(snf, addr, buf, NULL, raw);
++
++      ret = mtk_snand_read_page(snf, addr, buf, snf->buf_cache, raw);
++      if (ret && ret != -EBADMSG) {
++              if (actualooblen)
++                      *actualooblen = 0;
++              return ret;
++      }
++
++      oobremain = mtk_snand_transfer_oob(snf, oob, ooblen, snf->buf_cache);
++      if (actualooblen)
++              *actualooblen = ooblen - oobremain;
++
++      return ret;
++}
++
++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr,
++                                const void *buf, const void *oob,
++                                size_t ooblen, size_t *actualooblen, bool raw)
++{
++      int oobremain;
++
++      if (!snf)
++              return -EINVAL;
++
++      if (!oob)
++              return mtk_snand_write_page(snf, addr, buf, NULL, raw);
++
++      memset(snf->buf_cache, 0xff, snf->oobsize);
++      oobremain = mtk_snand_fill_oob(snf, snf->buf_cache, oob, ooblen);
++      if (actualooblen)
++              *actualooblen = ooblen - oobremain;
++
++      return mtk_snand_write_page(snf, addr, buf, snf->buf_cache, raw);
++}
++
++int mtk_snand_get_chip_info(struct mtk_snand *snf,
++                          struct mtk_snand_chip_info *info)
++{
++      if (!snf || !info)
++              return -EINVAL;
++
++      info->model = snf->model;
++      info->chipsize = snf->size;
++      info->blocksize = snf->erasesize;
++      info->pagesize = snf->writesize;
++      info->sparesize = snf->oobsize;
++      info->spare_per_sector = snf->spare_per_sector;
++      info->fdm_size = snf->nfi_soc->fdm_size;
++      info->fdm_ecc_size = snf->nfi_soc->fdm_ecc_size;
++      info->num_sectors = snf->ecc_steps;
++      info->sector_size = snf->nfi_soc->sector_size;
++      info->ecc_strength = snf->ecc_strength;
++      info->ecc_bytes = snf->ecc_bytes;
++
++      return 0;
++}
++
++int mtk_snand_irq_process(struct mtk_snand *snf)
++{
++      uint32_t sta, ien;
++
++      if (!snf)
++              return -EINVAL;
++
++      sta = nfi_read32(snf, NFI_INTR_STA);
++      ien = nfi_read32(snf, NFI_INTR_EN);
++
++      if (!(sta & ien))
++              return 0;
++
++      nfi_write32(snf, NFI_INTR_EN, 0);
++      irq_completion_done(snf->pdev);
++
++      return 1;
++}
++
++static int mtk_snand_select_spare_per_sector(struct mtk_snand *snf)
++{
++      uint32_t spare_per_step = snf->oobsize / snf->ecc_steps;
++      int i, mul = 1;
++
++      /*
++       * If we're using the 1KB sector size, HW will automatically
++       * double the spare size. So we should only use half of the value.
++       */
++      if (snf->nfi_soc->sector_size == 1024)
++              mul = 2;
++
++      spare_per_step /= mul;
++
++      for (i = snf->nfi_soc->num_spare_size - 1; i >= 0; i--) {
++              if (snf->nfi_soc->spare_sizes[i] <= spare_per_step) {
++                      snf->spare_per_sector = snf->nfi_soc->spare_sizes[i];
++                      snf->spare_per_sector *= mul;
++                      return i;
++              }
++      }
++
++      snand_log_nfi(snf->pdev,
++                    "Page size %u+%u is not supported\n", snf->writesize,
++                    snf->oobsize);
++
++      return -1;
++}
++
++static int mtk_snand_pagefmt_setup(struct mtk_snand *snf)
++{
++      uint32_t spare_size_idx, spare_size_shift, pagesize_idx;
++      uint32_t sector_size_512;
++
++      if (snf->nfi_soc->sector_size == 512) {
++              sector_size_512 = NFI_SEC_SEL_512;
++              spare_size_shift = NFI_SPARE_SIZE_S;
++      } else {
++              sector_size_512 = 0;
++              spare_size_shift = NFI_SPARE_SIZE_LS_S;
++      }
++
++      switch (snf->writesize) {
++      case SZ_512:
++              pagesize_idx = NFI_PAGE_SIZE_512_2K;
++              break;
++      case SZ_2K:
++              if (snf->nfi_soc->sector_size == 512)
++                      pagesize_idx = NFI_PAGE_SIZE_2K_4K;
++              else
++                      pagesize_idx = NFI_PAGE_SIZE_512_2K;
++              break;
++      case SZ_4K:
++              if (snf->nfi_soc->sector_size == 512)
++                      pagesize_idx = NFI_PAGE_SIZE_4K_8K;
++              else
++                      pagesize_idx = NFI_PAGE_SIZE_2K_4K;
++              break;
++      case SZ_8K:
++              if (snf->nfi_soc->sector_size == 512)
++                      pagesize_idx = NFI_PAGE_SIZE_8K_16K;
++              else
++                      pagesize_idx = NFI_PAGE_SIZE_4K_8K;
++              break;
++      case SZ_16K:
++              pagesize_idx = NFI_PAGE_SIZE_8K_16K;
++              break;
++      default:
++              snand_log_nfi(snf->pdev, "Page size %u is not supported\n",
++                            snf->writesize);
++              return -ENOTSUPP;
++      }
++
++      spare_size_idx = mtk_snand_select_spare_per_sector(snf);
++      if (unlikely(spare_size_idx < 0))
++              return -ENOTSUPP;
++
++      snf->raw_sector_size = snf->nfi_soc->sector_size +
++                             snf->spare_per_sector;
++
++      /* Setup page format */
++      nfi_write32(snf, NFI_PAGEFMT,
++                  (snf->nfi_soc->fdm_ecc_size << NFI_FDM_ECC_NUM_S) |
++                  (snf->nfi_soc->fdm_size << NFI_FDM_NUM_S) |
++                  (spare_size_idx << spare_size_shift) |
++                  (pagesize_idx << NFI_PAGE_SIZE_S) |
++                  sector_size_512);
++
++      return 0;
++}
++
++static enum snand_flash_io mtk_snand_select_opcode(struct mtk_snand *snf,
++                                 uint32_t snfi_caps, uint8_t *opcode,
++                                 uint8_t *dummy,
++                                 const struct snand_io_cap *op_cap)
++{
++      uint32_t i, caps;
++
++      caps = snfi_caps & op_cap->caps;
++
++      i = fls(caps);
++      if (i > 0) {
++              *opcode = op_cap->opcodes[i - 1].opcode;
++              if (dummy)
++                      *dummy = op_cap->opcodes[i - 1].dummy;
++              return i - 1;
++      }
++
++      return __SNAND_IO_MAX;
++}
++
++static int mtk_snand_select_opcode_rfc(struct mtk_snand *snf,
++                                     uint32_t snfi_caps,
++                                     const struct snand_io_cap *op_cap)
++{
++      enum snand_flash_io idx;
++
++      static const uint8_t rfc_modes[__SNAND_IO_MAX] = {
++              [SNAND_IO_1_1_1] = DATA_READ_MODE_X1,
++              [SNAND_IO_1_1_2] = DATA_READ_MODE_X2,
++              [SNAND_IO_1_2_2] = DATA_READ_MODE_DUAL,
++              [SNAND_IO_1_1_4] = DATA_READ_MODE_X4,
++              [SNAND_IO_1_4_4] = DATA_READ_MODE_QUAD,
++      };
++
++      idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_rfc,
++                                    &snf->dummy_rfc, op_cap);
++      if (idx >= __SNAND_IO_MAX) {
++              snand_log_snfi(snf->pdev,
++                             "No capable opcode for read from cache\n");
++              return -ENOTSUPP;
++      }
++
++      snf->mode_rfc = rfc_modes[idx];
++
++      if (idx == SNAND_IO_1_1_4 || idx == SNAND_IO_1_4_4)
++              snf->quad_spi_op = true;
++
++      return 0;
++}
++
++static int mtk_snand_select_opcode_pl(struct mtk_snand *snf, uint32_t snfi_caps,
++                                    const struct snand_io_cap *op_cap)
++{
++      enum snand_flash_io idx;
++
++      static const uint8_t pl_modes[__SNAND_IO_MAX] = {
++              [SNAND_IO_1_1_1] = 0,
++              [SNAND_IO_1_1_4] = 1,
++      };
++
++      idx = mtk_snand_select_opcode(snf, snfi_caps, &snf->opcode_pl,
++                                    NULL, op_cap);
++      if (idx >= __SNAND_IO_MAX) {
++              snand_log_snfi(snf->pdev,
++                             "No capable opcode for program load\n");
++              return -ENOTSUPP;
++      }
++
++      snf->mode_pl = pl_modes[idx];
++
++      if (idx == SNAND_IO_1_1_4)
++              snf->quad_spi_op = true;
++
++      return 0;
++}
++
++static int mtk_snand_setup(struct mtk_snand *snf,
++                         const struct snand_flash_info *snand_info)
++{
++      const struct snand_mem_org *memorg = &snand_info->memorg;
++      uint32_t i, msg_size, snfi_caps;
++      int ret;
++
++      /* Calculate flash memory organization */
++      snf->model = snand_info->model;
++      snf->writesize = memorg->pagesize;
++      snf->oobsize = memorg->sparesize;
++      snf->erasesize = snf->writesize * memorg->pages_per_block;
++      snf->die_size = (uint64_t)snf->erasesize * memorg->blocks_per_die;
++      snf->size = snf->die_size * memorg->ndies;
++      snf->num_dies = memorg->ndies;
++
++      snf->writesize_mask = snf->writesize - 1;
++      snf->erasesize_mask = snf->erasesize - 1;
++      snf->die_mask = snf->die_size - 1;
++
++      snf->writesize_shift = ffs(snf->writesize) - 1;
++      snf->erasesize_shift = ffs(snf->erasesize) - 1;
++      snf->die_shift = mtk_snand_ffs64(snf->die_size) - 1;
++
++      snf->select_die = snand_info->select_die;
++
++      /* Determine opcodes for read from cache/program load */
++      snfi_caps = SPI_IO_1_1_1 | SPI_IO_1_1_2 | SPI_IO_1_2_2;
++      if (snf->snfi_quad_spi)
++              snfi_caps |= SPI_IO_1_1_4 | SPI_IO_1_4_4;
++
++      ret = mtk_snand_select_opcode_rfc(snf, snfi_caps, snand_info->cap_rd);
++      if (ret)
++              return ret;
++
++      ret = mtk_snand_select_opcode_pl(snf, snfi_caps, snand_info->cap_pl);
++      if (ret)
++              return ret;
++
++      /* ECC and page format */
++      snf->ecc_steps = snf->writesize / snf->nfi_soc->sector_size;
++      if (snf->ecc_steps > snf->nfi_soc->max_sectors) {
++              snand_log_nfi(snf->pdev, "Page size %u is not supported\n",
++                            snf->writesize);
++              return -ENOTSUPP;
++      }
++
++      ret = mtk_snand_pagefmt_setup(snf);
++      if (ret)
++              return ret;
++
++      msg_size = snf->nfi_soc->sector_size + snf->nfi_soc->fdm_ecc_size;
++      ret = mtk_ecc_setup(snf, snf->nfi_base + NFI_FDM0L,
++                          snf->spare_per_sector - snf->nfi_soc->fdm_size,
++                          msg_size);
++      if (ret)
++              return ret;
++
++      nfi_write16(snf, NFI_CNFG, 0);
++
++      /* Tuning options */
++      nfi_write16(snf, NFI_DEBUG_CON1, WBUF_EN);
++      nfi_write32(snf, SNF_DLY_CTL3, (snf->nfi_soc->sample_delay << SFCK_SAM_DLY_S));
++
++      /* Interrupts */
++      nfi_read32(snf, NFI_INTR_STA);
++      nfi_write32(snf, NFI_INTR_EN, 0);
++
++      /* Clear SNF done flag */
++      nfi_rmw32(snf, SNF_STA_CTL1, 0, CUS_READ_DONE | CUS_PG_DONE);
++      nfi_write32(snf, SNF_STA_CTL1, 0);
++
++      /* Initialization on all dies */
++      for (i = 0; i < snf->num_dies; i++) {
++              mtk_snand_select_die(snf, i);
++
++              /* Disable On-Die ECC engine */
++              ret = mtk_snand_ondie_ecc_control(snf, false);
++              if (ret)
++                      return ret;
++
++              /* Disable block protection */
++              mtk_snand_unlock(snf);
++
++              /* Enable/disable quad-spi */
++              mtk_snand_qspi_control(snf, snf->quad_spi_op);
++      }
++
++      mtk_snand_select_die(snf, 0);
++
++      return 0;
++}
++
++static int mtk_snand_id_probe(struct mtk_snand *snf,
++                            const struct snand_flash_info **snand_info)
++{
++      uint8_t id[4], op[2];
++      int ret;
++
++      /* Read SPI-NAND JEDEC ID, OP + dummy/addr + ID */
++      op[0] = SNAND_CMD_READID;
++      op[1] = 0;
++      ret = mtk_snand_mac_io(snf, op, 2, id, sizeof(id));
++      if (ret)
++              return ret;
++
++      *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id);
++      if (*snand_info)
++              return 0;
++
++      /* Read SPI-NAND JEDEC ID, OP + ID */
++      op[0] = SNAND_CMD_READID;
++      ret = mtk_snand_mac_io(snf, op, 1, id, sizeof(id));
++      if (ret)
++              return ret;
++
++      *snand_info = snand_flash_id_lookup(SNAND_ID_DYMMY, id);
++      if (*snand_info)
++              return 0;
++
++      snand_log_chip(snf->pdev,
++                     "Unrecognized SPI-NAND ID: %02x %02x %02x %02x\n",
++                     id[0], id[1], id[2], id[3]);
++
++      return -EINVAL;
++}
++
++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata,
++                 struct mtk_snand **psnf)
++{
++      const struct snand_flash_info *snand_info;
++      uint32_t rawpage_size, sect_bf_size;
++      struct mtk_snand tmpsnf, *snf;
++      int ret;
++
++      if (!pdata || !psnf)
++              return -EINVAL;
++
++      if (pdata->soc >= __SNAND_SOC_MAX) {
++              snand_log_chip(dev, "Invalid SOC %u for MTK-SNAND\n",
++                             pdata->soc);
++              return -EINVAL;
++      }
++
++      /* Dummy instance only for initial reset and id probe */
++      tmpsnf.nfi_base = pdata->nfi_base;
++      tmpsnf.ecc_base = pdata->ecc_base;
++      tmpsnf.soc = pdata->soc;
++      tmpsnf.nfi_soc = &mtk_snand_socs[pdata->soc];
++      tmpsnf.pdev = dev;
++
++      /* Switch to SNFI mode */
++      writel(SPI_MODE, tmpsnf.nfi_base + SNF_CFG);
++
++      /* Reset SNFI & NFI */
++      mtk_snand_mac_reset(&tmpsnf);
++      mtk_nfi_reset(&tmpsnf);
++
++      /* Reset SPI-NAND chip */
++      ret = mtk_snand_chip_reset(&tmpsnf);
++      if (ret) {
++              snand_log_chip(dev, "Failed to reset SPI-NAND chip\n");
++              return ret;
++      }
++
++      /* Probe SPI-NAND flash by JEDEC ID */
++      ret = mtk_snand_id_probe(&tmpsnf, &snand_info);
++      if (ret)
++              return ret;
++
++      rawpage_size = snand_info->memorg.pagesize +
++                     snand_info->memorg.sparesize;
++
++      sect_bf_size = mtk_snand_socs[pdata->soc].max_sectors *
++                     sizeof(*snf->sect_bf);
++
++      /* Allocate memory for instance and cache */
++      snf = generic_mem_alloc(dev,
++                              sizeof(*snf) + rawpage_size + sect_bf_size);
++      if (!snf) {
++              snand_log_chip(dev, "Failed to allocate memory for instance\n");
++              return -ENOMEM;
++      }
++
++      snf->sect_bf = (int *)((uintptr_t)snf + sizeof(*snf));
++      snf->buf_cache = (uint8_t *)((uintptr_t)snf->sect_bf + sect_bf_size);
++
++      /* Allocate memory for DMA buffer */
++      snf->page_cache = dma_mem_alloc(dev, rawpage_size);
++      if (!snf->page_cache) {
++              generic_mem_free(dev, snf);
++              snand_log_chip(dev,
++                             "Failed to allocate memory for DMA buffer\n");
++              return -ENOMEM;
++      }
++
++      /* Fill up instance */
++      snf->pdev = dev;
++      snf->nfi_base = pdata->nfi_base;
++      snf->ecc_base = pdata->ecc_base;
++      snf->soc = pdata->soc;
++      snf->nfi_soc = &mtk_snand_socs[pdata->soc];
++      snf->snfi_quad_spi = pdata->quad_spi;
++
++      /* Initialize SNFI & ECC engine */
++      ret = mtk_snand_setup(snf, snand_info);
++      if (ret) {
++              dma_mem_free(dev, snf->page_cache);
++              generic_mem_free(dev, snf);
++              return ret;
++      }
++
++      *psnf = snf;
++
++      return 0;
++}
++
++int mtk_snand_cleanup(struct mtk_snand *snf)
++{
++      if (!snf)
++              return 0;
++
++      dma_mem_free(snf->pdev, snf->page_cache);
++      generic_mem_free(snf->pdev, snf);
++
++      return 0;
++}
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/mtk-snand.h
+@@ -0,0 +1,77 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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_MT7981,
++      SNAND_SOC_MT7986,
++      __SNAND_SOC_MAX
++};
++
++struct mtk_snand_platdata {
++      void *nfi_base;
++      void *ecc_base;
++      enum mtk_snand_soc soc;
++      bool quad_spi;
++};
++
++struct mtk_snand_chip_info {
++      const char *model;
++      uint64_t chipsize;
++      uint32_t blocksize;
++      uint32_t pagesize;
++      uint32_t sparesize;
++      uint32_t spare_per_sector;
++      uint32_t fdm_size;
++      uint32_t fdm_ecc_size;
++      uint32_t num_sectors;
++      uint32_t sector_size;
++      uint32_t ecc_strength;
++      uint32_t ecc_bytes;
++};
++
++struct mtk_snand;
++struct snand_flash_info;
++
++int mtk_snand_init(void *dev, const struct mtk_snand_platdata *pdata,
++                 struct mtk_snand **psnf);
++int mtk_snand_cleanup(struct mtk_snand *snf);
++
++int mtk_snand_chip_reset(struct mtk_snand *snf);
++int mtk_snand_read_page(struct mtk_snand *snf, uint64_t addr, void *buf,
++                      void *oob, bool raw);
++int mtk_snand_write_page(struct mtk_snand *snf, uint64_t addr, const void *buf,
++                       const void *oob, bool raw);
++int mtk_snand_erase_block(struct mtk_snand *snf, uint64_t addr);
++int mtk_snand_block_isbad(struct mtk_snand *snf, uint64_t addr);
++int mtk_snand_block_markbad(struct mtk_snand *snf, uint64_t addr);
++int mtk_snand_fill_oob(struct mtk_snand *snf, uint8_t *oobraw,
++                     const uint8_t *oobbuf, size_t ooblen);
++int mtk_snand_transfer_oob(struct mtk_snand *snf, uint8_t *oobbuf,
++                         size_t ooblen, const uint8_t *oobraw);
++int mtk_snand_read_page_auto_oob(struct mtk_snand *snf, uint64_t addr,
++                               void *buf, void *oob, size_t ooblen,
++                               size_t *actualooblen, bool raw);
++int mtk_snand_write_page_auto_oob(struct mtk_snand *snf, uint64_t addr,
++                                const void *buf, const void *oob,
++                                size_t ooblen, size_t *actualooblen,
++                                bool raw);
++int mtk_snand_get_chip_info(struct mtk_snand *snf,
++                          struct mtk_snand_chip_info *info);
++int mtk_snand_irq_process(struct mtk_snand *snf);
++
++#endif /* _MTK_SNAND_H_ */
diff --git a/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/100-03-mtd-mtk-snand-add-support-for-SPL.patch
new file mode 100644 (file)
index 0000000..ac56638
--- /dev/null
@@ -0,0 +1,175 @@
+From a347e374cb338213632c6dde88dd226d64bd8b27 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 37/71] mtd: mtk-snand: add support for SPL
+
+Add support to initialize SPI-NAND in SPL.
+Add implementation for SPL NAND loader.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/mtk-snand/Kconfig         |   6 ++
+ drivers/mtd/mtk-snand/Makefile        |   4 +
+ drivers/mtd/mtk-snand/mtk-snand-spl.c | 133 ++++++++++++++++++++++++++
+ 3 files changed, 143 insertions(+)
+ create mode 100644 drivers/mtd/mtk-snand/mtk-snand-spl.c
+
+--- a/drivers/mtd/mtk-snand/Kconfig
++++ b/drivers/mtd/mtk-snand/Kconfig
+@@ -19,3 +19,9 @@ config MTK_SPI_NAND_MTD
+       help
+         This option enables access to SPI-NAND flashes through the
+         MTD interface of MediaTek SPI NAND Flash Controller
++
++config SPL_MTK_SPI_NAND
++      tristate "SPL support for MediaTek SPI NAND flash controller"
++      depends on MTK_SPI_NAND
++      help
++        This option enables access to SPI-NAND flashes in the SPL stage
+--- a/drivers/mtd/mtk-snand/Makefile
++++ b/drivers/mtd/mtk-snand/Makefile
+@@ -8,4 +8,8 @@
+ obj-y += mtk-snand.o mtk-snand-ecc.o mtk-snand-ids.o mtk-snand-os.o
+ obj-$(CONFIG_MTK_SPI_NAND_MTD) += mtk-snand-mtd.o
++ifdef CONFIG_SPL_BUILD
++obj-$(CONFIG_SPL_MTK_SPI_NAND) += mtk-snand-spl.o
++endif
++
+ ccflags-y += -DPRIVATE_MTK_SNAND_HEADER
+--- /dev/null
++++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c
+@@ -0,0 +1,133 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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,mt7981-snand", .data = SNAND_SOC_MT7981 },
++      { .compatible = "mediatek,mt7986-snand", .data = SNAND_SOC_MT7986 },
++      { /* sentinel */ },
++};
++
++U_BOOT_DRIVER(mtk_snand) = {
++      .name = "mtk-snand",
++      .id = UCLASS_MTD,
++      .of_match = mtk_snand_ids,
++      .flags = DM_FLAG_PRE_RELOC,
++};
diff --git a/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch b/package/boot/uboot-mediatek/patches/100-04-env-add-support-for-generic-MTD-device.patch
new file mode 100644 (file)
index 0000000..9d6beb3
--- /dev/null
@@ -0,0 +1,409 @@
+From efc3e6f5d29f87a433b42f15a0b87e04b7cd498d 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 38/71] 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
+@@ -48,6 +48,7 @@ DECLARE_GLOBAL_DATA_PTR;
+       defined(CONFIG_ENV_IS_IN_MMC)           || \
+       defined(CONFIG_ENV_IS_IN_FAT)           || \
+       defined(CONFIG_ENV_IS_IN_EXT4)          || \
++      defined(CONFIG_ENV_IS_IN_MTD)           || \
+       defined(CONFIG_ENV_IS_IN_NAND)          || \
+       defined(CONFIG_ENV_IS_IN_NVRAM)         || \
+       defined(CONFIG_ENV_IS_IN_ONENAND)       || \
+@@ -62,7 +63,7 @@ DECLARE_GLOBAL_DATA_PTR;
+ #if   !defined(ENV_IS_IN_DEVICE)              && \
+       !defined(CONFIG_ENV_IS_NOWHERE)
+-# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|\
++# error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\
+ NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
+ #endif
+--- a/env/Kconfig
++++ b/env/Kconfig
+@@ -37,7 +37,7 @@ config ENV_IS_NOWHERE
+                    !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
+                    !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
+                    !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
+-                   !ENV_IS_IN_UBI
++                   !ENV_IS_IN_UBI && !ENV_IS_IN_MTD
+       help
+         Define this if you don't want to or can't have an environment stored
+         on a storage medium. In this case the environment will still exist
+@@ -226,6 +226,27 @@ config ENV_IS_IN_MMC
+         This value is also in units of bytes, but must also be aligned to
+         an MMC sector boundary.
++config ENV_IS_IN_MTD
++      bool "Environment in a MTD device"
++      depends on !CHAIN_OF_TRUST
++      depends on MTD
++      help
++        Define this if you have a MTD device which you want to use for
++        the environment.
++
++        - CONFIG_ENV_MTD_NAME:
++        - CONFIG_ENV_OFFSET:
++        - CONFIG_ENV_SIZE:
++
++        These three #defines specify the MTD device where the environment
++        is stored, offset and size of the environment area within the MTD
++        device. CONFIG_ENV_OFFSET must be aligned to an erase block boundary.
++
++        - CONFIG_ENV_SIZE_REDUND:
++
++        This #define specify the maximum size allowed for read/write/erase
++        with skipped bad blocks starting from ENV_OFFSET.
++
+ config ENV_IS_IN_NAND
+       bool "Environment in a NAND device"
+       depends on !CHAIN_OF_TRUST
+@@ -531,10 +552,16 @@ config ENV_ADDR_REDUND
+         Offset from the start of the device (or partition) of the redundant
+         environment location.
++config ENV_MTD_NAME
++      string "Name of the MTD device storing the environment"
++      depends on ENV_IS_IN_MTD
++      help
++        Name of the MTD device that stores the environment
++
+ config ENV_OFFSET
+       hex "Environment offset"
+       depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
+-                  ENV_IS_IN_SPI_FLASH
++                  ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
+       default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC
+       default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
+       default 0xF0000 if ARCH_SUNXI
+@@ -581,6 +608,12 @@ config ENV_SECT_SIZE
+       help
+         Size of the sector containing the environment.
++config ENV_SIZE_REDUND
++      hex "Redundant environment size"
++      depends on ENV_IS_IN_MTD
++      help
++        The maximum size allowed for read/write/erase with skipped bad blocks.
++
+ config ENV_UBI_PART
+       string "UBI partition name"
+       depends on ENV_IS_IN_UBI
+--- a/env/Makefile
++++ b/env/Makefile
+@@ -26,6 +26,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_NOWHERE)
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MMC) += mmc.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) += fat.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o
++obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o
+--- a/env/env.c
++++ b/env/env.c
+@@ -69,6 +69,9 @@ static enum env_location env_locations[]
+ #ifdef CONFIG_ENV_IS_IN_MMC
+       ENVL_MMC,
+ #endif
++#ifdef CONFIG_ENV_IS_IN_MTD
++      ENVL_MTD,
++#endif
+ #ifdef CONFIG_ENV_IS_IN_NAND
+       ENVL_NAND,
+ #endif
+--- /dev/null
++++ b/env/mtd.c
+@@ -0,0 +1,256 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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
+@@ -130,6 +130,7 @@ enum env_location {
+       ENVL_FAT,
+       ENVL_FLASH,
+       ENVL_MMC,
++      ENVL_MTD,
+       ENVL_NAND,
+       ENVL_NVRAM,
+       ENVL_ONENAND,
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -41,6 +41,7 @@ ENVCRC-$(CONFIG_ENV_IS_EMBEDDED) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_EEPROM) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y
++ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y
diff --git a/package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch b/package/boot/uboot-mediatek/patches/100-05-mtd-add-a-new-mtd-device-type-for-NMBM.patch
new file mode 100644 (file)
index 0000000..1c62130
--- /dev/null
@@ -0,0 +1,44 @@
+From d26a789c451068caf4bbb4d1ac7bc1f592b5493e Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:58:06 +0800
+Subject: [PATCH 39/71] mtd: add a new mtd device type for NMBM
+
+This patch adds a new mtd device type for NMBM so that mtdparts can be
+correctly probed. And this also gives us an opportunity to add NMBM support
+for filesystems in the future.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/mtdparts.c              | 3 +++
+ include/jffs2/load_kernel.h | 4 +++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/cmd/mtdparts.c
++++ b/cmd/mtdparts.c
+@@ -1060,6 +1060,9 @@ int mtd_id_parse(const char *id, const c
+       } else if (strncmp(p, "spi-nand", 8) == 0) {
+               *dev_type = MTD_DEV_TYPE_SPINAND;
+               p += 8;
++      } else if (strncmp(p, "nmbm", 4) == 0) {
++              *dev_type = MTD_DEV_TYPE_NMBM;
++              p += 4;
+       } else {
+               printf("incorrect device type in %s\n", id);
+               return 1;
+--- a/include/jffs2/load_kernel.h
++++ b/include/jffs2/load_kernel.h
+@@ -16,11 +16,13 @@
+ #define MTD_DEV_TYPE_NAND     0x0002
+ #define MTD_DEV_TYPE_ONENAND  0x0004
+ #define MTD_DEV_TYPE_SPINAND  0x0008
++#define MTD_DEV_TYPE_NMBM     0x0010
+ #define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" :      \
+                           (type == MTD_DEV_TYPE_NOR ? "nor" :         \
+                            (type == MTD_DEV_TYPE_ONENAND ? "onenand" : \
+-                            "spi-nand")))                             \
++                            (type == MTD_DEV_TYPE_SPINAND ? "spi-nand" : \
++                             "nmbm"))))                               \
+ struct mtd_device {
+       struct list_head link;
diff --git a/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch b/package/boot/uboot-mediatek/patches/100-06-mtd-add-core-facility-code-of-NMBM.patch
new file mode 100644 (file)
index 0000000..9f80deb
--- /dev/null
@@ -0,0 +1,3422 @@
+From 690479081fb6a0c0f77f10fb457ad69e71390f15 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:26:35 +0800
+Subject: [PATCH 40/71] mtd: add core facility code of NMBM
+
+This patch adds a NAND bad block management named NMBM (NAND mapping block
+management) which supports using a mapping table to deal with bad blocks
+before factory shipping and during use.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/Kconfig             |    2 +
+ drivers/mtd/Makefile            |    1 +
+ drivers/mtd/nmbm/Kconfig        |   29 +
+ drivers/mtd/nmbm/Makefile       |    5 +
+ drivers/mtd/nmbm/nmbm-core.c    | 2936 +++++++++++++++++++++++++++++++
+ drivers/mtd/nmbm/nmbm-debug.h   |   37 +
+ drivers/mtd/nmbm/nmbm-debug.inl |   39 +
+ drivers/mtd/nmbm/nmbm-private.h |  137 ++
+ include/nmbm/nmbm-os.h          |   66 +
+ include/nmbm/nmbm.h             |  102 ++
+ 10 files changed, 3354 insertions(+)
+ create mode 100644 drivers/mtd/nmbm/Kconfig
+ create mode 100644 drivers/mtd/nmbm/Makefile
+ create mode 100644 drivers/mtd/nmbm/nmbm-core.c
+ create mode 100644 drivers/mtd/nmbm/nmbm-debug.h
+ create mode 100644 drivers/mtd/nmbm/nmbm-debug.inl
+ create mode 100644 drivers/mtd/nmbm/nmbm-private.h
+ create mode 100644 include/nmbm/nmbm-os.h
+ create mode 100644 include/nmbm/nmbm.h
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -174,4 +174,6 @@ source "drivers/mtd/spi/Kconfig"
+ source "drivers/mtd/ubi/Kconfig"
++source "drivers/mtd/nmbm/Kconfig"
++
+ endmenu
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -41,3 +41,4 @@ obj-$(CONFIG_SPL_UBI) += ubispl/
+ endif
+ obj-$(CONFIG_MTK_SPI_NAND) += mtk-snand/
++obj-$(CONFIG_NMBM) += nmbm/
+--- /dev/null
++++ b/drivers/mtd/nmbm/Kconfig
+@@ -0,0 +1,29 @@
++
++config NMBM
++      bool "Enable NAND mapping block management"
++      default n
++
++choice
++      prompt "Default log level"
++      depends on NMBM
++      default NMBM_LOG_LEVEL_INFO
++
++config NMBM_LOG_LEVEL_DEBUG
++      bool "0 - Debug"
++
++config NMBM_LOG_LEVEL_INFO
++      bool "1 - Info"
++
++config NMBM_LOG_LEVEL_WARN
++      bool "2 - Warn"
++
++config NMBM_LOG_LEVEL_ERR
++      bool "3 - Error"
++
++config NMBM_LOG_LEVEL_EMERG
++      bool "4 - Emergency"
++
++config NMBM_LOG_LEVEL_NONE
++      bool "5 - None"
++
++endchoice
+--- /dev/null
++++ b/drivers/mtd/nmbm/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# (C) Copyright 2020 MediaTek Inc. All rights reserved.
++
++obj-$(CONFIG_NMBM) += nmbm-core.o
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-core.c
+@@ -0,0 +1,2936 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include "nmbm-private.h"
++
++#include "nmbm-debug.h"
++
++#define NMBM_VER_MAJOR                        1
++#define NMBM_VER_MINOR                        0
++#define NMBM_VER                      NMBM_VERSION_MAKE(NMBM_VER_MAJOR, \
++                                                        NMBM_VER_MINOR)
++
++#define NMBM_ALIGN(v, a)              (((v) + (a) - 1) & ~((a) - 1))
++
++/*****************************************************************************/
++/* Logging related functions */
++/*****************************************************************************/
++
++/*
++ * nmbm_log_lower - Print log using OS specific routine
++ * @nld: NMBM lower device structure
++ * @level: log level
++ * @fmt: format string
++ */
++static void nmbm_log_lower(struct nmbm_lower_device *nld,
++                         enum nmbm_log_category level, const char *fmt, ...)
++{
++      va_list ap;
++
++      if (!nld->logprint)
++              return;
++
++      va_start(ap, fmt);
++      nld->logprint(nld->arg, level, fmt, ap);
++      va_end(ap);
++}
++
++/*
++ * nmbm_log - Print log using OS specific routine
++ * @ni: NMBM instance structure
++ * @level: log level
++ * @fmt: format string
++ */
++static void nmbm_log(struct nmbm_instance *ni, enum nmbm_log_category level,
++                   const char *fmt, ...)
++{
++      va_list ap;
++
++      if (!ni)
++              return;
++
++      if (!ni->lower.logprint || level < ni->log_display_level)
++              return;
++
++      va_start(ap, fmt);
++      ni->lower.logprint(ni->lower.arg, level, fmt, ap);
++      va_end(ap);
++}
++
++/*
++ * nmbm_set_log_level - Set log display level
++ * @ni: NMBM instance structure
++ * @level: log display level
++ */
++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
++                                        enum nmbm_log_category level)
++{
++      enum nmbm_log_category old;
++
++      if (!ni)
++              return __NMBM_LOG_MAX;
++
++      old = ni->log_display_level;
++      ni->log_display_level = level;
++      return old;
++}
++
++/*
++ * nlog_table_creation - Print log of table creation event
++ * @ni: NMBM instance structure
++ * @main_table: whether the table is main info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_creation(struct nmbm_instance *ni, bool main_table,
++                             uint32_t start_ba, uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_info(ni, "%s info table has been written to block %u\n",
++                       main_table ? "Main" : "Backup", start_ba);
++      else
++              nlog_info(ni, "%s info table has been written to block %u-%u\n",
++                       main_table ? "Main" : "Backup", start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*
++ * nlog_table_update - Print log of table update event
++ * @ni: NMBM instance structure
++ * @main_table: whether the table is main info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_update(struct nmbm_instance *ni, bool main_table,
++                           uint32_t start_ba, uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_debug(ni, "%s info table has been updated in block %u\n",
++                        main_table ? "Main" : "Backup", start_ba);
++      else
++              nlog_debug(ni, "%s info table has been updated in block %u-%u\n",
++                        main_table ? "Main" : "Backup", start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*
++ * nlog_table_found - Print log of table found event
++ * @ni: NMBM instance structure
++ * @first_table: whether the table is first found info table
++ * @write_count: write count of the info table
++ * @start_ba: start block address of the table
++ * @end_ba: block address after the end of the table
++ */
++static void nlog_table_found(struct nmbm_instance *ni, bool first_table,
++                          uint32_t write_count, uint32_t start_ba,
++                          uint32_t end_ba)
++{
++      if (start_ba == end_ba - 1)
++              nlog_info(ni, "%s info table with writecount %u found in block %u\n",
++                       first_table ? "First" : "Second", write_count,
++                       start_ba);
++      else
++              nlog_info(ni, "%s info table with writecount %u found in block %u-%u\n",
++                       first_table ? "First" : "Second", write_count,
++                       start_ba, end_ba - 1);
++
++      nmbm_mark_block_color_info_table(ni, start_ba, end_ba - 1);
++}
++
++/*****************************************************************************/
++/* Address conversion functions */
++/*****************************************************************************/
++
++/*
++ * addr2ba - Convert a linear address to block address
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ */
++static uint32_t addr2ba(struct nmbm_instance *ni, uint64_t addr)
++{
++      return addr >> ni->erasesize_shift;
++}
++
++/*
++ * ba2addr - Convert a block address to linear address
++ * @ni: NMBM instance structure
++ * @ba: Block address
++ */
++static uint64_t ba2addr(struct nmbm_instance *ni, uint32_t ba)
++{
++      return (uint64_t)ba << ni->erasesize_shift;
++}
++/*
++ * size2blk - Get minimum required blocks for storing specific size of data
++ * @ni: NMBM instance structure
++ * @size: size for storing
++ */
++static uint32_t size2blk(struct nmbm_instance *ni, uint64_t size)
++{
++      return (size + ni->lower.erasesize - 1) >> ni->erasesize_shift;
++}
++
++/*****************************************************************************/
++/* High level NAND chip APIs */
++/*****************************************************************************/
++
++/*
++ * nmbm_reset_chip - Reset NAND device
++ * @nld: Lower NAND chip structure
++ */
++static void nmbm_reset_chip(struct nmbm_instance *ni)
++{
++      if (ni->lower.reset_chip)
++              ni->lower.reset_chip(ni->lower.arg);
++}
++
++/*
++ * nmbm_read_phys_page - Read page with retry
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be read from
++ * @data: the main data to be read
++ * @oob: the oob data to be read
++ * @mode: mode for processing oob data
++ *
++ * Read a page for at most NMBM_TRY_COUNT times.
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbm_read_phys_page(struct nmbm_instance *ni, uint64_t addr,
++                             void *data, void *oob, enum nmbm_oob_mode mode)
++{
++      int tries, ret;
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.read_page(ni->lower.arg, addr, data, oob, mode);
++              if (ret >= 0)
++                      return ret;
++
++              nmbm_reset_chip(ni);
++      }
++
++      if (ret != -EBADMSG)
++              nlog_err(ni, "Page read failed at address 0x%08llx\n", addr);
++
++      return ret;
++}
++
++/*
++ * nmbm_write_phys_page - Write page with retry
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be written to
++ * @data: the main data to be written
++ * @oob: the oob data to be written
++ * @mode: mode for processing oob data
++ *
++ * Write a page for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbm_write_phys_page(struct nmbm_instance *ni, uint64_t addr,
++                               const void *data, const void *oob,
++                               enum nmbm_oob_mode mode)
++{
++      int tries, ret;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.write_page(ni->lower.arg, addr, data, oob, mode);
++              if (!ret)
++                      return true;
++
++              nmbm_reset_chip(ni);
++      }
++
++      nlog_err(ni, "Page write failed at address 0x%08llx\n", addr);
++
++      return false;
++}
++
++/*
++ * nmbm_erase_phys_block - Erase a block with retry
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ *
++ * Erase a block for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbm_erase_phys_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      int tries, ret;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      for (tries = 0; tries < NMBM_TRY_COUNT; tries++) {
++              ret = ni->lower.erase_block(ni->lower.arg, addr);
++              if (!ret)
++                      return true;
++
++              nmbm_reset_chip(ni);
++      }
++
++      nlog_err(ni, "Block erasure failed at address 0x%08llx\n", addr);
++
++      return false;
++}
++
++/*
++ * nmbm_check_bad_phys_block - Check whether a block is marked bad in OOB
++ * @ni: NMBM instance structure
++ * @ba: block address
++ */
++static bool nmbm_check_bad_phys_block(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr = ba2addr(ni, ba);
++      int ret;
++
++      if (ni->lower.is_bad_block)
++              return ni->lower.is_bad_block(ni->lower.arg, addr);
++
++      /* Treat ECC error as read success */
++      ret = nmbm_read_phys_page(ni, addr, NULL,
++                                ni->page_cache + ni->lower.writesize,
++                                NMBM_MODE_RAW);
++      if (ret < 0 && ret != -EBADMSG)
++              return true;
++
++      return ni->page_cache[ni->lower.writesize] != 0xff;
++}
++
++/*
++ * nmbm_mark_phys_bad_block - Mark a block bad
++ * @ni: NMBM instance structure
++ * @addr: Linear address
++ */
++static int nmbm_mark_phys_bad_block(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr = ba2addr(ni, ba);
++      enum nmbm_log_category level;
++      uint32_t off;
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_err(ni, "%s called with NMBM_F_READ_ONLY set\n", addr);
++              return false;
++      }
++
++      nlog_info(ni, "Block %u [0x%08llx] will be marked bad\n", ba, addr);
++
++      if (ni->lower.mark_bad_block)
++              return ni->lower.mark_bad_block(ni->lower.arg, addr);
++
++      /* Whole page set to 0x00 */
++      memset(ni->page_cache, 0, ni->rawpage_size);
++
++      /* Write to all pages within this block, disable all errors */
++      level = nmbm_set_log_level(ni, __NMBM_LOG_MAX);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              nmbm_write_phys_page(ni, addr + off, ni->page_cache,
++                                   ni->page_cache + ni->lower.writesize,
++                                   NMBM_MODE_RAW);
++      }
++
++      nmbm_set_log_level(ni, level);
++
++      return 0;
++}
++
++/*****************************************************************************/
++/* NMBM related functions */
++/*****************************************************************************/
++
++/*
++ * nmbm_check_header - Check whether a NMBM structure is valid
++ * @data: pointer to a NMBM structure with a NMBM header at beginning
++ * @size: Size of the buffer pointed by @header
++ *
++ * The size of the NMBM structure may be larger than NMBM header,
++ * e.g. block mapping table and block state table.
++ */
++static bool nmbm_check_header(const void *data, uint32_t size)
++{
++      const struct nmbm_header *header = data;
++      struct nmbm_header nhdr;
++      uint32_t new_checksum;
++
++      /*
++       * Make sure expected structure size is equal or smaller than
++       * buffer size.
++       */
++      if (header->size > size)
++              return false;
++
++      memcpy(&nhdr, data, sizeof(nhdr));
++
++      nhdr.checksum = 0;
++      new_checksum = nmbm_crc32(0, &nhdr, sizeof(nhdr));
++      if (header->size > sizeof(nhdr))
++              new_checksum = nmbm_crc32(new_checksum,
++                      (const uint8_t *)data + sizeof(nhdr),
++                      header->size - sizeof(nhdr));
++
++      if (header->checksum != new_checksum)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_update_checksum - Update checksum of a NMBM structure
++ * @header: pointer to a NMBM structure with a NMBM header at beginning
++ *
++ * The size of the NMBM structure must be specified by @header->size
++ */
++static void nmbm_update_checksum(struct nmbm_header *header)
++{
++      header->checksum = 0;
++      header->checksum = nmbm_crc32(0, header, header->size);
++}
++
++/*
++ * nmbm_get_spare_block_count - Calculate number of blocks should be reserved
++ * @block_count: number of blocks of data
++ *
++ * Calculate number of blocks should be reserved for data
++ */
++static uint32_t nmbm_get_spare_block_count(uint32_t block_count)
++{
++      uint32_t val;
++
++      val = (block_count + NMBM_SPARE_BLOCK_DIV / 2) / NMBM_SPARE_BLOCK_DIV;
++      val *= NMBM_SPARE_BLOCK_MULTI;
++
++      if (val < NMBM_SPARE_BLOCK_MIN)
++              val = NMBM_SPARE_BLOCK_MIN;
++
++      return val;
++}
++
++/*
++ * nmbm_get_block_state_raw - Get state of a block from raw block state table
++ * @block_state: pointer to raw block state table (bitmap)
++ * @ba: block address
++ */
++static uint32_t nmbm_get_block_state_raw(nmbm_bitmap_t *block_state,
++                                       uint32_t ba)
++{
++      uint32_t unit, shift;
++
++      unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
++      shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
++
++      return (block_state[unit] >> shift) & BLOCK_ST_MASK;
++}
++
++/*
++ * nmbm_get_block_state - Get state of a block from block state table
++ * @ni: NMBM instance structure
++ * @ba: block address
++ */
++static uint32_t nmbm_get_block_state(struct nmbm_instance *ni, uint32_t ba)
++{
++      return nmbm_get_block_state_raw(ni->block_state, ba);
++}
++
++/*
++ * nmbm_set_block_state - Set state of a block to block state table
++ * @ni: NMBM instance structure
++ * @ba: block address
++ * @state: block state
++ *
++ * Set state of a block. If the block state changed, ni->block_state_changed
++ * will be increased.
++ */
++static bool nmbm_set_block_state(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t state)
++{
++      uint32_t unit, shift, orig;
++      nmbm_bitmap_t uv;
++
++      unit = ba / NMBM_BITMAP_BLOCKS_PER_UNIT;
++      shift = (ba % NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_BITS_PER_BLOCK;
++
++      orig = (ni->block_state[unit] >> shift) & BLOCK_ST_MASK;
++      state &= BLOCK_ST_MASK;
++
++      uv = ni->block_state[unit] & (~(BLOCK_ST_MASK << shift));
++      uv |= state << shift;
++      ni->block_state[unit] = uv;
++
++      if (state == BLOCK_ST_BAD)
++              nmbm_mark_block_color_bad(ni, ba);
++
++      if (orig != state) {
++              ni->block_state_changed++;
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk_asc - Skip specified number of good blocks, ascending addr.
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: highest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk_asc(struct nmbm_instance *ni, uint32_t ba,
++                              uint32_t *nba, uint32_t count,
++                              uint32_t limit)
++{
++      int32_t nblock = count;
++
++      if (limit >= ni->block_count)
++              limit = ni->block_count - 1;
++
++      while (ba < limit) {
++              if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
++                      nblock--;
++
++              if (nblock < 0) {
++                      *nba = ba;
++                      return true;
++              }
++
++              ba++;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk_desc - Skip specified number of good blocks, descending addr
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: lowest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk_desc(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t *nba, uint32_t count, uint32_t limit)
++{
++      int32_t nblock = count;
++
++      if (limit >= ni->block_count)
++              limit = ni->block_count - 1;
++
++      while (ba > limit) {
++              if (nmbm_get_block_state(ni, ba) == BLOCK_ST_GOOD)
++                      nblock--;
++
++              if (nblock < 0) {
++                      *nba = ba;
++                      return true;
++              }
++
++              ba--;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_block_walk - Skip specified number of good blocks from curr. block addr
++ * @ni: NMBM instance structure
++ * @ascending: whether to walk ascending
++ * @ba: start physical block address
++ * @nba: return physical block address after walk
++ * @count: number of good blocks to be skipped
++ * @limit: highest/lowest block address allowed for walking
++ *
++ * Start from @ba, skipping any bad blocks, counting @count good blocks, and
++ * return the next good block address.
++ *
++ * If no enough good blocks counted while @limit reached, false will be returned.
++ *
++ * If @count == 0, nearest good block address will be returned.
++ * @limit can be set to negative if no limit required.
++ * @limit is not counted in walking.
++ */
++static bool nmbm_block_walk(struct nmbm_instance *ni, bool ascending,
++                          uint32_t ba, uint32_t *nba, int32_t count,
++                          int32_t limit)
++{
++      if (ascending)
++              return nmbm_block_walk_asc(ni, ba, nba, count, limit);
++
++      return nmbm_block_walk_desc(ni, ba, nba, count, limit);
++}
++
++/*
++ * nmbm_scan_badblocks - Scan and record all bad blocks
++ * @ni: NMBM instance structure
++ *
++ * Scan the entire lower NAND chip and record all bad blocks in to block state
++ * table.
++ */
++static void nmbm_scan_badblocks(struct nmbm_instance *ni)
++{
++      uint32_t ba;
++
++      for (ba = 0; ba < ni->block_count; ba++) {
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      nlog_info(ni, "Bad block %u [0x%08llx]\n", ba,
++                               ba2addr(ni, ba));
++              }
++      }
++}
++
++/*
++ * nmbm_build_mapping_table - Build initial block mapping table
++ * @ni: NMBM instance structure
++ *
++ * The initial mapping table will be compatible with the stratage of
++ * factory production.
++ */
++static void nmbm_build_mapping_table(struct nmbm_instance *ni)
++{
++      uint32_t pb, lb;
++
++      for (pb = 0, lb = 0; pb < ni->mgmt_start_ba; pb++) {
++              if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++                      continue;
++
++              /* Always map to the next good block */
++              ni->block_mapping[lb++] = pb;
++      }
++
++      ni->data_block_count = lb;
++
++      /* Unusable/Management blocks */
++      for (pb = lb; pb < ni->block_count; pb++)
++              ni->block_mapping[pb] = -1;
++}
++
++/*
++ * nmbm_erase_block_and_check - Erase a block and check its usability
++ * @ni: NMBM instance structure
++ * @ba: block address to be erased
++ *
++ * Erase a block anc check its usability
++ *
++ * Return true if the block is usable, false if erasure failure or the block
++ * has too many bitflips.
++ */
++static bool nmbm_erase_block_and_check(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint64_t addr, off;
++      bool success;
++      int ret;
++
++      success = nmbm_erase_phys_block(ni, ba2addr(ni, ba));
++      if (!success)
++              return false;
++
++      if (!(ni->lower.flags & NMBM_F_EMPTY_PAGE_ECC_OK))
++              return true;
++
++      /* Check every page to make sure there aren't too many bitflips */
++
++      addr = ba2addr(ni, ba);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              WATCHDOG_RESET();
++
++              ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
++                                        NMBM_MODE_PLACE_OOB);
++              if (ret == -EBADMSG) {
++                      /*
++                       * NMBM_F_EMPTY_PAGE_ECC_OK means the empty page is
++                       * still protected by ECC. So reading pages with ECC
++                       * enabled and -EBADMSG means there are too many
++                       * bitflips that can't be recovered, and the block
++                       * containing the page should be marked bad.
++                       */
++                      nlog_err(ni,
++                               "Too many bitflips in empty page at 0x%llx\n",
++                               addr + off);
++                      return false;
++              }
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_erase_range - Erase a range of blocks
++ * @ni: NMBM instance structure
++ * @ba: block address where the erasure will start
++ * @limit: top block address allowed for erasure
++ *
++ * Erase blocks within the specific range. Newly-found bad blocks will be
++ * marked.
++ *
++ * @limit is not counted into the allowed erasure address.
++ */
++static void nmbm_erase_range(struct nmbm_instance *ni, uint32_t ba,
++                           uint32_t limit)
++{
++      bool success;
++
++      while (ba < limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (success)
++                      goto next_block;
++
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++}
++
++/*
++ * nmbm_write_repeated_data - Write critical data to a block with retry
++ * @ni: NMBM instance structure
++ * @ba: block address where the data will be written to
++ * @data: the data to be written
++ * @size: size of the data
++ *
++ * Write data to every page of the block. Success only if all pages within
++ * this block have been successfully written.
++ *
++ * Make sure data size is not bigger than one page.
++ *
++ * This function will write and verify every page for at most
++ * NMBM_TRY_COUNT times.
++ */
++static bool nmbm_write_repeated_data(struct nmbm_instance *ni, uint32_t ba,
++                                   const void *data, uint32_t size)
++{
++      uint64_t addr, off;
++      bool success;
++      int ret;
++
++      if (size > ni->lower.writesize)
++              return false;
++
++      addr = ba2addr(ni, ba);
++
++      for (off = 0; off < ni->lower.erasesize; off += ni->lower.writesize) {
++              WATCHDOG_RESET();
++
++              /* Prepare page data. fill 0xff to unused region */
++              memcpy(ni->page_cache, data, size);
++              memset(ni->page_cache + size, 0xff, ni->rawpage_size - size);
++
++              success = nmbm_write_phys_page(ni, addr + off, ni->page_cache,
++                                             NULL, NMBM_MODE_PLACE_OOB);
++              if (!success)
++                      return false;
++
++              /* Verify the data just written. ECC error indicates failure */
++              ret = nmbm_read_phys_page(ni, addr + off, ni->page_cache, NULL,
++                                        NMBM_MODE_PLACE_OOB);
++              if (ret < 0)
++                      return false;
++
++              if (memcmp(ni->page_cache, data, size))
++                      return false;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_write_signature - Write signature to NAND chip
++ * @ni: NMBM instance structure
++ * @limit: top block address allowed for writing
++ * @signature: the signature to be written
++ * @signature_ba: the actual block address where signature is written to
++ *
++ * Write signature within a specific range, from chip bottom to limit.
++ * At most one block will be written.
++ *
++ * @limit is not counted into the allowed write address.
++ */
++static bool nmbm_write_signature(struct nmbm_instance *ni, uint32_t limit,
++                               const struct nmbm_signature *signature,
++                               uint32_t *signature_ba)
++{
++      uint32_t ba = ni->block_count - 1;
++      bool success;
++
++      while (ba > limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (!success)
++                      goto skip_bad_block;
++
++              success = nmbm_write_repeated_data(ni, ba, signature,
++                                                 sizeof(*signature));
++              if (success) {
++                      *signature_ba = ba;
++                      return true;
++              }
++
++      skip_bad_block:
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba--;
++      };
++
++      return false;
++}
++
++/*
++ * nmbn_read_data - Read data
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be read from
++ * @data: the data to be read
++ * @size: the size of data
++ *
++ * Read data range.
++ * Every page will be tried for at most NMBM_TRY_COUNT times.
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbn_read_data(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        uint32_t size)
++{
++      uint64_t off = addr;
++      uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize, leading;
++      int ret;
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_read_phys_page(ni, off - leading, ptr, NULL,
++                                                NMBM_MODE_PLACE_OOB);
++                      if (ret < 0)
++                              return ret;
++              } else {
++                      ret = nmbm_read_phys_page(ni, off - leading,
++                                                ni->page_cache, NULL,
++                                                NMBM_MODE_PLACE_OOB);
++                      if (ret < 0)
++                              return ret;
++
++                      memcpy(ptr, ni->page_cache + leading, chunksize);
++              }
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      return 0;
++}
++
++/*
++ * nmbn_write_verify_data - Write data with validation
++ * @ni: NMBM instance structure
++ * @addr: linear address where the data will be written to
++ * @data: the data to be written
++ * @size: the size of data
++ *
++ * Write data and verify.
++ * Every page will be tried for at most NMBM_TRY_COUNT times.
++ */
++static bool nmbn_write_verify_data(struct nmbm_instance *ni, uint64_t addr,
++                                 const void *data, uint32_t size)
++{
++      uint64_t off = addr;
++      const uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize, leading;
++      bool success;
++      int ret;
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              /* Prepare page data. fill 0xff to unused region */
++              memset(ni->page_cache, 0xff, ni->rawpage_size);
++              memcpy(ni->page_cache + leading, ptr, chunksize);
++
++              success = nmbm_write_phys_page(ni, off - leading,
++                                             ni->page_cache, NULL,
++                                             NMBM_MODE_PLACE_OOB);
++              if (!success)
++                      return false;
++
++              /* Verify the data just written. ECC error indicates failure */
++              ret = nmbm_read_phys_page(ni, off - leading, ni->page_cache,
++                                        NULL, NMBM_MODE_PLACE_OOB);
++              if (ret < 0)
++                      return false;
++
++              if (memcmp(ni->page_cache + leading, ptr, chunksize))
++                      return false;
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_write_mgmt_range - Write management data into NAND within a range
++ * @ni: NMBM instance structure
++ * @addr: preferred start block address for writing
++ * @limit: highest block address allowed for writing
++ * @data: the data to be written
++ * @size: the size of data
++ * @actual_start_ba: actual start block address of data
++ * @actual_end_ba: block address after the end of data
++ *
++ * @limit is not counted into the allowed write address.
++ */
++static bool nmbm_write_mgmt_range(struct nmbm_instance *ni, uint32_t ba,
++                                uint32_t limit, const void *data,
++                                uint32_t size, uint32_t *actual_start_ba,
++                                uint32_t *actual_end_ba)
++{
++      const uint8_t *ptr = data;
++      uint32_t sizeremain = size, chunksize;
++      bool success;
++
++      while (sizeremain && ba < limit) {
++              WATCHDOG_RESET();
++
++              chunksize = sizeremain;
++              if (chunksize > ni->lower.erasesize)
++                      chunksize = ni->lower.erasesize;
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              /* Insurance to detect unexpected bad block marked by user */
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              success = nmbm_erase_block_and_check(ni, ba);
++              if (!success)
++                      goto skip_bad_block;
++
++              success = nmbn_write_verify_data(ni, ba2addr(ni, ba), ptr,
++                                               chunksize);
++              if (!success)
++                      goto skip_bad_block;
++
++              if (sizeremain == size)
++                      *actual_start_ba = ba;
++
++              ptr += chunksize;
++              sizeremain -= chunksize;
++
++              goto next_block;
++
++      skip_bad_block:
++              nmbm_mark_phys_bad_block(ni, ba);
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++
++      if (sizeremain)
++              return false;
++
++      *actual_end_ba = ba;
++
++      return true;
++}
++
++/*
++ * nmbm_generate_info_table_cache - Generate info table cache data
++ * @ni: NMBM instance structure
++ *
++ * Generate info table cache data to be written into flash.
++ */
++static bool nmbm_generate_info_table_cache(struct nmbm_instance *ni)
++{
++      bool changed = false;
++
++      memset(ni->info_table_cache, 0xff, ni->info_table_size);
++
++      memcpy(ni->info_table_cache + ni->info_table.state_table_off,
++             ni->block_state, ni->state_table_size);
++
++      memcpy(ni->info_table_cache + ni->info_table.mapping_table_off,
++              ni->block_mapping, ni->mapping_table_size);
++
++      ni->info_table.header.magic = NMBM_MAGIC_INFO_TABLE;
++      ni->info_table.header.version = NMBM_VER;
++      ni->info_table.header.size = ni->info_table_size;
++
++      if (ni->block_state_changed || ni->block_mapping_changed) {
++              ni->info_table.write_count++;
++              changed = true;
++      }
++
++      memcpy(ni->info_table_cache, &ni->info_table, sizeof(ni->info_table));
++
++      nmbm_update_checksum((struct nmbm_header *)ni->info_table_cache);
++
++      return changed;
++}
++
++/*
++ * nmbm_write_info_table - Write info table into NAND within a range
++ * @ni: NMBM instance structure
++ * @ba: preferred start block address for writing
++ * @limit: highest block address allowed for writing
++ * @actual_start_ba: actual start block address of info table
++ * @actual_end_ba: block address after the end of info table
++ *
++ * @limit is counted into the allowed write address.
++ */
++static bool nmbm_write_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                uint32_t limit, uint32_t *actual_start_ba,
++                                uint32_t *actual_end_ba)
++{
++      return nmbm_write_mgmt_range(ni, ba, limit, ni->info_table_cache,
++                                   ni->info_table_size, actual_start_ba,
++                                   actual_end_ba);
++}
++
++/*
++ * nmbm_mark_tables_clean - Mark info table `clean'
++ * @ni: NMBM instance structure
++ */
++static void nmbm_mark_tables_clean(struct nmbm_instance *ni)
++{
++      ni->block_state_changed = 0;
++      ni->block_mapping_changed = 0;
++}
++
++/*
++ * nmbm_try_reserve_blocks - Reserve blocks with compromisation
++ * @ni: NMBM instance structure
++ * @ba: start physical block address
++ * @nba: return physical block address after reservation
++ * @count: number of good blocks to be skipped
++ * @min_count: minimum number of good blocks to be skipped
++ * @limit: highest/lowest block address allowed for walking
++ *
++ * Reserve specific blocks. If failed, try to reserve as many as possible.
++ */
++static bool nmbm_try_reserve_blocks(struct nmbm_instance *ni, uint32_t ba,
++                                  uint32_t *nba, uint32_t count,
++                                  int32_t min_count, int32_t limit)
++{
++      int32_t nblocks = count;
++      bool success;
++
++      while (nblocks >= min_count) {
++              success = nmbm_block_walk(ni, true, ba, nba, nblocks, limit);
++              if (success)
++                      return true;
++
++              nblocks--;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_rebuild_info_table - Build main & backup info table from scratch
++ * @ni: NMBM instance structure
++ * @allow_no_gap: allow no spare blocks between two tables
++ */
++static bool nmbm_rebuild_info_table(struct nmbm_instance *ni)
++{
++      uint32_t table_start_ba, table_end_ba, next_start_ba;
++      uint32_t main_table_end_ba;
++      bool success;
++
++      /* Set initial value */
++      ni->main_table_ba = 0;
++      ni->backup_table_ba = 0;
++      ni->mapping_blocks_ba = ni->mapping_blocks_top_ba;
++
++      /* Write main table */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /* Failed to write main table, data will be lost */
++              nlog_emerg(ni, "Unable to write at least one info table!\n");
++              nlog_emerg(ni, "Please save your data before power off!\n");
++              ni->protected = 1;
++              return false;
++      }
++
++      /* Main info table is successfully written, record its offset */
++      ni->main_table_ba = table_start_ba;
++      main_table_end_ba = table_end_ba;
++
++      /* Adjust mapping_blocks_ba */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_creation(ni, true, table_start_ba, table_end_ba);
++
++      /* Reserve spare blocks for main info table. */
++      success = nmbm_try_reserve_blocks(ni, table_end_ba,
++                                        &next_start_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        size2blk(ni, ni->info_table_size));
++      if (!success) {
++              /* There is no spare block. */
++              nlog_debug(ni, "No room for backup info table\n");
++              return true;
++      }
++
++      /* Write backup info table. */
++      success = nmbm_write_info_table(ni, next_start_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /* There is no enough blocks for backup table. */
++              nlog_debug(ni, "No room for backup info table\n");
++              return true;
++      }
++
++      /* Backup table is successfully written, record its offset */
++      ni->backup_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      /* Erase spare blocks of main table to clean possible interference data */
++      nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
++
++      nlog_table_creation(ni, false, table_start_ba, table_end_ba);
++
++      return true;
++}
++
++/*
++ * nmbm_rescue_single_info_table - Rescue when there is only one info table
++ * @ni: NMBM instance structure
++ *
++ * This function is called when there is only one info table exists.
++ * This function may fail if we can't write new info table
++ */
++static bool nmbm_rescue_single_info_table(struct nmbm_instance *ni)
++{
++      uint32_t table_start_ba, table_end_ba, write_ba;
++      bool success;
++
++      /* Try to write new info table in front of existing table */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      ni->main_table_ba,
++                                      &table_start_ba,
++                                      &table_end_ba);
++      if (success) {
++              /*
++               * New table becomes the main table, existing table becomes
++               * the backup table.
++               */
++              ni->backup_table_ba = ni->main_table_ba;
++              ni->main_table_ba = table_start_ba;
++
++              nmbm_mark_tables_clean(ni);
++
++              /* Erase spare blocks of main table to clean possible interference data */
++              nmbm_erase_range(ni, table_end_ba, ni->backup_table_ba);
++
++              nlog_table_creation(ni, true, table_start_ba, table_end_ba);
++
++              return true;
++      }
++
++      /* Try to reserve spare blocks for existing table */
++      success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        size2blk(ni, ni->info_table_size));
++      if (!success) {
++              nlog_warn(ni, "Failed to rescue single info table\n");
++              return false;
++      }
++
++      /* Try to write new info table next to the existing table */
++      while (write_ba >= ni->mapping_blocks_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &table_start_ba,
++                                              &table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (success) {
++              /* Erase spare blocks of main table to clean possible interference data */
++              nmbm_erase_range(ni, ni->mapping_blocks_ba, table_start_ba);
++
++              /* New table becomes the backup table */
++              ni->backup_table_ba = table_start_ba;
++              ni->mapping_blocks_ba = table_end_ba;
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_table_creation(ni, false, table_start_ba, table_end_ba);
++
++              return true;
++      }
++
++      nlog_warn(ni, "Failed to rescue single info table\n");
++      return false;
++}
++
++/*
++ * nmbm_update_single_info_table - Update specific one info table
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_update_single_info_table(struct nmbm_instance *ni,
++                                        bool update_main_table)
++{
++      uint32_t write_start_ba, write_limit, table_start_ba, table_end_ba;
++      bool success;
++
++      /* Determine the write range */
++      if (update_main_table) {
++              write_start_ba = ni->main_table_ba;
++              write_limit = ni->backup_table_ba;
++      } else {
++              write_start_ba = ni->backup_table_ba;
++              write_limit = ni->mapping_blocks_top_ba;
++      }
++
++      nmbm_mark_block_color_mgmt(ni, write_start_ba, write_limit - 1);
++
++      success = nmbm_write_info_table(ni, write_start_ba, write_limit,
++                                      &table_start_ba, &table_end_ba);
++      if (success) {
++              if (update_main_table) {
++                      ni->main_table_ba = table_start_ba;
++              } else {
++                      ni->backup_table_ba = table_start_ba;
++                      ni->mapping_blocks_ba = table_end_ba;
++              }
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_table_update(ni, update_main_table, table_start_ba,
++                               table_end_ba);
++
++              return true;
++      }
++
++      if (update_main_table) {
++              /*
++               * If failed to update main table, make backup table the new
++               * main table, and call nmbm_rescue_single_info_table()
++               */
++              nlog_warn(ni, "Unable to update %s info table\n",
++                       update_main_table ? "Main" : "Backup");
++
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return nmbm_rescue_single_info_table(ni);
++      }
++
++      /* Only one table left */
++      ni->mapping_blocks_ba = ni->backup_table_ba;
++      ni->backup_table_ba = 0;
++
++      return false;
++}
++
++/*
++ * nmbm_rescue_main_info_table - Rescue when failed to write main info table
++ * @ni: NMBM instance structure
++ *
++ * This function is called when main info table failed to be written, and
++ *    backup info table exists.
++ */
++static bool nmbm_rescue_main_info_table(struct nmbm_instance *ni)
++{
++      uint32_t tmp_table_start_ba, tmp_table_end_ba, main_table_start_ba;
++      uint32_t main_table_end_ba, write_ba;
++      uint32_t info_table_erasesize = size2blk(ni, ni->info_table_size);
++      bool success;
++
++      /* Try to reserve spare blocks for existing backup info table */
++      success = nmbm_try_reserve_blocks(ni, ni->mapping_blocks_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        info_table_erasesize);
++      if (!success) {
++              /* There is no spare block. Backup info table becomes the main table. */
++              nlog_err(ni, "No room for temporary info table\n");
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Try to write temporary info table into spare unmapped blocks */
++      while (write_ba >= ni->mapping_blocks_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &tmp_table_start_ba,
++                                              &tmp_table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (!success) {
++              /* Backup info table becomes the main table */
++              nlog_err(ni, "Failed to update main info table\n");
++              ni->main_table_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = tmp_table_end_ba;
++
++      nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
++                                 tmp_table_end_ba - 1);
++
++      /*
++       * Now write main info table at the beginning of management area.
++       * This operation will generally destroy the original backup info
++       * table.
++       */
++      success = nmbm_write_info_table(ni, ni->mgmt_start_ba,
++                                      tmp_table_start_ba,
++                                      &main_table_start_ba,
++                                      &main_table_end_ba);
++      if (!success) {
++              /* Temporary info table becomes the main table */
++              ni->main_table_ba = tmp_table_start_ba;
++              ni->backup_table_ba = 0;
++
++              nmbm_mark_tables_clean(ni);
++
++              nlog_err(ni, "Failed to update main info table\n");
++              nmbm_mark_block_color_info_table(ni, tmp_table_start_ba,
++                                               tmp_table_end_ba - 1);
++
++              return true;
++      }
++
++      /* Main info table has been successfully written, record its offset */
++      ni->main_table_ba = main_table_start_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_creation(ni, true, main_table_start_ba, main_table_end_ba);
++
++      /*
++       * Temporary info table becomes the new backup info table if it's
++       * not overwritten.
++       */
++      if (main_table_end_ba <= tmp_table_start_ba) {
++              ni->backup_table_ba = tmp_table_start_ba;
++
++              nlog_table_creation(ni, false, tmp_table_start_ba,
++                                 tmp_table_end_ba);
++
++              return true;
++      }
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = main_table_end_ba;
++
++      /* Try to reserve spare blocks for new main info table */
++      success = nmbm_try_reserve_blocks(ni, main_table_end_ba, &write_ba,
++                                        ni->info_table_spare_blocks, 0,
++                                        ni->mapping_blocks_top_ba -
++                                        info_table_erasesize);
++      if (!success) {
++              /* There is no spare block. Only main table exists. */
++              nlog_err(ni, "No room for backup info table\n");
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Write new backup info table. */
++      while (write_ba >= main_table_end_ba) {
++              WATCHDOG_RESET();
++
++              success = nmbm_write_info_table(ni, write_ba,
++                                              ni->mapping_blocks_top_ba,
++                                              &tmp_table_start_ba,
++                                              &tmp_table_end_ba);
++              if (success)
++                      break;
++
++              write_ba--;
++      }
++
++      if (!success) {
++              nlog_err(ni, "No room for backup info table\n");
++              ni->backup_table_ba = 0;
++              return true;
++      }
++
++      /* Backup info table has been successfully written, record its offset */
++      ni->backup_table_ba = tmp_table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = tmp_table_end_ba;
++
++      /* Erase spare blocks of main table to clean possible interference data */
++      nmbm_erase_range(ni, main_table_end_ba, ni->backup_table_ba);
++
++      nlog_table_creation(ni, false, tmp_table_start_ba, tmp_table_end_ba);
++
++      return true;
++}
++
++/*
++ * nmbm_update_info_table_once - Update info table once
++ * @ni: NMBM instance structure
++ * @force: force update
++ *
++ * Update both main and backup info table. Return true if at least one info
++ * table has been successfully written.
++ * This function only try to update info table once regard less of the result.
++ */
++static bool nmbm_update_info_table_once(struct nmbm_instance *ni, bool force)
++{
++      uint32_t table_start_ba, table_end_ba;
++      uint32_t main_table_limit;
++      bool success;
++
++      /* Do nothing if there is no change */
++      if (!nmbm_generate_info_table_cache(ni) && !force)
++              return true;
++
++      /* Check whether both two tables exist */
++      if (!ni->backup_table_ba) {
++              main_table_limit = ni->mapping_blocks_top_ba;
++              goto write_main_table;
++      }
++
++      nmbm_mark_block_color_mgmt(ni, ni->backup_table_ba,
++                                 ni->mapping_blocks_ba - 1);
++
++      /*
++       * Write backup info table in its current range.
++       * Note that limit is set to mapping_blocks_top_off to provide as many
++       * spare blocks as possible for the backup table. If at last
++       * unmapped blocks are used by backup table, mapping_blocks_off will
++       * be adjusted.
++       */
++      success = nmbm_write_info_table(ni, ni->backup_table_ba,
++                                      ni->mapping_blocks_top_ba,
++                                      &table_start_ba, &table_end_ba);
++      if (!success) {
++              /*
++               * There is nothing to do if failed to write backup table.
++               * Write the main table now.
++               */
++              nlog_err(ni, "No room for backup table\n");
++              ni->mapping_blocks_ba = ni->backup_table_ba;
++              ni->backup_table_ba = 0;
++              main_table_limit = ni->mapping_blocks_top_ba;
++              goto write_main_table;
++      }
++
++      /* Backup table is successfully written, record its offset */
++      ni->backup_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      /* The normal limit of main table */
++      main_table_limit = ni->backup_table_ba;
++
++      nlog_table_update(ni, false, table_start_ba, table_end_ba);
++
++write_main_table:
++      if (!ni->main_table_ba)
++              goto rebuild_tables;
++
++      if (!ni->backup_table_ba)
++              nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                         ni->mapping_blocks_ba - 1);
++      else
++              nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                         ni->backup_table_ba - 1);
++
++      /* Write main info table in its current range */
++      success = nmbm_write_info_table(ni, ni->main_table_ba,
++                                      main_table_limit, &table_start_ba,
++                                      &table_end_ba);
++      if (!success) {
++              /* If failed to write main table, go rescue procedure */
++              if (!ni->backup_table_ba)
++                      goto rebuild_tables;
++
++              return nmbm_rescue_main_info_table(ni);
++      }
++
++      /* Main info table is successfully written, record its offset */
++      ni->main_table_ba = table_start_ba;
++
++      /* Adjust mapping_blocks_off */
++      if (!ni->backup_table_ba)
++              ni->mapping_blocks_ba = table_end_ba;
++
++      nmbm_mark_tables_clean(ni);
++
++      nlog_table_update(ni, true, table_start_ba, table_end_ba);
++
++      return true;
++
++rebuild_tables:
++      return nmbm_rebuild_info_table(ni);
++}
++
++/*
++ * nmbm_update_info_table - Update info table
++ * @ni: NMBM instance structure
++ *
++ * Update both main and backup info table. Return true if at least one table
++ * has been successfully written.
++ * This function will try to update info table repeatedly until no new bad
++ * block found during updating.
++ */
++static bool nmbm_update_info_table(struct nmbm_instance *ni)
++{
++      bool success;
++
++      if (ni->protected)
++              return true;
++
++      while (ni->block_state_changed || ni->block_mapping_changed) {
++              success = nmbm_update_info_table_once(ni, false);
++              if (!success) {
++                      nlog_err(ni, "Failed to update info table\n");
++                      return false;
++              }
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_map_block - Map a bad block to a unused spare block
++ * @ni: NMBM instance structure
++ * @lb: logic block addr to map
++ */
++static bool nmbm_map_block(struct nmbm_instance *ni, uint32_t lb)
++{
++      uint32_t pb;
++      bool success;
++
++      if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
++              nlog_warn(ni, "No spare unmapped blocks.\n");
++              return false;
++      }
++
++      success = nmbm_block_walk(ni, false, ni->mapping_blocks_top_ba, &pb, 0,
++                                ni->mapping_blocks_ba);
++      if (!success) {
++              nlog_warn(ni, "No spare unmapped blocks.\n");
++              nmbm_update_info_table(ni);
++              ni->mapping_blocks_top_ba = ni->mapping_blocks_ba;
++              return false;
++      }
++
++      ni->block_mapping[lb] = pb;
++      ni->mapping_blocks_top_ba--;
++      ni->block_mapping_changed++;
++
++      nlog_info(ni, "Logic block %u mapped to physical blcok %u\n", lb, pb);
++      nmbm_mark_block_color_mapped(ni, pb);
++
++      return true;
++}
++
++/*
++ * nmbm_create_info_table - Create info table(s)
++ * @ni: NMBM instance structure
++ *
++ * This function assumes that the chip has no existing info table(s)
++ */
++static bool nmbm_create_info_table(struct nmbm_instance *ni)
++{
++      uint32_t lb;
++      bool success;
++
++      /* Set initial mapping_blocks_top_off  */
++      success = nmbm_block_walk(ni, false, ni->signature_ba,
++                                &ni->mapping_blocks_top_ba, 1,
++                                ni->mgmt_start_ba);
++      if (!success) {
++              nlog_err(ni, "No room for spare blocks\n");
++              return false;
++      }
++
++      /* Generate info table cache */
++      nmbm_generate_info_table_cache(ni);
++
++      /* Write info table */
++      success = nmbm_rebuild_info_table(ni);
++      if (!success) {
++              nlog_err(ni, "Failed to build info tables\n");
++              return false;
++      }
++
++      /* Remap bad block(s) at end of data area */
++      for (lb = ni->data_block_count; lb < ni->mgmt_start_ba; lb++) {
++              success = nmbm_map_block(ni, lb);
++              if (!success)
++                      break;
++
++              ni->data_block_count++;
++      }
++
++      /* If state table and/or mapping table changed, update info table. */
++      success = nmbm_update_info_table(ni);
++      if (!success)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_create_new - Create NMBM on a new chip
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_create_new(struct nmbm_instance *ni)
++{
++      bool success;
++
++      /* Determine the boundary of management blocks */
++      ni->mgmt_start_ba = ni->block_count * (NMBM_MGMT_DIV - ni->lower.max_ratio) / NMBM_MGMT_DIV;
++
++      if (ni->lower.max_reserved_blocks && ni->block_count - ni->mgmt_start_ba > ni->lower.max_reserved_blocks)
++              ni->mgmt_start_ba = ni->block_count - ni->lower.max_reserved_blocks;
++
++      nlog_info(ni, "NMBM management region starts at block %u [0x%08llx]\n",
++                ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
++      nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba, ni->block_count - 1);
++
++      /* Fill block state table & mapping table */
++      nmbm_scan_badblocks(ni);
++      nmbm_build_mapping_table(ni);
++
++      /* Write signature */
++      ni->signature.header.magic = NMBM_MAGIC_SIGNATURE;
++      ni->signature.header.version = NMBM_VER;
++      ni->signature.header.size = sizeof(ni->signature);
++      ni->signature.nand_size = ni->lower.size;
++      ni->signature.block_size = ni->lower.erasesize;
++      ni->signature.page_size = ni->lower.writesize;
++      ni->signature.spare_size = ni->lower.oobsize;
++      ni->signature.mgmt_start_pb = ni->mgmt_start_ba;
++      ni->signature.max_try_count = NMBM_TRY_COUNT;
++      nmbm_update_checksum(&ni->signature.header);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_info(ni, "NMBM has been initialized in read-only mode\n");
++              return true;
++      }
++
++      success = nmbm_write_signature(ni, ni->mgmt_start_ba,
++                                     &ni->signature, &ni->signature_ba);
++      if (!success) {
++              nlog_err(ni, "Failed to write signature to a proper offset\n");
++              return false;
++      }
++
++      nlog_info(ni, "Signature has been written to block %u [0x%08llx]\n",
++               ni->signature_ba, ba2addr(ni, ni->signature_ba));
++      nmbm_mark_block_color_signature(ni, ni->signature_ba);
++
++      /* Write info table(s) */
++      success = nmbm_create_info_table(ni);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully created\n");
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_check_info_table_header - Check if a info table header is valid
++ * @ni: NMBM instance structure
++ * @data: pointer to the info table header
++ */
++static bool nmbm_check_info_table_header(struct nmbm_instance *ni, void *data)
++{
++      struct nmbm_info_table_header *ifthdr = data;
++
++      if (ifthdr->header.magic != NMBM_MAGIC_INFO_TABLE)
++              return false;
++
++      if (ifthdr->header.size != ni->info_table_size)
++              return false;
++
++      if (ifthdr->mapping_table_off - ifthdr->state_table_off < ni->state_table_size)
++              return false;
++
++      if (ni->info_table_size - ifthdr->mapping_table_off < ni->mapping_table_size)
++              return false;
++
++      return true;
++}
++
++/*
++ * nmbm_check_info_table - Check if a whole info table is valid
++ * @ni: NMBM instance structure
++ * @start_ba: start block address of this table
++ * @end_ba: end block address of this table
++ * @data: pointer to the info table header
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ */
++static bool nmbm_check_info_table(struct nmbm_instance *ni, uint32_t start_ba,
++                                uint32_t end_ba, void *data,
++                                uint32_t *mapping_blocks_top_ba)
++{
++      struct nmbm_info_table_header *ifthdr = data;
++      int32_t *block_mapping = (int32_t *)((uintptr_t)data + ifthdr->mapping_table_off);
++      nmbm_bitmap_t *block_state = (nmbm_bitmap_t *)((uintptr_t)data + ifthdr->state_table_off);
++      uint32_t minimum_mapping_pb = ni->signature_ba;
++      uint32_t ba;
++
++      for (ba = 0; ba < ni->data_block_count; ba++) {
++              if ((block_mapping[ba] >= ni->data_block_count && block_mapping[ba] < end_ba) ||
++                  block_mapping[ba] == ni->signature_ba)
++                      return false;
++
++              if (block_mapping[ba] >= end_ba && block_mapping[ba] < minimum_mapping_pb)
++                      minimum_mapping_pb = block_mapping[ba];
++      }
++
++      for (ba = start_ba; ba < end_ba; ba++) {
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      continue;
++
++              if (nmbm_get_block_state_raw(block_state, ba) != BLOCK_ST_GOOD)
++                      return false;
++      }
++
++      *mapping_blocks_top_ba = minimum_mapping_pb - 1;
++
++      return true;
++}
++
++/*
++ * nmbm_try_load_info_table - Try to load info table from a address
++ * @ni: NMBM instance structure
++ * @ba: start block address of the info table
++ * @eba: return the block address after end of the table
++ * @write_count: return the write count of this table
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ * @table_loaded: used to record whether ni->info_table has valid data
++ */
++static bool nmbm_try_load_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                   uint32_t *eba, uint32_t *write_count,
++                                   uint32_t *mapping_blocks_top_ba,
++                                   bool table_loaded)
++{
++      struct nmbm_info_table_header *ifthdr = (void *)ni->info_table_cache;
++      uint8_t *off = ni->info_table_cache;
++      uint32_t limit = ba + size2blk(ni, ni->info_table_size);
++      uint32_t start_ba = 0, chunksize, sizeremain = ni->info_table_size;
++      bool success, checkhdr = true;
++      int ret;
++
++      while (sizeremain && ba < limit) {
++              WATCHDOG_RESET();
++
++              if (nmbm_get_block_state(ni, ba) != BLOCK_ST_GOOD)
++                      goto next_block;
++
++              if (nmbm_check_bad_phys_block(ni, ba)) {
++                      nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++                      goto next_block;
++              }
++
++              chunksize = sizeremain;
++              if (chunksize > ni->lower.erasesize)
++                      chunksize = ni->lower.erasesize;
++
++              /* Assume block with ECC error has no info table data */
++              ret = nmbn_read_data(ni, ba2addr(ni, ba), off, chunksize);
++              if (ret < 0)
++                      goto skip_bad_block;
++              else if (ret > 0)
++                      return false;
++
++              if (checkhdr) {
++                      success = nmbm_check_info_table_header(ni, off);
++                      if (!success)
++                              return false;
++
++                      start_ba = ba;
++                      checkhdr = false;
++              }
++
++              off += chunksize;
++              sizeremain -= chunksize;
++
++              goto next_block;
++
++      skip_bad_block:
++              /* Only mark bad in memory */
++              nmbm_set_block_state(ni, ba, BLOCK_ST_BAD);
++
++      next_block:
++              ba++;
++      }
++
++      if (sizeremain)
++              return false;
++
++      success = nmbm_check_header(ni->info_table_cache, ni->info_table_size);
++      if (!success)
++              return false;
++
++      *eba = ba;
++      *write_count = ifthdr->write_count;
++
++      success = nmbm_check_info_table(ni, start_ba, ba, ni->info_table_cache,
++                                      mapping_blocks_top_ba);
++      if (!success)
++              return false;
++
++      if (!table_loaded || ifthdr->write_count > ni->info_table.write_count) {
++              memcpy(&ni->info_table, ifthdr, sizeof(ni->info_table));
++              memcpy(ni->block_state,
++                     (uint8_t *)ifthdr + ifthdr->state_table_off,
++                     ni->state_table_size);
++              memcpy(ni->block_mapping,
++                     (uint8_t *)ifthdr + ifthdr->mapping_table_off,
++                     ni->mapping_table_size);
++              ni->info_table.write_count = ifthdr->write_count;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_search_info_table - Search info table from specific address
++ * @ni: NMBM instance structure
++ * @ba: start block address to search
++ * @limit: highest block address allowed for searching
++ * @table_start_ba: return the start block address of this table
++ * @table_end_ba: return the block address after end of this table
++ * @write_count: return the write count of this table
++ * @mapping_blocks_top_ba: return the block address of top remapped block
++ * @table_loaded: used to record whether ni->info_table has valid data
++ */
++static bool nmbm_search_info_table(struct nmbm_instance *ni, uint32_t ba,
++                                 uint32_t limit, uint32_t *table_start_ba,
++                                 uint32_t *table_end_ba,
++                                 uint32_t *write_count,
++                                 uint32_t *mapping_blocks_top_ba,
++                                 bool table_loaded)
++{
++      bool success;
++
++      while (ba < limit - size2blk(ni, ni->info_table_size)) {
++              WATCHDOG_RESET();
++
++              success = nmbm_try_load_info_table(ni, ba, table_end_ba,
++                                                 write_count,
++                                                 mapping_blocks_top_ba,
++                                                 table_loaded);
++              if (success) {
++                      *table_start_ba = ba;
++                      return true;
++              }
++
++              ba++;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_load_info_table - Load info table(s) from a chip
++ * @ni: NMBM instance structure
++ * @ba: start block address to search info table
++ * @limit: highest block address allowed for searching
++ */
++static bool nmbm_load_info_table(struct nmbm_instance *ni, uint32_t ba,
++                               uint32_t limit)
++{
++      uint32_t main_table_end_ba, backup_table_end_ba, table_end_ba;
++      uint32_t main_mapping_blocks_top_ba, backup_mapping_blocks_top_ba;
++      uint32_t main_table_write_count, backup_table_write_count;
++      uint32_t i;
++      bool success;
++
++      /* Set initial value */
++      ni->main_table_ba = 0;
++      ni->backup_table_ba = 0;
++      ni->info_table.write_count = 0;
++      ni->mapping_blocks_top_ba = ni->signature_ba - 1;
++      ni->data_block_count = ni->signature.mgmt_start_pb;
++
++      /* Find first info table */
++      success = nmbm_search_info_table(ni, ba, limit, &ni->main_table_ba,
++              &main_table_end_ba, &main_table_write_count,
++              &main_mapping_blocks_top_ba, false);
++      if (!success) {
++              nlog_warn(ni, "No valid info table found\n");
++              return false;
++      }
++
++      table_end_ba = main_table_end_ba;
++
++      nlog_table_found(ni, true, main_table_write_count, ni->main_table_ba,
++                      main_table_end_ba);
++
++      /* Find second info table */
++      success = nmbm_search_info_table(ni, main_table_end_ba, limit,
++              &ni->backup_table_ba, &backup_table_end_ba,
++              &backup_table_write_count, &backup_mapping_blocks_top_ba, true);
++      if (!success) {
++              nlog_warn(ni, "Second info table not found\n");
++      } else {
++              table_end_ba = backup_table_end_ba;
++
++              nlog_table_found(ni, false, backup_table_write_count,
++                              ni->backup_table_ba, backup_table_end_ba);
++      }
++
++      /* Pick mapping_blocks_top_ba */
++      if (!ni->backup_table_ba) {
++              ni->mapping_blocks_top_ba= main_mapping_blocks_top_ba;
++      } else {
++              if (main_table_write_count >= backup_table_write_count)
++                      ni->mapping_blocks_top_ba = main_mapping_blocks_top_ba;
++              else
++                      ni->mapping_blocks_top_ba = backup_mapping_blocks_top_ba;
++      }
++
++      /* Set final mapping_blocks_ba */
++      ni->mapping_blocks_ba = table_end_ba;
++
++      /* Set final data_block_count */
++      for (i = ni->signature.mgmt_start_pb; i > 0; i--) {
++              if (ni->block_mapping[i - 1] >= 0) {
++                      ni->data_block_count = i;
++                      break;
++              }
++      }
++
++      /* Debug purpose: mark mapped blocks and bad blocks */
++      for (i = 0; i < ni->data_block_count; i++) {
++              if (ni->block_mapping[i] > ni->mapping_blocks_top_ba)
++                      nmbm_mark_block_color_mapped(ni, ni->block_mapping[i]);
++      }
++
++      for (i = 0; i < ni->block_count; i++) {
++              if (nmbm_get_block_state(ni, i) == BLOCK_ST_BAD)
++                      nmbm_mark_block_color_bad(ni, i);
++      }
++
++      /* Regenerate the info table cache from the final selected info table */
++      nmbm_generate_info_table_cache(ni);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY)
++              return true;
++
++      /*
++       * If only one table exists, try to write another table.
++       * If two tables have different write count, try to update info table
++       */
++      if (!ni->backup_table_ba) {
++              success = nmbm_rescue_single_info_table(ni);
++      } else if (main_table_write_count != backup_table_write_count) {
++              /* Mark state & mapping tables changed */
++              ni->block_state_changed = 1;
++              ni->block_mapping_changed = 1;
++
++              success = nmbm_update_single_info_table(ni,
++                      main_table_write_count < backup_table_write_count);
++      } else {
++              success = true;
++      }
++
++      /*
++       * If there is no spare unmapped blocks, or still only one table
++       * exists, set the chip to read-only
++       */
++      if (ni->mapping_blocks_ba == ni->mapping_blocks_top_ba) {
++              nlog_warn(ni, "No spare unmapped blocks. Device is now read-only\n");
++              ni->protected = 1;
++      } else if (!success) {
++              nlog_warn(ni, "Only one info table found. Device is now read-only\n");
++              ni->protected = 1;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_load_existing - Load NMBM from a new chip
++ * @ni: NMBM instance structure
++ */
++static bool nmbm_load_existing(struct nmbm_instance *ni)
++{
++      bool success;
++
++      /* Calculate the boundary of management blocks */
++      ni->mgmt_start_ba = ni->signature.mgmt_start_pb;
++
++      nlog_debug(ni, "NMBM management region starts at block %u [0x%08llx]\n",
++                ni->mgmt_start_ba, ba2addr(ni, ni->mgmt_start_ba));
++      nmbm_mark_block_color_mgmt(ni, ni->mgmt_start_ba,
++                                 ni->signature_ba - 1);
++
++      /* Look for info table(s) */
++      success = nmbm_load_info_table(ni, ni->mgmt_start_ba,
++              ni->signature_ba);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully attached %s\n",
++                        (ni->lower.flags & NMBM_F_READ_ONLY) ? "in read-only mode" : "");
++              return true;
++      }
++
++      if (!(ni->lower.flags & NMBM_F_CREATE))
++              return false;
++
++      /* Fill block state table & mapping table */
++      nmbm_scan_badblocks(ni);
++      nmbm_build_mapping_table(ni);
++
++      if (ni->lower.flags & NMBM_F_READ_ONLY) {
++              nlog_info(ni, "NMBM has been initialized in read-only mode\n");
++              return true;
++      }
++
++      /* Write info table(s) */
++      success = nmbm_create_info_table(ni);
++      if (success) {
++              nlog_info(ni, "NMBM has been successfully created\n");
++              return true;
++      }
++
++      return false;
++}
++
++/*
++ * nmbm_find_signature - Find signature in the lower NAND chip
++ * @ni: NMBM instance structure
++ * @signature_ba: used for storing block address of the signature
++ * @signature_ba: return the actual block address of signature block
++ *
++ * Find a valid signature from a specific range in the lower NAND chip,
++ * from bottom (highest address) to top (lowest address)
++ *
++ * Return true if found.
++ */
++static bool nmbm_find_signature(struct nmbm_instance *ni,
++                              struct nmbm_signature *signature,
++                              uint32_t *signature_ba)
++{
++      struct nmbm_signature sig;
++      uint64_t off, addr;
++      uint32_t block_count, ba, limit;
++      bool success;
++      int ret;
++
++      /* Calculate top and bottom block address */
++      block_count = ni->lower.size >> ni->erasesize_shift;
++      ba = block_count;
++      limit = (block_count / NMBM_MGMT_DIV) * (NMBM_MGMT_DIV - ni->lower.max_ratio);
++      if (ni->lower.max_reserved_blocks && block_count - limit > ni->lower.max_reserved_blocks)
++              limit = block_count - ni->lower.max_reserved_blocks;
++
++      while (ba >= limit) {
++              WATCHDOG_RESET();
++
++              ba--;
++              addr = ba2addr(ni, ba);
++
++              if (nmbm_check_bad_phys_block(ni, ba))
++                      continue;
++
++              /* Check every page.
++               * As long as at leaset one page contains valid signature,
++               * the block is treated as a valid signature block.
++               */
++              for (off = 0; off < ni->lower.erasesize;
++                   off += ni->lower.writesize) {
++                      WATCHDOG_RESET();
++
++                      ret = nmbn_read_data(ni, addr + off, &sig,
++                                           sizeof(sig));
++                      if (ret)
++                              continue;
++
++                      /* Check for header size and checksum */
++                      success = nmbm_check_header(&sig, sizeof(sig));
++                      if (!success)
++                              continue;
++
++                      /* Check for header magic */
++                      if (sig.header.magic == NMBM_MAGIC_SIGNATURE) {
++                              /* Found it */
++                              memcpy(signature, &sig, sizeof(sig));
++                              *signature_ba = ba;
++                              return true;
++                      }
++              }
++      };
++
++      return false;
++}
++
++/*
++ * is_power_of_2_u64 - Check whether a 64-bit integer is power of 2
++ * @n: number to check
++ */
++static bool is_power_of_2_u64(uint64_t n)
++{
++      return (n != 0 && ((n & (n - 1)) == 0));
++}
++
++/*
++ * nmbm_check_lower_members - Validate the members of lower NAND device
++ * @nld: Lower NAND chip structure
++ */
++static bool nmbm_check_lower_members(struct nmbm_lower_device *nld)
++{
++
++      if (!nld->size || !is_power_of_2_u64(nld->size)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Chip size %llu is not valid\n", nld->size);
++              return false;
++      }
++
++      if (!nld->erasesize || !is_power_of_2(nld->erasesize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Block size %u is not valid\n", nld->erasesize);
++              return false;
++      }
++
++      if (!nld->writesize || !is_power_of_2(nld->writesize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Page size %u is not valid\n", nld->writesize);
++              return false;
++      }
++
++      if (!nld->oobsize || !is_power_of_2(nld->oobsize)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "Page spare size %u is not valid\n", nld->oobsize);
++              return false;
++      }
++
++      if (!nld->read_page) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR, "read_page() is required\n");
++              return false;
++      }
++
++      if (!(nld->flags & NMBM_F_READ_ONLY) && (!nld->write_page || !nld->erase_block)) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "write_page() and erase_block() are required\n");
++              return false;
++      }
++
++      /* Data sanity check */
++      if (!nld->max_ratio)
++              nld->max_ratio = 1;
++
++      if (nld->max_ratio >= NMBM_MGMT_DIV - 1) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "max ratio %u is invalid\n", nld->max_ratio);
++              return false;
++      }
++
++      if (nld->max_reserved_blocks && nld->max_reserved_blocks < NMBM_MGMT_BLOCKS_MIN) {
++              nmbm_log_lower(nld, NMBM_LOG_ERR,
++                             "max reserved blocks %u is too small\n", nld->max_reserved_blocks);
++              return false;
++      }
++
++      return true;
++}
++
++/*
++ * nmbm_calc_structure_size - Calculate the instance structure size
++ * @nld: NMBM lower device structure
++ */
++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld)
++{
++      uint32_t state_table_size, mapping_table_size, info_table_size;
++      uint32_t block_count;
++
++      block_count = nmbm_lldiv(nld->size, nld->erasesize);
++
++      /* Calculate info table size */
++      state_table_size = ((block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
++              NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
++      mapping_table_size = block_count * sizeof(int32_t);
++
++      info_table_size = NMBM_ALIGN(sizeof(struct nmbm_info_table_header),
++                                   nld->writesize);
++      info_table_size += NMBM_ALIGN(state_table_size, nld->writesize);
++      info_table_size += NMBM_ALIGN(mapping_table_size, nld->writesize);
++
++      return info_table_size + state_table_size + mapping_table_size +
++              nld->writesize + nld->oobsize + sizeof(struct nmbm_instance);
++}
++
++/*
++ * nmbm_init_structure - Initialize members of instance structure
++ * @ni: NMBM instance structure
++ */
++static void nmbm_init_structure(struct nmbm_instance *ni)
++{
++      uint32_t pages_per_block, blocks_per_chip;
++      uintptr_t ptr;
++
++      pages_per_block = ni->lower.erasesize / ni->lower.writesize;
++      blocks_per_chip = nmbm_lldiv(ni->lower.size, ni->lower.erasesize);
++
++      ni->rawpage_size = ni->lower.writesize + ni->lower.oobsize;
++      ni->rawblock_size = pages_per_block * ni->rawpage_size;
++      ni->rawchip_size = blocks_per_chip * ni->rawblock_size;
++
++      ni->writesize_mask = ni->lower.writesize - 1;
++      ni->erasesize_mask = ni->lower.erasesize - 1;
++
++      ni->writesize_shift = ffs(ni->lower.writesize) - 1;
++      ni->erasesize_shift = ffs(ni->lower.erasesize) - 1;
++
++      /* Calculate number of block this chip */
++      ni->block_count = ni->lower.size >> ni->erasesize_shift;
++
++      /* Calculate info table size */
++      ni->state_table_size = ((ni->block_count + NMBM_BITMAP_BLOCKS_PER_UNIT - 1) /
++              NMBM_BITMAP_BLOCKS_PER_UNIT) * NMBM_BITMAP_UNIT_SIZE;
++      ni->mapping_table_size = ni->block_count * sizeof(*ni->block_mapping);
++
++      ni->info_table_size = NMBM_ALIGN(sizeof(ni->info_table),
++                                       ni->lower.writesize);
++      ni->info_table.state_table_off = ni->info_table_size;
++
++      ni->info_table_size += NMBM_ALIGN(ni->state_table_size,
++                                        ni->lower.writesize);
++      ni->info_table.mapping_table_off = ni->info_table_size;
++
++      ni->info_table_size += NMBM_ALIGN(ni->mapping_table_size,
++                                        ni->lower.writesize);
++
++      ni->info_table_spare_blocks = nmbm_get_spare_block_count(
++              size2blk(ni, ni->info_table_size));
++
++      /* Assign memory to members */
++      ptr = (uintptr_t)ni + sizeof(*ni);
++
++      ni->info_table_cache = (void *)ptr;
++      ptr += ni->info_table_size;
++
++      ni->block_state = (void *)ptr;
++      ptr += ni->state_table_size;
++
++      ni->block_mapping = (void *)ptr;
++      ptr += ni->mapping_table_size;
++
++      ni->page_cache = (uint8_t *)ptr;
++
++      /* Initialize block state table */
++      ni->block_state_changed = 0;
++      memset(ni->block_state, 0xff, ni->state_table_size);
++
++      /* Initialize block mapping table */
++      ni->block_mapping_changed = 0;
++}
++
++/*
++ * nmbm_attach - Attach to a lower device
++ * @nld: NMBM lower device structure
++ * @ni: NMBM instance structure
++ */
++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni)
++{
++      bool success;
++
++      if (!nld || !ni)
++              return -EINVAL;
++
++      /* Set default log level */
++      ni->log_display_level = NMBM_DEFAULT_LOG_LEVEL;
++
++      /* Check lower members */
++      success = nmbm_check_lower_members(nld);
++      if (!success)
++              return -EINVAL;
++
++      /* Initialize NMBM instance */
++      memcpy(&ni->lower, nld, sizeof(struct nmbm_lower_device));
++      nmbm_init_structure(ni);
++
++      success = nmbm_find_signature(ni, &ni->signature, &ni->signature_ba);
++      if (!success) {
++              if (!(nld->flags & NMBM_F_CREATE)) {
++                      nlog_err(ni, "Signature not found\n");
++                      return -ENODEV;
++              }
++
++              success = nmbm_create_new(ni);
++              if (!success)
++                      return -ENODEV;
++
++              return 0;
++      }
++
++      nlog_info(ni, "Signature found at block %u [0x%08llx]\n",
++               ni->signature_ba, ba2addr(ni, ni->signature_ba));
++      nmbm_mark_block_color_signature(ni, ni->signature_ba);
++
++      if (ni->signature.header.version != NMBM_VER) {
++              nlog_err(ni, "NMBM version %u.%u is not supported\n",
++                      NMBM_VERSION_MAJOR_GET(ni->signature.header.version),
++                      NMBM_VERSION_MINOR_GET(ni->signature.header.version));
++              return -EINVAL;
++      }
++
++      if (ni->signature.nand_size != nld->size ||
++          ni->signature.block_size != nld->erasesize ||
++          ni->signature.page_size != nld->writesize ||
++          ni->signature.spare_size != nld->oobsize) {
++              nlog_err(ni, "NMBM configuration mismatch\n");
++              return -EINVAL;
++      }
++
++      success = nmbm_load_existing(ni);
++      if (!success)
++              return -ENODEV;
++
++      return 0;
++}
++
++/*
++ * nmbm_detach - Detach from a lower device, and save all tables
++ * @ni: NMBM instance structure
++ */
++int nmbm_detach(struct nmbm_instance *ni)
++{
++      if (!ni)
++              return -EINVAL;
++
++      if (!(ni->lower.flags & NMBM_F_READ_ONLY))
++              nmbm_update_info_table(ni);
++
++      nmbm_mark_block_color_normal(ni, 0, ni->block_count - 1);
++
++      return 0;
++}
++
++/*
++ * nmbm_erase_logic_block - Erase a logic block
++ * @ni: NMBM instance structure
++ * @nmbm_erase_logic_block: logic block address
++ *
++ * Logic block will be mapped to physical block before erasing.
++ * Bad block found during erasinh will be remapped to a good block if there is
++ * still at least one good spare block available.
++ */
++static int nmbm_erase_logic_block(struct nmbm_instance *ni, uint32_t block_addr)
++{
++      uint32_t pb;
++      bool success;
++
++retry:
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[block_addr];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", block_addr);
++              return -EIO;
++      }
++
++      /* Remap logic block if current physical block is a bad block */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD ||
++          nmbm_get_block_state(ni, pb) == BLOCK_ST_NEED_REMAP)
++              goto remap_logic_block;
++
++      /* Insurance to detect unexpected bad block marked by user */
++      if (nmbm_check_bad_phys_block(ni, pb)) {
++              nlog_warn(ni, "Found unexpected bad block possibly marked by user\n");
++              nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++              goto remap_logic_block;
++      }
++
++      success = nmbm_erase_block_and_check(ni, pb);
++      if (success)
++              return 0;
++
++      /* Mark bad block */
++      nmbm_mark_phys_bad_block(ni, pb);
++      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++
++remap_logic_block:
++      /* Try to assign a new block */
++      success = nmbm_map_block(ni, block_addr);
++      if (!success) {
++              /* Mark logic block unusable, and update info table */
++              ni->block_mapping[block_addr] = -1;
++              if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
++                      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++              nmbm_update_info_table(ni);
++              return -EIO;
++      }
++
++      /* Update info table before erasing */
++      if (nmbm_get_block_state(ni, pb) != BLOCK_ST_NEED_REMAP)
++              nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++      nmbm_update_info_table(ni);
++
++      goto retry;
++}
++
++/*
++ * nmbm_erase_block_range - Erase logic blocks
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: erase range
++ * @failed_addr: return failed block address if error occurs
++ */
++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
++                         uint64_t size, uint64_t *failed_addr)
++{
++      uint32_t start_ba, end_ba;
++      int ret;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Erase range 0xllxu is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No blocks to be erased\n");
++              return 0;
++      }
++
++      start_ba = addr2ba(ni, addr);
++      end_ba = addr2ba(ni, addr + size - 1);
++
++      while (start_ba <= end_ba) {
++              WATCHDOG_RESET();
++
++              ret = nmbm_erase_logic_block(ni, start_ba);
++              if (ret) {
++                      if (failed_addr)
++                              *failed_addr = ba2addr(ni, start_ba);
++                      return ret;
++              }
++
++              start_ba++;
++      }
++
++      return 0;
++}
++
++/*
++ * nmbm_read_logic_page - Read page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer to store main data. optional.
++ * @oob: buffer to store oob data. optional.
++ * @mode: read mode
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++static int nmbm_read_logic_page(struct nmbm_instance *ni, uint64_t addr,
++                              void *data, void *oob, enum nmbm_oob_mode mode)
++{
++      uint32_t lb, pb, offset;
++      uint64_t paddr;
++
++      /* Extract block address and in-block offset */
++      lb = addr2ba(ni, addr);
++      offset = addr & ni->erasesize_mask;
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", lb);
++              return -EIO;
++      }
++
++      /* Fail if physical block is marked bad */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return -EIO;
++
++      /* Assemble new address */
++      paddr = ba2addr(ni, pb) + offset;
++
++      return nmbm_read_phys_page(ni, paddr, data, oob, mode);
++}
++
++/*
++ * nmbm_read_single_page - Read one page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer to store main data. optional.
++ * @oob: buffer to store oob data. optional.
++ * @mode: read mode
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        void *oob, enum nmbm_oob_mode mode)
++{
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      return nmbm_read_logic_page(ni, addr, data, oob, mode);
++}
++
++/*
++ * nmbm_read_range - Read data without oob
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: data size to read
++ * @data: buffer to store main data to be read
++ * @mode: read mode
++ * @retlen: return actual data size read
++ *
++ * Return 0 for success, positive value for corrected bitflip count,
++ * -EBADMSG for ecc error, other negative values for other errors
++ */
++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                  void *data, enum nmbm_oob_mode mode, size_t *retlen)
++{
++      uint64_t off = addr;
++      uint8_t *ptr = data;
++      size_t sizeremain = size, chunksize, leading;
++      bool has_ecc_err = false;
++      int ret, max_bitflips = 0;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Read range 0x%llx is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No data to be read\n");
++              return 0;
++      }
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_read_logic_page(ni, off - leading, ptr,
++                                                      NULL, mode);
++                      if (ret < 0 && ret != -EBADMSG)
++                              break;
++              } else {
++                      ret = nmbm_read_logic_page(ni, off - leading,
++                                                      ni->page_cache, NULL,
++                                                      mode);
++                      if (ret < 0 && ret != -EBADMSG)
++                              break;
++
++                      memcpy(ptr, ni->page_cache + leading, chunksize);
++              }
++
++              if (ret == -EBADMSG)
++                      has_ecc_err = true;
++
++              if (ret > max_bitflips)
++                      max_bitflips = ret;
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      if (retlen)
++              *retlen = size - sizeremain;
++
++      if (ret < 0 && ret != -EBADMSG)
++              return ret;
++
++      if (has_ecc_err)
++              return -EBADMSG;
++
++      return max_bitflips;
++}
++
++/*
++ * nmbm_write_logic_page - Read page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer contains main data. optional.
++ * @oob: buffer contains oob data. optional.
++ * @mode: write mode
++ */
++static int nmbm_write_logic_page(struct nmbm_instance *ni, uint64_t addr,
++                                const void *data, const void *oob,
++                                enum nmbm_oob_mode mode)
++{
++      uint32_t lb, pb, offset;
++      uint64_t paddr;
++      bool success;
++
++      /* Extract block address and in-block offset */
++      lb = addr2ba(ni, addr);
++      offset = addr & ni->erasesize_mask;
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      /* Whether the logic block is good (has valid mapping) */
++      if ((int32_t)pb < 0) {
++              nlog_debug(ni, "Logic block %u is a bad block\n", lb);
++              return -EIO;
++      }
++
++      /* Fail if physical block is marked bad */
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return -EIO;
++
++      /* Assemble new address */
++      paddr = ba2addr(ni, pb) + offset;
++
++      success = nmbm_write_phys_page(ni, paddr, data, oob, mode);
++      if (success)
++              return 0;
++
++      /*
++       * Do not remap bad block here. Just mark this block in state table.
++       * Remap this block on erasing.
++       */
++      nmbm_set_block_state(ni, pb, BLOCK_ST_NEED_REMAP);
++      nmbm_update_info_table(ni);
++
++      return -EIO;
++}
++
++/*
++ * nmbm_write_single_page - Write one page based on logic address
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @data: buffer contains main data. optional.
++ * @oob: buffer contains oob data. optional.
++ * @mode: write mode
++ */
++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
++                         const void *data, const void *oob,
++                         enum nmbm_oob_mode mode)
++{
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      return nmbm_write_logic_page(ni, addr, data, oob, mode);
++}
++
++/*
++ * nmbm_write_range - Write data without oob
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ * @size: data size to write
++ * @data: buffer contains data to be written
++ * @mode: write mode
++ * @retlen: return actual data size written
++ */
++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                   const void *data, enum nmbm_oob_mode mode,
++                   size_t *retlen)
++{
++      uint64_t off = addr;
++      const uint8_t *ptr = data;
++      size_t sizeremain = size, chunksize, leading;
++      int ret;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      if (addr + size > ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Write size 0x%zx is too large\n", size);
++              return -EINVAL;
++      }
++
++      if (!size) {
++              nlog_warn(ni, "No data to be written\n");
++              return 0;
++      }
++
++      while (sizeremain) {
++              WATCHDOG_RESET();
++
++              leading = off & ni->writesize_mask;
++              chunksize = ni->lower.writesize - leading;
++              if (chunksize > sizeremain)
++                      chunksize = sizeremain;
++
++              if (chunksize == ni->lower.writesize) {
++                      ret = nmbm_write_logic_page(ni, off - leading, ptr,
++                                                       NULL, mode);
++                      if (ret)
++                              break;
++              } else {
++                      memset(ni->page_cache, 0xff, leading);
++                      memcpy(ni->page_cache + leading, ptr, chunksize);
++
++                      ret = nmbm_write_logic_page(ni, off - leading,
++                                                       ni->page_cache, NULL,
++                                                       mode);
++                      if (ret)
++                              break;
++              }
++
++              off += chunksize;
++              ptr += chunksize;
++              sizeremain -= chunksize;
++      }
++
++      if (retlen)
++              *retlen = size - sizeremain;
++
++      return ret;
++}
++
++/*
++ * nmbm_check_bad_block - Check whether a logic block is usable
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ */
++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      uint32_t lb, pb;
++
++      if (!ni)
++              return -EINVAL;
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      lb = addr2ba(ni, addr);
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      if ((int32_t)pb < 0)
++              return 1;
++
++      if (nmbm_get_block_state(ni, pb) == BLOCK_ST_BAD)
++              return 1;
++
++      return 0;
++}
++
++/*
++ * nmbm_mark_bad_block - Mark a logic block unusable
++ * @ni: NMBM instance structure
++ * @addr: logic linear address
++ */
++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr)
++{
++      uint32_t lb, pb;
++
++      if (!ni)
++              return -EINVAL;
++
++      /* Sanity check */
++      if (ni->protected || (ni->lower.flags & NMBM_F_READ_ONLY)) {
++              nlog_debug(ni, "Device is forced read-only\n");
++              return -EROFS;
++      }
++
++      if (addr >= ba2addr(ni, ni->data_block_count)) {
++              nlog_err(ni, "Address 0x%llx is invalid\n", addr);
++              return -EINVAL;
++      }
++
++      lb = addr2ba(ni, addr);
++
++      /* Map logic block to physical block */
++      pb = ni->block_mapping[lb];
++
++      if ((int32_t)pb < 0)
++              return 0;
++
++      ni->block_mapping[lb] = -1;
++      nmbm_mark_phys_bad_block(ni, pb);
++      nmbm_set_block_state(ni, pb, BLOCK_ST_BAD);
++      nmbm_update_info_table(ni);
++
++      return 0;
++}
++
++/*
++ * nmbm_get_avail_size - Get available user data size
++ * @ni: NMBM instance structure
++ */
++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni)
++{
++      if (!ni)
++              return 0;
++
++      return (uint64_t)ni->data_block_count << ni->erasesize_shift;
++}
++
++/*
++ * nmbm_get_lower_device - Get lower device structure
++ * @ni: NMBM instance structure
++ * @nld: pointer to hold the data of lower device structure
++ */
++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld)
++{
++      if (!ni)
++              return -EINVAL;
++
++      if (nld)
++              memcpy(nld, &ni->lower, sizeof(*nld));
++
++      return 0;
++}
++
++#include "nmbm-debug.inl"
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-debug.h
+@@ -0,0 +1,37 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Debug addons for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_DEBUG_H_
++#define _NMBM_DEBUG_H_
++
++#include "nmbm-private.h"
++
++#define nmbm_mark_block_color_normal(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_bad(ni, ba)
++#define nmbm_mark_block_color_mgmt(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_signature(ni, ba)
++#define nmbm_mark_block_color_info_table(ni, start_ba, end_ba)
++#define nmbm_mark_block_color_mapped(ni, ba)
++
++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba);
++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba);
++
++enum nmmb_block_type {
++      NMBM_BLOCK_GOOD_DATA,
++      NMBM_BLOCK_GOOD_MGMT,
++      NMBM_BLOCK_BAD,
++      NMBM_BLOCK_MAIN_INFO_TABLE,
++      NMBM_BLOCK_BACKUP_INFO_TABLE,
++      NMBM_BLOCK_REMAPPED,
++      NMBM_BLOCK_SIGNATURE,
++
++      __NMBM_BLOCK_TYPE_MAX
++};
++
++#endif /* _NMBM_DEBUG_H_ */
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-debug.inl
+@@ -0,0 +1,39 @@
++
++uint32_t nmbm_debug_get_block_state(struct nmbm_instance *ni, uint32_t ba)
++{
++      return nmbm_get_block_state(ni, ba);
++}
++
++char nmbm_debug_get_phys_block_type(struct nmbm_instance *ni, uint32_t ba)
++{
++      uint32_t eba, limit;
++      bool success;
++
++      if (nmbm_get_block_state(ni, ba) == BLOCK_ST_BAD)
++              return NMBM_BLOCK_BAD;
++
++      if (ba < ni->data_block_count)
++              return NMBM_BLOCK_GOOD_DATA;
++
++      if (ba == ni->signature_ba)
++              return NMBM_BLOCK_SIGNATURE;
++
++      if (ni->main_table_ba) {
++              limit = ni->backup_table_ba ? ni->backup_table_ba :
++                      ni->mapping_blocks_ba;
++
++              success = nmbm_block_walk_asc(ni, ni->main_table_ba, &eba,
++                      size2blk(ni, ni->info_table_size), limit);
++
++              if (success && ba >= ni->main_table_ba && ba < eba)
++                      return NMBM_BLOCK_MAIN_INFO_TABLE;
++      }
++
++      if (ba >= ni->backup_table_ba && ba < ni->mapping_blocks_ba)
++              return NMBM_BLOCK_BACKUP_INFO_TABLE;
++
++      if (ba > ni->mapping_blocks_top_ba && ba < ni->signature_ba)
++              return NMBM_BLOCK_REMAPPED;
++
++      return NMBM_BLOCK_GOOD_MGMT;
++}
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-private.h
+@@ -0,0 +1,137 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_PRIVATE_H_
++#define _NMBM_PRIVATE_H_
++
++#include <nmbm/nmbm.h>
++
++#define NMBM_MAGIC_SIGNATURE                  0x304d4d4e      /* NMM0 */
++#define NMBM_MAGIC_INFO_TABLE                 0x314d4d4e      /* NMM1 */
++
++#define NMBM_VERSION_MAJOR_S                  0
++#define NMBM_VERSION_MAJOR_M                  0xffff
++#define NMBM_VERSION_MINOR_S                  16
++#define NMBM_VERSION_MINOR_M                  0xffff
++#define NMBM_VERSION_MAKE(major, minor)               (((major) & NMBM_VERSION_MAJOR_M) | \
++                                              (((minor) & NMBM_VERSION_MINOR_M) << \
++                                              NMBM_VERSION_MINOR_S))
++#define NMBM_VERSION_MAJOR_GET(ver)           (((ver) >> NMBM_VERSION_MAJOR_S) & \
++                                              NMBM_VERSION_MAJOR_M)
++#define NMBM_VERSION_MINOR_GET(ver)           (((ver) >> NMBM_VERSION_MINOR_S) & \
++                                              NMBM_VERSION_MINOR_M)
++
++typedef uint32_t                              nmbm_bitmap_t;
++#define NMBM_BITMAP_UNIT_SIZE                 (sizeof(nmbm_bitmap_t))
++#define NMBM_BITMAP_BITS_PER_BLOCK            2
++#define NMBM_BITMAP_BITS_PER_UNIT             (8 * sizeof(nmbm_bitmap_t))
++#define NMBM_BITMAP_BLOCKS_PER_UNIT           (NMBM_BITMAP_BITS_PER_UNIT / \
++                                               NMBM_BITMAP_BITS_PER_BLOCK)
++
++#define NMBM_SPARE_BLOCK_MULTI                        1
++#define NMBM_SPARE_BLOCK_DIV                  2
++#define NMBM_SPARE_BLOCK_MIN                  2
++
++#define NMBM_MGMT_DIV                         16
++#define NMBM_MGMT_BLOCKS_MIN                  32
++
++#define NMBM_TRY_COUNT                                3
++
++#define BLOCK_ST_BAD                          0
++#define BLOCK_ST_NEED_REMAP                   2
++#define BLOCK_ST_GOOD                         3
++#define BLOCK_ST_MASK                         3
++
++struct nmbm_header {
++      uint32_t magic;
++      uint32_t version;
++      uint32_t size;
++      uint32_t checksum;
++};
++
++struct nmbm_signature {
++      struct nmbm_header header;
++      uint64_t nand_size;
++      uint32_t block_size;
++      uint32_t page_size;
++      uint32_t spare_size;
++      uint32_t mgmt_start_pb;
++      uint8_t max_try_count;
++      uint8_t padding[3];
++};
++
++struct nmbm_info_table_header {
++      struct nmbm_header header;
++      uint32_t write_count;
++      uint32_t state_table_off;
++      uint32_t mapping_table_off;
++      uint32_t padding;
++};
++
++struct nmbm_instance {
++      struct nmbm_lower_device lower;
++
++      uint32_t rawpage_size;
++      uint32_t rawblock_size;
++      uint32_t rawchip_size;
++
++      uint32_t writesize_mask;
++      uint32_t erasesize_mask;
++      uint16_t writesize_shift;
++      uint16_t erasesize_shift;
++
++      struct nmbm_signature signature;
++
++      uint8_t *info_table_cache;
++      uint32_t info_table_size;
++      uint32_t info_table_spare_blocks;
++      struct nmbm_info_table_header info_table;
++
++      nmbm_bitmap_t *block_state;
++      uint32_t block_state_changed;
++      uint32_t state_table_size;
++
++      int32_t *block_mapping;
++      uint32_t block_mapping_changed;
++      uint32_t mapping_table_size;
++
++      uint8_t *page_cache;
++
++      int protected;
++
++      uint32_t block_count;
++      uint32_t data_block_count;
++
++      uint32_t mgmt_start_ba;
++      uint32_t main_table_ba;
++      uint32_t backup_table_ba;
++      uint32_t mapping_blocks_ba;
++      uint32_t mapping_blocks_top_ba;
++      uint32_t signature_ba;
++
++      enum nmbm_log_category log_display_level;
++};
++
++/* Log utilities */
++#define nlog_debug(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_DEBUG, fmt, ##__VA_ARGS__)
++
++#define nlog_info(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_INFO, fmt, ##__VA_ARGS__)
++
++#define nlog_warn(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_WARN, fmt, ##__VA_ARGS__)
++
++#define nlog_err(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_ERR, fmt, ##__VA_ARGS__)
++
++#define nlog_emerg(ni, fmt, ...) \
++      nmbm_log(ni, NMBM_LOG_EMERG, fmt, ##__VA_ARGS__)
++
++#endif /* _NMBM_PRIVATE_H_ */
+--- /dev/null
++++ b/include/nmbm/nmbm-os.h
+@@ -0,0 +1,66 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * OS-dependent definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_OS_H_
++#define _NMBM_OS_H_
++
++#include <div64.h>
++#include <stdbool.h>
++#include <watchdog.h>
++#include <u-boot/crc.h>
++#include <linux/errno.h>
++#include <linux/log2.h>
++#include <linux/types.h>
++
++static inline uint32_t nmbm_crc32(uint32_t crcval, const void *buf, size_t size)
++{
++      uint chksz;
++      const unsigned char *p = buf;
++
++      while (size) {
++              if (size > UINT_MAX)
++                      chksz = UINT_MAX;
++              else
++                      chksz = (uint)size;
++
++              crcval = crc32_no_comp(crcval, p, chksz);
++              size -= chksz;
++              p += chksz;
++      }
++
++      return crcval;
++}
++
++static inline uint32_t nmbm_lldiv(uint64_t dividend, uint32_t divisor)
++{
++#if BITS_PER_LONG == 64
++      return dividend / divisor;
++#else
++      __div64_32(&dividend, divisor);
++      return dividend;
++#endif
++}
++
++#ifdef CONFIG_NMBM_LOG_LEVEL_DEBUG
++#define NMBM_DEFAULT_LOG_LEVEL                0
++#elif defined(NMBM_LOG_LEVEL_INFO)
++#define NMBM_DEFAULT_LOG_LEVEL                1
++#elif defined(NMBM_LOG_LEVEL_WARN)
++#define NMBM_DEFAULT_LOG_LEVEL                2
++#elif defined(NMBM_LOG_LEVEL_ERR)
++#define NMBM_DEFAULT_LOG_LEVEL                3
++#elif defined(NMBM_LOG_LEVEL_EMERG)
++#define NMBM_DEFAULT_LOG_LEVEL                4
++#elif defined(NMBM_LOG_LEVEL_NONE)
++#define NMBM_DEFAULT_LOG_LEVEL                5
++#else
++#define NMBM_DEFAULT_LOG_LEVEL                1
++#endif
++
++#endif /* _NMBM_OS_H_ */
+--- /dev/null
++++ b/include/nmbm/nmbm.h
+@@ -0,0 +1,102 @@
++/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Definitions for NAND Mapped-block Management (NMBM)
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_H_
++#define _NMBM_H_
++
++#include <nmbm/nmbm-os.h>
++
++enum nmbm_log_category {
++      NMBM_LOG_DEBUG,
++      NMBM_LOG_INFO,
++      NMBM_LOG_WARN,
++      NMBM_LOG_ERR,
++      NMBM_LOG_EMERG,
++
++      __NMBM_LOG_MAX
++};
++
++enum nmbm_oob_mode {
++      NMBM_MODE_PLACE_OOB,
++      NMBM_MODE_AUTO_OOB,
++      NMBM_MODE_RAW,
++
++      __NMBM_MODE_MAX
++};
++
++struct nmbm_lower_device {
++      uint32_t max_ratio;
++      uint32_t max_reserved_blocks;
++      int flags;
++
++      uint64_t size;
++      uint32_t erasesize;
++      uint32_t writesize;
++      uint32_t oobsize;
++      uint32_t oobavail;
++
++      void *arg;
++      int (*reset_chip)(void *arg);
++
++      /*
++       * read_page:
++       *    return 0 if succeeds
++       *    return positive number for ecc error
++       *    return negative number for other errors
++       */
++      int (*read_page)(void *arg, uint64_t addr, void *buf, void *oob, enum nmbm_oob_mode mode);
++      int (*write_page)(void *arg, uint64_t addr, const void *buf, const void *oob, enum nmbm_oob_mode mode);
++      int (*erase_block)(void *arg, uint64_t addr);
++
++      int (*is_bad_block)(void *arg, uint64_t addr);
++      int (*mark_bad_block)(void *arg, uint64_t addr);
++
++      /* OS-dependent logging function */
++      void (*logprint)(void *arg, enum nmbm_log_category level, const char *fmt, va_list ap);
++};
++
++struct nmbm_instance;
++
++/* Create NMBM if management area not found, or not complete */
++#define NMBM_F_CREATE                 0x01
++
++/* Empty page is also protected by ECC, and bitflip(s) can be corrected */
++#define NMBM_F_EMPTY_PAGE_ECC_OK      0x02
++
++/* Do not write anything back to flash */
++#define NMBM_F_READ_ONLY              0x04
++
++size_t nmbm_calc_structure_size(struct nmbm_lower_device *nld);
++int nmbm_attach(struct nmbm_lower_device *nld, struct nmbm_instance *ni);
++int nmbm_detach(struct nmbm_instance *ni);
++
++enum nmbm_log_category nmbm_set_log_level(struct nmbm_instance *ni,
++                                        enum nmbm_log_category level);
++
++int nmbm_erase_block_range(struct nmbm_instance *ni, uint64_t addr,
++                         uint64_t size, uint64_t *failed_addr);
++int nmbm_read_single_page(struct nmbm_instance *ni, uint64_t addr, void *data,
++                        void *oob, enum nmbm_oob_mode mode);
++int nmbm_read_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                  void *data, enum nmbm_oob_mode mode, size_t *retlen);
++int nmbm_write_single_page(struct nmbm_instance *ni, uint64_t addr,
++                         const void *data, const void *oob,
++                         enum nmbm_oob_mode mode);
++int nmbm_write_range(struct nmbm_instance *ni, uint64_t addr, size_t size,
++                   const void *data, enum nmbm_oob_mode mode,
++                   size_t *retlen);
++
++int nmbm_check_bad_block(struct nmbm_instance *ni, uint64_t addr);
++int nmbm_mark_bad_block(struct nmbm_instance *ni, uint64_t addr);
++
++uint64_t nmbm_get_avail_size(struct nmbm_instance *ni);
++
++int nmbm_get_lower_device(struct nmbm_instance *ni, struct nmbm_lower_device *nld);
++
++#endif /* _NMBM_H_ */
diff --git a/package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch b/package/boot/uboot-mediatek/patches/100-07-mtd-nmbm-add-support-for-mtd.patch
new file mode 100644 (file)
index 0000000..644ac8f
--- /dev/null
@@ -0,0 +1,958 @@
+From 0524995f07fcd216a1a7e267fdb5cf2b0ede8489 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:42:12 +0800
+Subject: [PATCH 41/71] mtd: nmbm: add support for mtd
+
+Add support to create NMBM based on MTD devices
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/nmbm/Kconfig    |   5 +
+ drivers/mtd/nmbm/Makefile   |   1 +
+ drivers/mtd/nmbm/nmbm-mtd.c | 890 ++++++++++++++++++++++++++++++++++++
+ include/nmbm/nmbm-mtd.h     |  27 ++
+ 4 files changed, 923 insertions(+)
+ create mode 100644 drivers/mtd/nmbm/nmbm-mtd.c
+ create mode 100644 include/nmbm/nmbm-mtd.h
+
+--- a/drivers/mtd/nmbm/Kconfig
++++ b/drivers/mtd/nmbm/Kconfig
+@@ -27,3 +27,8 @@ config NMBM_LOG_LEVEL_NONE
+       bool "5 - None"
+ endchoice
++
++config NMBM_MTD
++      bool "Enable MTD based NAND mapping block management"
++      default n
++      depends on NMBM
+--- a/drivers/mtd/nmbm/Makefile
++++ b/drivers/mtd/nmbm/Makefile
+@@ -3,3 +3,4 @@
+ # (C) Copyright 2020 MediaTek Inc. All rights reserved.
+ obj-$(CONFIG_NMBM) += nmbm-core.o
++obj-$(CONFIG_NMBM_MTD) += nmbm-mtd.o
+--- /dev/null
++++ b/drivers/mtd/nmbm/nmbm-mtd.c
+@@ -0,0 +1,890 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <linux/list.h>
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <jffs2/load_kernel.h>
++#include <watchdog.h>
++
++#include "nmbm-debug.h"
++
++#define NMBM_UPPER_MTD_NAME   "nmbm"
++
++static uint32_t nmbm_id_cnt;
++static LIST_HEAD(nmbm_devs);
++
++struct nmbm_mtd {
++      struct mtd_info upper;
++      char *name;
++      uint32_t id;
++
++      struct mtd_info *lower;
++
++      struct nmbm_instance *ni;
++      uint8_t *page_cache;
++
++      struct list_head node;
++};
++
++static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob,
++                              enum nmbm_oob_mode mode)
++{
++      struct nmbm_mtd *nm = arg;
++      struct mtd_oob_ops ops;
++      int ret;
++
++      memset(&ops, 0, sizeof(ops));
++
++      switch (mode) {
++      case NMBM_MODE_PLACE_OOB:
++              ops.mode = MTD_OPS_PLACE_OOB;
++              break;
++      case NMBM_MODE_AUTO_OOB:
++              ops.mode = MTD_OPS_AUTO_OOB;
++              break;
++      case NMBM_MODE_RAW:
++              ops.mode = MTD_OPS_RAW;
++              break;
++      default:
++              pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode);
++              return -ENOTSUPP;
++      }
++
++      if (buf) {
++              ops.datbuf = buf;
++              ops.len = nm->lower->writesize;
++      }
++
++      if (oob) {
++              ops.oobbuf = oob;
++              ops.ooblen = mtd_oobavail(nm->lower, &ops);
++      }
++
++      ret = mtd_read_oob(nm->lower, addr, &ops);
++      nm->upper.ecc_stats.corrected = nm->lower->ecc_stats.corrected;
++      nm->upper.ecc_stats.failed = nm->lower->ecc_stats.failed;
++
++      /* Report error on failure (including ecc error) */
++      if (ret < 0 && ret != -EUCLEAN)
++              return ret;
++
++      /*
++       * Since mtd_read_oob() won't report exact bitflips, what we can know
++       * is whether bitflips exceeds the threshold.
++       * We want the -EUCLEAN to be passed to the upper layer, but not the
++       * error value itself. To achieve this, report bitflips above the
++       * threshold.
++       */
++
++      if (ret == -EUCLEAN) {
++              return min_t(u32, nm->lower->bitflip_threshold + 1,
++                           nm->lower->ecc_strength);
++      }
++
++      /* For bitflips less than the threshold, return 0 */
++
++      return 0;
++}
++
++static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf,
++                               const void *oob, enum nmbm_oob_mode mode)
++{
++      struct nmbm_mtd *nm = arg;
++      struct mtd_oob_ops ops;
++
++      memset(&ops, 0, sizeof(ops));
++
++      switch (mode) {
++      case NMBM_MODE_PLACE_OOB:
++              ops.mode = MTD_OPS_PLACE_OOB;
++              break;
++      case NMBM_MODE_AUTO_OOB:
++              ops.mode = MTD_OPS_AUTO_OOB;
++              break;
++      case NMBM_MODE_RAW:
++              ops.mode = MTD_OPS_RAW;
++              break;
++      default:
++              pr_debug("%s: unsupported NMBM mode: %u\n", __func__, mode);
++              return -ENOTSUPP;
++      }
++
++      if (buf) {
++              ops.datbuf = (uint8_t *)buf;
++              ops.len = nm->lower->writesize;
++      }
++
++      if (oob) {
++              ops.oobbuf = (uint8_t *)oob;
++              ops.ooblen = mtd_oobavail(nm->lower, &ops);
++      }
++
++      return mtd_write_oob(nm->lower, addr, &ops);
++}
++
++static int nmbm_lower_erase_block(void *arg, uint64_t addr)
++{
++      struct nmbm_mtd *nm = arg;
++      struct erase_info ei;
++
++      memset(&ei, 0, sizeof(ei));
++
++      ei.mtd = nm->lower;
++      ei.addr = addr;
++      ei.len = nm->lower->erasesize;
++
++      return mtd_erase(nm->lower, &ei);
++}
++
++static int nmbm_lower_is_bad_block(void *arg, uint64_t addr)
++{
++      struct nmbm_mtd *nm = arg;
++
++      return mtd_block_isbad(nm->lower, addr);
++}
++
++static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr)
++{
++      struct nmbm_mtd *nm = arg;
++
++      return mtd_block_markbad(nm->lower, addr);
++}
++
++static void nmbm_lower_log(void *arg, enum nmbm_log_category level,
++                         const char *fmt, va_list ap)
++{
++      vprintf(fmt, ap);
++}
++
++static int nmbm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
++                       size_t *retlen, u_char *buf)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++
++      /* Do not allow read past end of device */
++      if ((from + len) > mtd->size) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      return nmbm_read_range(nm->ni, from, len, buf, MTD_OPS_PLACE_OOB,
++                             retlen);
++}
++
++static int nmbm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
++                        size_t *retlen, const u_char *buf)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++
++      /* Do not allow write past end of device */
++      if ((to + len) > mtd->size) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      return nmbm_write_range(nm->ni, to, len, buf, MTD_OPS_PLACE_OOB,
++                              retlen);
++}
++
++static int nmbm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++      int ret;
++
++      instr->state = MTD_ERASING;
++
++      ret = nmbm_erase_block_range(nm->ni, instr->addr, instr->len,
++                                   &instr->fail_addr);
++      if (ret)
++              instr->state = MTD_ERASE_FAILED;
++      else
++              instr->state = MTD_ERASE_DONE;
++
++      if (!ret)
++              /* FIXME */
++              /* mtd_erase_callback(instr); */
++              return ret;
++      else
++              ret = -EIO;
++
++      return ret;
++}
++
++static int nmbm_mtd_read_data(struct nmbm_mtd *nm, uint64_t addr,
++                            struct mtd_oob_ops *ops, enum nmbm_oob_mode mode)
++{
++      size_t len, ooblen, maxooblen, chklen;
++      uint32_t col, ooboffs;
++      uint8_t *datcache, *oobcache;
++      bool has_ecc_err = false;
++      int ret, max_bitflips = 0;
++
++      col = addr & nm->lower->writesize_mask;
++      addr &= ~nm->lower->writesize_mask;
++      maxooblen = mtd_oobavail(nm->lower, ops);
++      ooboffs = ops->ooboffs;
++      ooblen = ops->ooblen;
++      len = ops->len;
++
++      datcache = len ? nm->page_cache : NULL;
++      oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL;
++
++      ops->oobretlen = 0;
++      ops->retlen = 0;
++
++      while (len || ooblen) {
++              WATCHDOG_RESET();
++
++              ret = nmbm_read_single_page(nm->ni, addr, datcache, oobcache,
++                                          mode);
++              if (ret < 0 && ret != -EBADMSG)
++                      return ret;
++
++              /* Continue reading on ecc error */
++              if (ret == -EBADMSG)
++                      has_ecc_err = true;
++
++              /* Record the maximum bitflips between pages */
++              if (ret > max_bitflips)
++                      max_bitflips = ret;
++
++              if (len) {
++                      /* Move data */
++                      chklen = nm->lower->writesize - col;
++                      if (chklen > len)
++                              chklen = len;
++
++                      memcpy(ops->datbuf + ops->retlen, datcache + col,
++                             chklen);
++                      len -= chklen;
++                      col = 0; /* (col + chklen) %  */
++                      ops->retlen += chklen;
++              }
++
++              if (ooblen) {
++                      /* Move oob */
++                      chklen = maxooblen - ooboffs;
++                      if (chklen > ooblen)
++                              chklen = ooblen;
++
++                      memcpy(ops->oobbuf + ops->oobretlen, oobcache + ooboffs,
++                             chklen);
++                      ooblen -= chklen;
++                      ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
++                      ops->oobretlen += chklen;
++              }
++
++              addr += nm->lower->writesize;
++      }
++
++      if (has_ecc_err)
++              return -EBADMSG;
++
++      return max_bitflips;
++}
++
++static int nmbm_mtd_read_oob(struct mtd_info *mtd, loff_t from,
++                           struct mtd_oob_ops *ops)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++      uint32_t maxooblen;
++      enum nmbm_oob_mode mode;
++
++      if (!ops->oobbuf && !ops->datbuf) {
++              if (ops->ooblen || ops->len)
++                      return -EINVAL;
++
++              return 0;
++      }
++
++      switch (ops->mode) {
++      case MTD_OPS_PLACE_OOB:
++              mode = NMBM_MODE_PLACE_OOB;
++              break;
++      case MTD_OPS_AUTO_OOB:
++              mode = NMBM_MODE_AUTO_OOB;
++              break;
++      case MTD_OPS_RAW:
++              mode = NMBM_MODE_RAW;
++              break;
++      default:
++              pr_debug("%s: unsupported oob mode: %u\n", __func__, ops->mode);
++              return -ENOTSUPP;
++      }
++
++      maxooblen = mtd_oobavail(mtd, ops);
++
++      /* Do not allow read past end of device */
++      if (ops->datbuf && (from + ops->len) > mtd->size) {
++              pr_debug("%s: attempt to read beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (!ops->oobbuf) {
++              /* Optimized for reading data only */
++              return nmbm_read_range(nm->ni, from, ops->len, ops->datbuf,
++                                     mode, &ops->retlen);
++      }
++
++      if (unlikely(ops->ooboffs >= maxooblen)) {
++              pr_debug("%s: attempt to start read outside oob\n",
++                      __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(from >= mtd->size ||
++          ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
++          (from >> mtd->writesize_shift)) * maxooblen)) {
++              pr_debug("%s: attempt to read beyond end of device\n",
++                              __func__);
++              return -EINVAL;
++      }
++
++      return nmbm_mtd_read_data(nm, from, ops, mode);
++}
++
++static int nmbm_mtd_write_data(struct nmbm_mtd *nm, uint64_t addr,
++                             struct mtd_oob_ops *ops, enum nmbm_oob_mode mode)
++{
++      size_t len, ooblen, maxooblen, chklen;
++      uint32_t col, ooboffs;
++      uint8_t *datcache, *oobcache;
++      int ret;
++
++      col = addr & nm->lower->writesize_mask;
++      addr &= ~nm->lower->writesize_mask;
++      maxooblen = mtd_oobavail(nm->lower, ops);
++      ooboffs = ops->ooboffs;
++      ooblen = ops->ooblen;
++      len = ops->len;
++
++      datcache = len ? nm->page_cache : NULL;
++      oobcache = ooblen ? nm->page_cache + nm->lower->writesize : NULL;
++
++      ops->oobretlen = 0;
++      ops->retlen = 0;
++
++      while (len || ooblen) {
++              WATCHDOG_RESET();
++
++              if (len) {
++                      /* Move data */
++                      chklen = nm->lower->writesize - col;
++                      if (chklen > len)
++                              chklen = len;
++
++                      memset(datcache, 0xff, col);
++                      memcpy(datcache + col, ops->datbuf + ops->retlen,
++                             chklen);
++                      memset(datcache + col + chklen, 0xff,
++                             nm->lower->writesize - col - chklen);
++                      len -= chklen;
++                      col = 0; /* (col + chklen) %  */
++                      ops->retlen += chklen;
++              }
++
++              if (ooblen) {
++                      /* Move oob */
++                      chklen = maxooblen - ooboffs;
++                      if (chklen > ooblen)
++                              chklen = ooblen;
++
++                      memset(oobcache, 0xff, ooboffs);
++                      memcpy(oobcache + ooboffs,
++                             ops->oobbuf + ops->oobretlen, chklen);
++                      memset(oobcache + ooboffs + chklen, 0xff,
++                             nm->lower->oobsize - ooboffs - chklen);
++                      ooblen -= chklen;
++                      ooboffs = 0; /* (ooboffs + chklen) % maxooblen; */
++                      ops->oobretlen += chklen;
++              }
++
++              ret = nmbm_write_single_page(nm->ni, addr, datcache, oobcache,
++                                           mode);
++              if (ret)
++                      return ret;
++
++              addr += nm->lower->writesize;
++      }
++
++      return 0;
++}
++
++static int nmbm_mtd_write_oob(struct mtd_info *mtd, loff_t to,
++                            struct mtd_oob_ops *ops)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++      enum nmbm_oob_mode mode;
++      uint32_t maxooblen;
++
++      if (!ops->oobbuf && !ops->datbuf) {
++              if (ops->ooblen || ops->len)
++                      return -EINVAL;
++
++              return 0;
++      }
++
++      switch (ops->mode) {
++      case MTD_OPS_PLACE_OOB:
++              mode = NMBM_MODE_PLACE_OOB;
++              break;
++      case MTD_OPS_AUTO_OOB:
++              mode = NMBM_MODE_AUTO_OOB;
++              break;
++      case MTD_OPS_RAW:
++              mode = NMBM_MODE_RAW;
++              break;
++      default:
++              pr_debug("%s: unsupported oob mode: %u\n", __func__,
++                       ops->mode);
++              return -ENOTSUPP;
++      }
++
++      maxooblen = mtd_oobavail(mtd, ops);
++
++      /* Do not allow write past end of device */
++      if (ops->datbuf && (to + ops->len) > mtd->size) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                       __func__);
++              return -EINVAL;
++      }
++
++      if (!ops->oobbuf) {
++              /* Optimized for writing data only */
++              return nmbm_write_range(nm->ni, to, ops->len, ops->datbuf,
++                                      mode, &ops->retlen);
++      }
++
++      if (unlikely(ops->ooboffs >= maxooblen)) {
++              pr_debug("%s: attempt to start write outside oob\n",
++                      __func__);
++              return -EINVAL;
++      }
++
++      if (unlikely(to >= mtd->size ||
++          ops->ooboffs + ops->ooblen > ((mtd->size >> mtd->writesize_shift) -
++          (to >> mtd->writesize_shift)) * maxooblen)) {
++              pr_debug("%s: attempt to write beyond end of device\n",
++                              __func__);
++              return -EINVAL;
++      }
++
++      return nmbm_mtd_write_data(nm, to, ops, mode);
++}
++
++static int nmbm_mtd_block_isbad(struct mtd_info *mtd, loff_t offs)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++
++      return nmbm_check_bad_block(nm->ni, offs);
++}
++
++static int nmbm_mtd_block_markbad(struct mtd_info *mtd, loff_t offs)
++{
++      struct nmbm_mtd *nm = container_of(mtd, struct nmbm_mtd, upper);
++
++      return nmbm_mark_bad_block(nm->ni, offs);
++}
++
++int nmbm_attach_mtd(struct mtd_info *lower, int flags, uint32_t max_ratio,
++                  uint32_t max_reserved_blocks, struct mtd_info **upper)
++{
++      struct nmbm_lower_device nld;
++      struct nmbm_instance *ni;
++      struct mtd_info *mtd;
++      struct nmbm_mtd *nm;
++      size_t namelen, alloc_size;
++      int ret;
++
++      if (!lower)
++              return -EINVAL;
++
++      if (lower->type != MTD_NANDFLASH || lower->flags != MTD_CAP_NANDFLASH)
++              return -ENOTSUPP;
++
++      namelen = strlen(NMBM_UPPER_MTD_NAME) + 16;
++
++      nm = calloc(sizeof(*nm) + lower->writesize + lower->oobsize + namelen + 1, 1);
++      if (!nm)
++              return -ENOMEM;
++
++      nm->lower = lower;
++      nm->name = (char *)nm + sizeof(*nm);
++      nm->page_cache = (uint8_t *)nm->name + namelen + 1;
++
++      nm->id = nmbm_id_cnt++;
++      snprintf(nm->name, namelen + 1, "%s%u", NMBM_UPPER_MTD_NAME, nm->id);
++
++      memset(&nld, 0, sizeof(nld));
++
++      nld.flags = flags;
++      nld.max_ratio = max_ratio;
++      nld.max_reserved_blocks = max_reserved_blocks;
++
++      nld.size = lower->size;
++      nld.erasesize = lower->erasesize;
++      nld.writesize = lower->writesize;
++      nld.oobsize = lower->oobsize;
++      nld.oobavail = lower->oobavail;
++
++      nld.arg = nm;
++      nld.read_page = nmbm_lower_read_page;
++      nld.write_page = nmbm_lower_write_page;
++      nld.erase_block = nmbm_lower_erase_block;
++      nld.is_bad_block = nmbm_lower_is_bad_block;
++      nld.mark_bad_block = nmbm_lower_mark_bad_block;
++
++      nld.logprint = nmbm_lower_log;
++
++      alloc_size = nmbm_calc_structure_size(&nld);
++      ni = calloc(alloc_size, 1);
++      if (!ni) {
++              free(nm);
++              return -ENOMEM;
++      }
++
++      ret = nmbm_attach(&nld, ni);
++      if (ret) {
++              free(ni);
++              free(nm);
++              return ret;
++      }
++
++      nm->ni = ni;
++
++      /* Initialize upper mtd */
++      mtd = &nm->upper;
++
++      mtd->name = nm->name;
++      mtd->type = MTD_DEV_TYPE_NMBM;
++      mtd->flags = lower->flags;
++
++      mtd->size = (uint64_t)ni->data_block_count * ni->lower.erasesize;
++      mtd->erasesize = lower->erasesize;
++      mtd->writesize = lower->writesize;
++      mtd->writebufsize = lower->writesize;
++      mtd->oobsize = lower->oobsize;
++      mtd->oobavail = lower->oobavail;
++
++      mtd->erasesize_shift = lower->erasesize_shift;
++      mtd->writesize_shift = lower->writesize_shift;
++      mtd->erasesize_mask = lower->erasesize_mask;
++      mtd->writesize_mask = lower->writesize_mask;
++
++      mtd->bitflip_threshold = lower->bitflip_threshold;
++
++      /* XXX: should this be duplicated? */
++      mtd->ooblayout = lower->ooblayout;
++      mtd->ecclayout = lower->ecclayout;
++
++      mtd->ecc_step_size = lower->ecc_step_size;
++      mtd->ecc_strength = lower->ecc_strength;
++
++      mtd->numeraseregions = lower->numeraseregions;
++      mtd->eraseregions = lower->eraseregions;
++
++      mtd->_read = nmbm_mtd_read;
++      mtd->_write = nmbm_mtd_write;
++      mtd->_erase = nmbm_mtd_erase;
++      mtd->_read_oob = nmbm_mtd_read_oob;
++      mtd->_write_oob = nmbm_mtd_write_oob;
++      mtd->_block_isbad = nmbm_mtd_block_isbad;
++      mtd->_block_markbad = nmbm_mtd_block_markbad;
++
++      *upper = mtd;
++
++      list_add_tail(&nm->node, &nmbm_devs);
++
++      return 0;
++}
++
++int nmbm_free_mtd(struct mtd_info *upper)
++{
++      struct nmbm_mtd *pos;
++
++      if (!upper)
++              return -EINVAL;
++
++      list_for_each_entry(pos, &nmbm_devs, node) {
++              if (&pos->upper == upper) {
++                      list_del(&pos->node);
++
++                      nmbm_detach(pos->ni);
++                      free(pos->ni);
++                      free(pos);
++
++                      return 0;
++              }
++      }
++
++      return -ENODEV;
++}
++
++struct mtd_info *nmbm_mtd_get_upper_by_index(uint32_t index)
++{
++      struct nmbm_mtd *nm;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (nm->id == index)
++                      return &nm->upper;
++      }
++
++      return NULL;
++}
++
++struct mtd_info *nmbm_mtd_get_upper(struct mtd_info *lower)
++{
++      struct nmbm_mtd *nm;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (nm->lower == lower)
++                      return &nm->upper;
++      }
++
++      return NULL;
++}
++
++void nmbm_mtd_list_devices(void)
++{
++      struct nmbm_mtd *nm;
++
++      printf("Index   NMBM device         Lower device\n");
++      printf("========================================\n");
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              printf("%-8u%-20s%s\n", nm->id, nm->name, nm->lower->name);
++      }
++}
++
++int nmbm_mtd_print_info(const char *name)
++{
++      struct nmbm_mtd *nm;
++      bool found = false;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (!strcmp(nm->name, name)) {
++                      found = true;
++                      break;
++              }
++      }
++
++      if (!found) {
++              printf("Error: NMBM device '%s' not found\n", name);
++              return -ENODEV;
++      }
++
++      printf("%s:\n", name);
++      printf("Total blocks:                  %u\n", nm->ni->block_count);
++      printf("Data blocks:                   %u\n", nm->ni->data_block_count);
++      printf("Management start block:        %u\n", nm->ni->mgmt_start_ba);
++      printf("Info table size:               0x%x\n", nm->ni->info_table_size);
++
++      if (nm->ni->main_table_ba)
++              printf("Main info table start block:   %u\n", nm->ni->main_table_ba);
++      else
++              printf("Main info table start block:   Not exist\n");
++
++      if (nm->ni->backup_table_ba)
++              printf("Backup info table start block: %u\n", nm->ni->backup_table_ba);
++      else
++              printf("Backup info table start block: Not exist\n");
++
++      printf("Signature block:               %u\n", nm->ni->signature_ba);
++      printf("Mapping blocks top address:    %u\n", nm->ni->mapping_blocks_top_ba);
++      printf("Mapping blocks limit address:  %u\n", nm->ni->mapping_blocks_ba);
++
++      return 0;
++}
++
++static const char nmbm_block_legends[] = {
++      [NMBM_BLOCK_GOOD_DATA] = '-',
++      [NMBM_BLOCK_GOOD_MGMT] = '+',
++      [NMBM_BLOCK_BAD] = 'B',
++      [NMBM_BLOCK_MAIN_INFO_TABLE] = 'I',
++      [NMBM_BLOCK_BACKUP_INFO_TABLE] = 'i',
++      [NMBM_BLOCK_REMAPPED] = 'M',
++      [NMBM_BLOCK_SIGNATURE] = 'S',
++};
++
++int nmbm_mtd_print_states(const char *name)
++{
++      struct nmbm_mtd *nm;
++      enum nmmb_block_type bt;
++      bool found = false;
++      uint32_t i;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (!strcmp(nm->name, name)) {
++                      found = true;
++                      break;
++              }
++      }
++
++      if (!found) {
++              printf("Error: NMBM device '%s' not found\n", name);
++              return -ENODEV;
++      }
++
++      printf("Physical blocks:\n");
++      printf("\n");
++
++      printf("Legends:\n");
++      printf("  -     Good data block\n");
++      printf("  +     Good management block\n");
++      printf("  B     Bad block\n");
++      printf("  I     Main info table\n");
++      printf("  i     Backup info table\n");
++      printf("  M     Remapped spare block\n");
++      printf("  S     Signature block\n");
++      printf("\n");
++
++      for (i = 0; i < nm->ni->block_count; i++) {
++              if (i % 64 == 0)
++                      printf("    ");
++
++              bt = nmbm_debug_get_phys_block_type(nm->ni, i);
++              if (bt < __NMBM_BLOCK_TYPE_MAX)
++                      putc(nmbm_block_legends[bt]);
++              else
++                      putc('?');
++
++              if (i % 64 == 63)
++                      printf("\n");
++      }
++
++      printf("\n");
++      printf("Logical blocks:\n");
++      printf("\n");
++
++      printf("Legends:\n");
++      printf("  -     Good block\n");
++      printf("  +     Initially remapped block\n");
++      printf("  M     Remapped block\n");
++      printf("  B     Bad/Unmapped block\n");
++      printf("\n");
++
++      for (i = 0; i < nm->ni->data_block_count; i++) {
++              if (i % 64 == 0)
++                      printf("    ");
++
++              if (nm->ni->block_mapping[i] < 0)
++                      putc('B');
++              else if (nm->ni->block_mapping[i] == i)
++                      putc('-');
++              else if (nm->ni->block_mapping[i] < nm->ni->data_block_count)
++                      putc('+');
++              else if (nm->ni->block_mapping[i] > nm->ni->mapping_blocks_top_ba &&
++                       nm->ni->block_mapping[i] < nm->ni->signature_ba)
++                      putc('M');
++              else
++                      putc('?');
++
++              if (i % 64 == 63)
++                      printf("\n");
++      }
++
++      return 0;
++}
++
++int nmbm_mtd_print_bad_blocks(const char *name)
++{
++      struct nmbm_mtd *nm;
++      bool found = false;
++      uint32_t i;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (!strcmp(nm->name, name)) {
++                      found = true;
++                      break;
++              }
++      }
++
++      if (!found) {
++              printf("Error: NMBM device '%s' not found\n", name);
++              return -ENODEV;
++      }
++
++      printf("Physical blocks:\n");
++
++      for (i = 0; i < nm->ni->block_count; i++) {
++              switch (nmbm_debug_get_block_state(nm->ni, i)) {
++              case BLOCK_ST_BAD:
++                      printf("%-12u [0x%08llx] - Bad\n", i,
++                             (uint64_t)i << nm->ni->erasesize_shift);
++                      break;
++              case BLOCK_ST_NEED_REMAP:
++                      printf("%-12u [0x%08llx] - Awaiting remapping\n", i,
++                             (uint64_t)i << nm->ni->erasesize_shift);
++                      break;
++              }
++      }
++
++      printf("\n");
++      printf("Logical blocks:\n");
++
++      for (i = 0; i < nm->ni->data_block_count; i++) {
++              if (nm->ni->block_mapping[i] < 0) {
++                      printf("%-12u [0x%08llx] - Bad\n", i,
++                             (uint64_t)i << nm->ni->erasesize_shift);
++              }
++      }
++
++      return 0;
++}
++
++int nmbm_mtd_print_mappings(const char *name, int printall)
++{
++      struct nmbm_mtd *nm;
++      bool found = false;
++      int32_t pb;
++      uint32_t i;
++
++      list_for_each_entry(nm, &nmbm_devs, node) {
++              if (!strcmp(nm->name, name)) {
++                      found = true;
++                      break;
++              }
++      }
++
++      if (!found) {
++              printf("Error: NMBM device '%s' not found\n", name);
++              return -ENODEV;
++      }
++
++      printf("Logical Block       Physical Block\n");
++      printf("==================================\n");
++
++      if (!printall) {
++              for (i = 0; i < nm->ni->data_block_count; i++) {
++                      pb = nm->ni->block_mapping[i];
++                      if (pb < 0)
++                              printf("%-20uUnmapped\n", i);
++                      else if ((uint32_t)pb > nm->ni->mapping_blocks_top_ba &&
++                              (uint32_t)pb < nm->ni->signature_ba)
++                              printf("%-20u%u\n", i, pb);
++              }
++
++              return 0;
++      }
++
++      for (i = 0; i < nm->ni->data_block_count; i++) {
++              pb = nm->ni->block_mapping[i];
++
++              if (pb >= 0)
++                      printf("%-20u%u\n", i, pb);
++              else
++                      printf("%-20uUnmapped\n", i);
++      }
++
++      return 0;
++}
+--- /dev/null
++++ b/include/nmbm/nmbm-mtd.h
+@@ -0,0 +1,27 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _NMBM_MTD_H_
++#define _NMBM_MTD_H_
++
++#include <linux/mtd/mtd.h>
++
++int nmbm_attach_mtd(struct mtd_info *lower, int flags, uint32_t max_ratio,
++                  uint32_t max_reserved_blocks, struct mtd_info **upper);
++
++int nmbm_free_mtd(struct mtd_info *upper);
++
++struct mtd_info *nmbm_mtd_get_upper_by_index(uint32_t index);
++struct mtd_info *nmbm_mtd_get_upper(struct mtd_info *lower);
++
++void nmbm_mtd_list_devices(void);
++int nmbm_mtd_print_info(const char *name);
++int nmbm_mtd_print_states(const char *name);
++int nmbm_mtd_print_bad_blocks(const char *name);
++int nmbm_mtd_print_mappings(const char *name, int printall);
++
++#endif /* _NMBM_MTD_H_ */
diff --git a/package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch b/package/boot/uboot-mediatek/patches/100-08-common-board_r-add-support-to-initialize-NMBM-after-.patch
new file mode 100644 (file)
index 0000000..3dab053
--- /dev/null
@@ -0,0 +1,46 @@
+From dcf24c8deeb43a4406ae18136c8700dc2f867415 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 11:18:03 +0800
+Subject: [PATCH 42/71] common: board_r: add support to initialize NMBM after
+ nand initialization
+
+This patch add support to initialize NMBM after nand initialized.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ common/board_r.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/common/board_r.c
++++ b/common/board_r.c
+@@ -382,6 +382,20 @@ static int initr_nand(void)
+ }
+ #endif
++#ifdef CONFIG_NMBM_MTD
++
++__weak int board_nmbm_init(void)
++{
++      return 0;
++}
++
++/* go init the NMBM */
++static int initr_nmbm(void)
++{
++      return board_nmbm_init();
++}
++#endif
++
+ #if defined(CONFIG_CMD_ONENAND)
+ /* go init the NAND */
+ static int initr_onenand(void)
+@@ -703,6 +717,9 @@ static init_fnc_t init_sequence_r[] = {
+ #ifdef CONFIG_CMD_ONENAND
+       initr_onenand,
+ #endif
++#ifdef CONFIG_NMBM_MTD
++      initr_nmbm,
++#endif
+ #ifdef CONFIG_MMC
+       initr_mmc,
+ #endif
diff --git a/package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch b/package/boot/uboot-mediatek/patches/100-09-cmd-add-nmbm-command.patch
new file mode 100644 (file)
index 0000000..e6e155b
--- /dev/null
@@ -0,0 +1,370 @@
+From 0af8d0aac77f4df4bc7dadbcdea5d9a16f5f3e45 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:44:57 +0800
+Subject: [PATCH 43/71] cmd: add nmbm command
+
+Add nmbm command for debugging, data operations and image-booting support
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/Kconfig  |   6 +
+ cmd/Makefile |   1 +
+ cmd/nmbm.c   | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 334 insertions(+)
+ create mode 100644 cmd/nmbm.c
+
+--- a/cmd/Kconfig
++++ b/cmd/Kconfig
+@@ -1260,6 +1260,12 @@ config CMD_NAND_TORTURE
+ endif # CMD_NAND
++config CMD_NMBM
++      depends on NMBM_MTD
++      bool "nmbm"
++      help
++        NAND mapping block management (NMBM) utility
++
+ config CMD_NVME
+       bool "nvme"
+       depends on NVME
+--- a/cmd/Makefile
++++ b/cmd/Makefile
+@@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o
+ endif
+ obj-$(CONFIG_CMD_MUX) += mux.o
+ obj-$(CONFIG_CMD_NAND) += nand.o
++obj-$(CONFIG_CMD_NMBM) += nmbm.o
+ obj-$(CONFIG_CMD_NET) += net.o
+ obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
+ obj-$(CONFIG_CMD_ONENAND) += onenand.o
+--- /dev/null
++++ b/cmd/nmbm.c
+@@ -0,0 +1,327 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <command.h>
++#include <image.h>
++#include <stdbool.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++#include <jffs2/load_kernel.h>
++
++#include <nmbm/nmbm-mtd.h>
++
++static int nmbm_parse_offset_size(struct mtd_info *mtd, char *off_str,
++                                char *size_str, uint64_t *off,
++                                uint64_t *size)
++{
++      char *end;
++
++      *off = simple_strtoull(off_str, &end, 16);
++      if (end == off_str) {
++              printf("Error: offset '%s' is invalid\n", off_str);
++              return -EINVAL;
++      }
++
++      if (*off >= mtd->size) {
++              printf("Error: offset '0x%llx' is beyond the end of device\n",
++                     *off);
++              return -EINVAL;
++      }
++
++      *size = simple_strtoull(size_str, &end, 16);
++      if (end == off_str) {
++              printf("Error: size '%s' is invalid\n", off_str);
++              return -EINVAL;
++      }
++
++      if (*off + *size > mtd->size) {
++              printf("Error: size '0x%llx' is too large\n", *size);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int do_nmbm_erase(struct mtd_info *mtd, uint64_t offset, uint64_t size)
++{
++      struct erase_info ei;
++      int ret;
++
++      memset(&ei, 0, sizeof(ei));
++
++      ei.mtd = mtd;
++      ei.addr = offset;
++      ei.len = size;
++
++      printf("Erasing from 0x%llx, size 0x%llx ...\n", offset, size);
++
++      ret = mtd_erase(mtd, &ei);
++
++      if (!ret) {
++              printf("Succeeded\n");
++              return CMD_RET_SUCCESS;
++      }
++
++      printf("Failed at 0x%llx\n", ei.fail_addr);
++
++      return CMD_RET_FAILURE;
++}
++
++static int do_nmbm_rw(int read, struct mtd_info *mtd, uintptr_t addr,
++             uint64_t offset, size_t size)
++{
++      size_t retlen;
++      int ret;
++
++      printf("%s 0x%llx, size 0x%zx\n", read ? "Reading from" : "Writing to",
++              offset, size);
++
++      if (read)
++              ret = mtd_read(mtd, offset, size, &retlen, (void *)addr);
++      else
++              ret = mtd_write(mtd, offset, size, &retlen, (void *)addr);
++
++      if (!ret) {
++              printf("Succeeded\n");
++              return CMD_RET_SUCCESS;
++      }
++
++      printf("Failed at 0x%llx\n", offset + retlen);
++
++      return CMD_RET_FAILURE;
++}
++
++static int do_nmbm_mtd_boot(struct cmd_tbl *cmdtp, struct mtd_info *mtd,
++                          int argc, char *const argv[])
++{
++      bool print_image_contents = true;
++      uintptr_t loadaddr = image_load_addr;
++      char *end, *image_name;
++      const char *ep;
++      size_t retlen;
++      uint32_t size;
++      uint64_t off;
++      int ret;
++
++#if defined(CONFIG_CMD_MTDPARTS)
++      struct mtd_device *partdev;
++      struct mtd_info *partmtd;
++      struct part_info *part;
++      u8 pnum;
++#endif
++
++      ep = env_get("autostart");
++
++      if (ep && !strcmp(ep, "yes"))
++              print_image_contents = false;
++
++      if (argc == 2) {
++              loadaddr = simple_strtoul(argv[0], &end, 0);
++              if (*end || end == argv[0]) {
++                      printf("'%s' is not a valid address\n", argv[0]);
++                      return CMD_RET_FAILURE;
++              }
++
++              argc--;
++              argv++;
++      }
++
++      off = simple_strtoull(argv[0], &end, 0);
++      if (*end || end == argv[0]) {
++#if defined(CONFIG_CMD_MTDPARTS)
++              ret = mtdparts_init();
++              if (ret)
++                      return CMD_RET_FAILURE;
++
++              ret = find_dev_and_part(argv[0], &partdev, &pnum, &part);
++              if (ret)
++                      return CMD_RET_FAILURE;
++
++              if (partdev->id->type != MTD_DEV_TYPE_NMBM) {
++                      printf("'%s' is not a NMBM device partition\n",
++                              argv[0]);
++                      return CMD_RET_FAILURE;
++              }
++
++              partmtd = nmbm_mtd_get_upper_by_index(partdev->id->num);
++
++              if (partmtd != mtd) {
++                      printf("'%s' does not belong to this device\n",
++                              argv[0]);
++                      return CMD_RET_FAILURE;
++              }
++
++              off = part->offset;
++#else
++              printf("'%s' is not a valid offset\n", argv[0]);
++              return CMD_RET_FAILURE;
++#endif
++      }
++
++      ret = mtd_read(mtd, off, sizeof(image_header_t), &retlen,
++                     (void *)loadaddr);
++      if (ret || retlen != sizeof(image_header_t)) {
++              printf("Failed to read NMBM at offset 0x%08llx\n", off);
++              return CMD_RET_FAILURE;
++      }
++
++      switch (genimg_get_format((void *)loadaddr)) {
++#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
++      case IMAGE_FORMAT_LEGACY:
++              size = image_get_image_size((image_header_t *)loadaddr);
++              image_name = "legacy";
++              break;
++#endif
++#if defined(CONFIG_FIT)
++      case IMAGE_FORMAT_FIT:
++              size = fit_get_size((const void *)loadaddr);
++              image_name = "FIT";
++              break;
++#endif
++      default:
++              printf("Error: no Image found at offset 0x%08llx\n", off);
++              return CMD_RET_FAILURE;
++      }
++
++      printf("Loading %s image at offset 0x%llx to memory 0x%08lx, size 0x%x ...\n",
++             image_name, off, loadaddr, size);
++
++      ret = mtd_read(mtd, off, size, &retlen, (void *)loadaddr);
++      if (ret || retlen != size) {
++              printf("Error: Failed to load image at offset 0x%08llx\n",
++                     off + retlen);
++              return CMD_RET_FAILURE;
++      }
++
++      switch (genimg_get_format((void *)loadaddr)) {
++#if defined(CONFIG_LEGACY_IMAGE_FORMAT)
++      case IMAGE_FORMAT_LEGACY:
++              if (print_image_contents)
++                      image_print_contents((void *)loadaddr);
++              break;
++#endif
++#if defined(CONFIG_FIT)
++      case IMAGE_FORMAT_FIT:
++              if (print_image_contents)
++                      fit_print_contents((void *)loadaddr);
++              break;
++#endif
++      }
++
++      image_load_addr = loadaddr;
++
++      return bootm_maybe_autostart(cmdtp, "nmbm");
++}
++
++static int do_nmbm(struct cmd_tbl *cmdtp, int flag, int argc,
++                 char *const argv[])
++{
++      struct mtd_info *mtd;
++      uint64_t offset, size;
++      char *end;
++      uintptr_t addr;
++      int ret, all = 0;
++
++      if (argc == 1)
++              return CMD_RET_USAGE;
++
++      if (!strcmp(argv[1], "list")) {
++              nmbm_mtd_list_devices();
++              return CMD_RET_SUCCESS;
++      }
++
++      if (argc < 3)
++              return CMD_RET_USAGE;
++
++      if (!strcmp(argv[2], "info"))
++              return !!nmbm_mtd_print_info(argv[1]);
++
++      if (!strcmp(argv[2], "state"))
++              return !!nmbm_mtd_print_states(argv[1]);
++
++      if (!strcmp(argv[2], "bad"))
++              return !!nmbm_mtd_print_bad_blocks(argv[1]);
++
++      if (!strcmp(argv[2], "mapping")) {
++              if (argc >= 4) {
++                      if (!strcmp(argv[3], "all"))
++                              all = 1;
++              }
++
++              return nmbm_mtd_print_mappings(argv[1], all);
++      }
++
++      if (argc < 4)
++              return CMD_RET_USAGE;
++
++      mtd = get_mtd_device_nm(argv[1]);
++      if (IS_ERR(mtd)) {
++              printf("Error: NMBM device '%s' not found\n", argv[1]);
++              return CMD_RET_FAILURE;
++      }
++
++      if (mtd->type != MTD_DEV_TYPE_NMBM) {
++              printf("Error: '%s' is not a NMBM device\n", argv[1]);
++              return CMD_RET_FAILURE;
++      }
++
++      if (!strcmp(argv[2], "boot"))
++              return do_nmbm_mtd_boot(cmdtp, mtd, argc - 3, argv + 3);
++
++      if (argc < 5)
++              return CMD_RET_USAGE;
++
++      if (!strcmp(argv[2], "erase")) {
++              ret = nmbm_parse_offset_size(mtd, argv[3], argv[4], &offset,
++                                           &size);
++              if (ret)
++                      return CMD_RET_FAILURE;
++
++              return do_nmbm_erase(mtd, offset, size);
++      }
++
++      if (argc < 6)
++              return CMD_RET_USAGE;
++
++      ret = nmbm_parse_offset_size(mtd, argv[4], argv[5], &offset, &size);
++      if (ret)
++              return CMD_RET_FAILURE;
++
++      if (size > SIZE_MAX) {
++              printf("Error: size 0x%llx is too large\n", size);
++              return -EINVAL;
++      }
++
++      addr = simple_strtoul(argv[3], &end, 16);
++      if (end == argv[3]) {
++              printf("Error: addr '%s' is invalid\n", argv[3]);
++              return -EINVAL;
++      }
++
++      if (!strcmp(argv[2], "read"))
++              return do_nmbm_rw(1, mtd, addr, offset, (size_t)size);
++
++      if (!strcmp(argv[2], "write"))
++              return do_nmbm_rw(0, mtd, addr, offset, (size_t)size);
++
++      return CMD_RET_USAGE;
++}
++
++U_BOOT_CMD(
++      nmbm,   CONFIG_SYS_MAXARGS,     0,      do_nmbm,
++      "NMBM utility commands",
++      "\n"
++      "nmbm list                                - List NMBM devices\n"
++      "nmbm <name> info                         - Display NMBM information\n"
++      "nmbm <name> state                        - Display block states\n"
++      "nmbm <name> bad                          - Display bad blocks\n"
++      "nmbm <name> boot <part | [loadaddr] offset>  - Boot from NMBM\n"
++      "nmbm <name> mapping [all]                - Display block mapping\n"
++      "nmbm <name> erase <offset> <size>        - Erase blocks\n"
++      "nmbm <name> read <addr> <offset> <size>  - Read data\n"
++      "nmbm <name> write <addr> <offset> <size> - Write data\n"
++);
diff --git a/package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch b/package/boot/uboot-mediatek/patches/100-10-cmd-mtd-add-markbad-subcommand-for-NMBM-testing.patch
new file mode 100644 (file)
index 0000000..6e38ec4
--- /dev/null
@@ -0,0 +1,80 @@
+From 6dbbc8affb6ab22f940d13d0e928d5e881127ca4 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 11:22:57 +0800
+Subject: [PATCH 44/71] cmd: mtd: add markbad subcommand for NMBM testing
+
+This patch adds:
+* Mark bad block on lower mtd device and erase on upper mtd
+device, which will trigger remapping:
+$ mtd markbad spi-nand0 0x20000 (mark block1 as bad)
+$ mtd erase nmbm0 0x20000 0x20000 (let nmbm detect the bad block and remap it)
+
+* Clear bad block mark through:
+$ mtd erase.dontskipbad spi-nand0 0x20000 0x20000
+(After cleaning bad block mark, we need to rebuild nmbm manage table.)
+
+Signed-off-by: SkyLake.Huang <skylake.huang@mediatek.com>
+---
+ cmd/mtd.c | 39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/cmd/mtd.c
++++ b/cmd/mtd.c
+@@ -492,6 +492,42 @@ out_put_mtd:
+       return CMD_RET_SUCCESS;
+ }
++static int do_mtd_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
++                        char * const argv[])
++{
++      struct mtd_info *mtd;
++      loff_t off;
++      int ret;
++
++      if (argc < 3)
++              return CMD_RET_USAGE;
++
++      mtd = get_mtd_by_name(argv[1]);
++      if (IS_ERR(mtd) || !mtd)
++              return CMD_RET_FAILURE;
++
++      if (!mtd_can_have_bb(mtd)) {
++              printf("Only NAND-based devices can have mark blocks\n");
++              goto out_put_mtd;
++      }
++
++      off = simple_strtoull(argv[2], NULL, 0);
++
++      ret = mtd_block_markbad(mtd, off);
++      if (!ret) {
++              printf("MTD device %s block at 0x%08llx marked bad\n",
++                     mtd->name, off);
++      } else {
++              printf("MTD device %s block at 0x%08llx mark bad failed\n",
++                     mtd->name, off);
++      }
++
++out_put_mtd:
++      put_mtd_device(mtd);
++
++      return CMD_RET_SUCCESS;
++}
++
+ #ifdef CONFIG_AUTO_COMPLETE
+ static int mtd_name_complete(int argc, char *const argv[], char last_char,
+                            int maxv, char *cmdv[])
+@@ -540,6 +576,7 @@ static char mtd_help_text[] =
+       "\n"
+       "Specific functions:\n"
+       "mtd bad                               <name>\n"
++      "mtd markbad                           <name> <off>\n"
+       "\n"
+       "With:\n"
+       "\t<name>: NAND partition/chip name (or corresponding DM device name or OF path)\n"
+@@ -565,4 +602,6 @@ U_BOOT_CMD_WITH_SUBCMDS(mtd, "MTD utils"
+               U_BOOT_SUBCMD_MKENT_COMPLETE(erase, 4, 0, do_mtd_erase,
+                                            mtd_name_complete),
+               U_BOOT_SUBCMD_MKENT_COMPLETE(bad, 2, 1, do_mtd_bad,
++                                           mtd_name_complete),
++              U_BOOT_SUBCMD_MKENT_COMPLETE(markbad, 3, 1, do_mtd_markbad,
+                                            mtd_name_complete));
diff --git a/package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch b/package/boot/uboot-mediatek/patches/100-11-env-add-support-for-NMBM-upper-MTD-layer.patch
new file mode 100644 (file)
index 0000000..2791332
--- /dev/null
@@ -0,0 +1,280 @@
+From 240d98e6ad0aed3c11236aa40a60bbd6fe01fae5 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:50:46 +0800
+Subject: [PATCH 45/71] env: add support for NMBM upper MTD layer
+
+Add an env driver for NMBM upper MTD layer
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/nvedit.c           |   3 +-
+ env/Kconfig            |  19 ++++-
+ env/Makefile           |   1 +
+ env/env.c              |   3 +
+ env/nmbm.c             | 155 +++++++++++++++++++++++++++++++++++++++++
+ include/env_internal.h |   1 +
+ tools/Makefile         |   1 +
+ 7 files changed, 180 insertions(+), 3 deletions(-)
+ create mode 100644 env/nmbm.c
+
+--- a/cmd/nvedit.c
++++ b/cmd/nvedit.c
+@@ -50,6 +50,7 @@ DECLARE_GLOBAL_DATA_PTR;
+       defined(CONFIG_ENV_IS_IN_EXT4)          || \
+       defined(CONFIG_ENV_IS_IN_MTD)           || \
+       defined(CONFIG_ENV_IS_IN_NAND)          || \
++      defined(CONFIG_ENV_IS_IN_NMBM)          || \
+       defined(CONFIG_ENV_IS_IN_NVRAM)         || \
+       defined(CONFIG_ENV_IS_IN_ONENAND)       || \
+       defined(CONFIG_ENV_IS_IN_SATA)          || \
+@@ -64,7 +65,7 @@ DECLARE_GLOBAL_DATA_PTR;
+ #if   !defined(ENV_IS_IN_DEVICE)              && \
+       !defined(CONFIG_ENV_IS_NOWHERE)
+ # error Define one of CONFIG_ENV_IS_IN_{EEPROM|FLASH|MMC|FAT|EXT4|MTD|\
+-NAND|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
++NAND|NMBM|NVRAM|ONENAND|SATA|SPI_FLASH|REMOTE|UBI} or CONFIG_ENV_IS_NOWHERE
+ #endif
+ /*
+--- a/env/Kconfig
++++ b/env/Kconfig
+@@ -37,7 +37,7 @@ config ENV_IS_NOWHERE
+                    !ENV_IS_IN_MMC && !ENV_IS_IN_NAND && \
+                    !ENV_IS_IN_NVRAM && !ENV_IS_IN_ONENAND && \
+                    !ENV_IS_IN_REMOTE && !ENV_IS_IN_SPI_FLASH && \
+-                   !ENV_IS_IN_UBI && !ENV_IS_IN_MTD
++                   !ENV_IS_IN_UBI && !ENV_IS_IN_NMBM && !ENV_IS_IN_MTD
+       help
+         Define this if you don't want to or can't have an environment stored
+         on a storage medium. In this case the environment will still exist
+@@ -285,6 +285,21 @@ config ENV_IS_IN_NAND
+         Currently, CONFIG_ENV_OFFSET_REDUND is not supported when
+         using CONFIG_ENV_OFFSET_OOB.
++config ENV_IS_IN_NMBM
++      bool "Environment in a NMBM upper MTD layer"
++      depends on !CHAIN_OF_TRUST
++      depends on NMBM_MTD
++      help
++        Define this if you have a NMBM upper MTD which you want to use for
++        the environment.
++
++        - CONFIG_ENV_OFFSET:
++        - CONFIG_ENV_SIZE:
++
++        These two #defines specify the offset and size of the environment
++        area within the first NAND device.  CONFIG_ENV_OFFSET must be
++        aligned to an erase block boundary.
++
+ config ENV_IS_IN_NVRAM
+       bool "Environment in a non-volatile RAM"
+       depends on !CHAIN_OF_TRUST
+@@ -561,7 +576,7 @@ config ENV_MTD_NAME
+ config ENV_OFFSET
+       hex "Environment offset"
+       depends on ENV_IS_IN_EEPROM || ENV_IS_IN_MMC || ENV_IS_IN_NAND || \
+-                  ENV_IS_IN_SPI_FLASH || ENV_IS_IN_MTD
++                  ENV_IS_IN_SPI_FLASH || ENV_IS_IN_NMBM || ENV_IS_IN_MTD
+       default 0x3f8000 if ARCH_ROCKCHIP && ENV_IS_IN_MMC
+       default 0x140000 if ARCH_ROCKCHIP && ENV_IS_IN_SPI_FLASH
+       default 0xF0000 if ARCH_SUNXI
+--- a/env/Makefile
++++ b/env/Makefile
+@@ -28,6 +28,7 @@ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FAT) +
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_EXT4) += ext4.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_MTD) += mtd.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NAND) += nand.o
++obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_NMBM) += nmbm.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_SPI_FLASH) += sf.o
+ obj-$(CONFIG_$(SPL_TPL_)ENV_IS_IN_FLASH) += flash.o
+--- a/env/env.c
++++ b/env/env.c
+@@ -75,6 +75,9 @@ static enum env_location env_locations[]
+ #ifdef CONFIG_ENV_IS_IN_NAND
+       ENVL_NAND,
+ #endif
++#ifdef CONFIG_ENV_IS_IN_NMBM
++      ENVL_NMBM,
++#endif
+ #ifdef CONFIG_ENV_IS_IN_NVRAM
+       ENVL_NVRAM,
+ #endif
+--- /dev/null
++++ b/env/nmbm.c
+@@ -0,0 +1,155 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2020 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <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 <malloc.h>
++#include <memalign.h>
++#include <search.h>
++
++#include <nmbm/nmbm-mtd.h>
++
++#if defined(CONFIG_CMD_SAVEENV) && defined(CONFIG_NMBM_MTD)
++#define CMD_SAVEENV
++#endif
++
++#if defined(ENV_IS_EMBEDDED)
++env_t *env_ptr = &environment;
++#else /* ! ENV_IS_EMBEDDED */
++env_t *env_ptr;
++#endif /* ENV_IS_EMBEDDED */
++
++DECLARE_GLOBAL_DATA_PTR;
++
++static int env_nmbm_init(void)
++{
++#if defined(ENV_IS_EMBEDDED)
++      int crc1_ok = 0, crc2_ok = 0;
++      env_t *tmp_env1;
++
++      tmp_env1 = env_ptr;
++      crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc;
++
++      if (!crc1_ok && !crc2_ok) {
++              gd->env_addr    = 0;
++              gd->env_valid   = ENV_INVALID;
++
++              return 0;
++      } else if (crc1_ok && !crc2_ok) {
++              gd->env_valid = ENV_VALID;
++      }
++
++      if (gd->env_valid == ENV_VALID)
++              env_ptr = tmp_env1;
++
++      gd->env_addr = (ulong)env_ptr->data;
++
++#else /* ENV_IS_EMBEDDED */
++      gd->env_addr    = (ulong)&default_environment[0];
++      gd->env_valid   = ENV_VALID;
++#endif /* ENV_IS_EMBEDDED */
++
++      return 0;
++}
++
++#ifdef CMD_SAVEENV
++static int env_nmbm_save(void)
++{
++      ALLOC_CACHE_ALIGN_BUFFER(env_t, env_new, 1);
++      struct mtd_info *mtd;
++      struct erase_info ei;
++      int ret = 0;
++
++      ret = env_export(env_new);
++      if (ret)
++              return ret;
++
++      mtd = nmbm_mtd_get_upper_by_index(0);
++      if (!mtd)
++              return 1;
++
++      printf("Erasing on NMBM...\n");
++      memset(&ei, 0, sizeof(ei));
++
++      ei.mtd = mtd;
++      ei.addr = CONFIG_ENV_OFFSET;
++      ei.len = CONFIG_ENV_SIZE;
++
++      if (mtd_erase(mtd, &ei))
++              return 1;
++
++      printf("Writing on NMBM... ");
++      ret = mtd_write(mtd, CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, NULL,
++                      (u_char *)env_new);
++      puts(ret ? "FAILED!\n" : "OK\n");
++
++      return !!ret;
++}
++#endif /* CMD_SAVEENV */
++
++static int readenv(size_t offset, u_char *buf)
++{
++      struct mtd_info *mtd;
++      struct mtd_oob_ops ops;
++      int ret;
++      size_t len = CONFIG_ENV_SIZE;
++
++      mtd = nmbm_mtd_get_upper_by_index(0);
++      if (!mtd)
++              return 1;
++
++      ops.mode = MTD_OPS_AUTO_OOB;
++      ops.ooblen = 0;
++      while(len > 0) {
++              ops.datbuf = buf;
++              ops.len = min(len, (size_t)mtd->writesize);
++              ops.oobbuf = NULL;
++
++              ret = mtd_read_oob(mtd, offset, &ops);
++              if (ret)
++                      return 1;
++
++              buf += mtd->writesize;
++              len -= mtd->writesize;
++              offset += mtd->writesize;
++      }
++
++      return 0;
++}
++
++static int env_nmbm_load(void)
++{
++#if !defined(ENV_IS_EMBEDDED)
++      ALLOC_CACHE_ALIGN_BUFFER(char, buf, CONFIG_ENV_SIZE);
++      int ret;
++
++      ret = readenv(CONFIG_ENV_OFFSET, (u_char *)buf);
++      if (ret) {
++              env_set_default("readenv() failed", 0);
++              return -EIO;
++      }
++
++      return env_import(buf, 1, H_EXTERNAL);
++#endif /* ! ENV_IS_EMBEDDED */
++
++      return 0;
++}
++
++U_BOOT_ENV_LOCATION(nmbm) = {
++      .location       = ENVL_NMBM,
++      ENV_NAME("NMBM")
++      .load           = env_nmbm_load,
++#if defined(CMD_SAVEENV)
++      .save           = env_save_ptr(env_nmbm_save),
++#endif
++      .init           = env_nmbm_init,
++};
+--- a/include/env_internal.h
++++ b/include/env_internal.h
+@@ -132,6 +132,7 @@ enum env_location {
+       ENVL_MMC,
+       ENVL_MTD,
+       ENVL_NAND,
++      ENVL_NMBM,
+       ENVL_NVRAM,
+       ENVL_ONENAND,
+       ENVL_REMOTE,
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -43,6 +43,7 @@ ENVCRC-$(CONFIG_ENV_IS_IN_FLASH) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_ONENAND) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_MTD) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_NAND) = y
++ENVCRC-$(CONFIG_ENV_IS_IN_NMBM) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_NVRAM) = y
+ ENVCRC-$(CONFIG_ENV_IS_IN_SPI_FLASH) = y
+ CONFIG_BUILD_ENVCRC ?= $(ENVCRC-y)
diff --git a/package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch b/package/boot/uboot-mediatek/patches/100-12-mtd-mtk-snand-add-NMBM-support-for-SPL.patch
new file mode 100644 (file)
index 0000000..32b21be
--- /dev/null
@@ -0,0 +1,173 @@
+From 9e8ac4fc7125795ac5e8834aaf454fd45b99c580 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:53:03 +0800
+Subject: [PATCH 46/71] mtd: mtk-snand: add NMBM support for SPL
+
+Add NMBM support for mtk-snand SPL loader
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/mtk-snand/mtk-snand-spl.c | 127 ++++++++++++++++++++++++++
+ 1 file changed, 127 insertions(+)
+
+--- a/drivers/mtd/mtk-snand/mtk-snand-spl.c
++++ b/drivers/mtd/mtk-snand/mtk-snand-spl.c
+@@ -13,12 +13,134 @@
+ #include <mtd.h>
+ #include <watchdog.h>
++#include <nmbm/nmbm.h>
++
+ #include "mtk-snand.h"
+ static struct mtk_snand *snf;
+ static struct mtk_snand_chip_info cinfo;
+ static u32 oobavail;
++#ifdef CONFIG_ENABLE_NAND_NMBM
++static struct nmbm_instance *ni;
++
++static int nmbm_lower_read_page(void *arg, uint64_t addr, void *buf, void *oob,
++                              enum nmbm_oob_mode mode)
++{
++      int ret;
++      bool raw = mode == NMBM_MODE_RAW ? true : false;
++
++      if (mode == NMBM_MODE_AUTO_OOB) {
++              ret = mtk_snand_read_page_auto_oob(snf, addr, buf, oob,
++                      oobavail, NULL, false);
++      } else {
++              ret = mtk_snand_read_page(snf, addr, buf, oob, raw);
++      }
++
++      if (ret == -EBADMSG)
++              return 1;
++      else if (ret >= 0)
++              return 0;
++
++      return ret;
++}
++
++static int nmbm_lower_write_page(void *arg, uint64_t addr, const void *buf,
++                               const void *oob, enum nmbm_oob_mode mode)
++{
++      bool raw = mode == NMBM_MODE_RAW ? true : false;
++
++      if (mode == NMBM_MODE_AUTO_OOB) {
++              return mtk_snand_write_page_auto_oob(snf, addr, buf, oob,
++                      oobavail, NULL, false);
++      }
++
++      return mtk_snand_write_page(snf, addr, buf, oob, raw);
++}
++
++static int nmbm_lower_erase_block(void *arg, uint64_t addr)
++{
++      return mtk_snand_erase_block(snf, addr);
++}
++
++static int nmbm_lower_is_bad_block(void *arg, uint64_t addr)
++{
++      return mtk_snand_block_isbad(snf, addr);
++}
++
++static int nmbm_lower_mark_bad_block(void *arg, uint64_t addr)
++{
++      return mtk_snand_block_markbad(snf, addr);
++}
++
++static void nmbm_lower_log(void *arg, enum nmbm_log_category level,
++                         const char *fmt, va_list ap)
++{
++      vprintf(fmt, ap);
++}
++
++static int nmbm_init(void)
++{
++      struct nmbm_lower_device nld;
++      size_t ni_size;
++      int ret;
++
++      memset(&nld, 0, sizeof(nld));
++
++      nld.flags = NMBM_F_CREATE;
++      nld.max_ratio = CONFIG_NMBM_MAX_RATIO;
++      nld.max_reserved_blocks = CONFIG_NMBM_MAX_BLOCKS;
++
++      nld.size = cinfo.chipsize;
++      nld.erasesize = cinfo.blocksize;
++      nld.writesize = cinfo.pagesize;
++      nld.oobsize = cinfo.sparesize;
++      nld.oobavail = oobavail;
++
++      nld.read_page = nmbm_lower_read_page;
++      nld.write_page = nmbm_lower_write_page;
++      nld.erase_block = nmbm_lower_erase_block;
++      nld.is_bad_block = nmbm_lower_is_bad_block;
++      nld.mark_bad_block = nmbm_lower_mark_bad_block;
++
++      nld.logprint = nmbm_lower_log;
++
++      ni_size = nmbm_calc_structure_size(&nld);
++      ni = malloc(ni_size);
++      if (!ni) {
++              printf("Failed to allocate memory (0x%u) for NMBM instance\n",
++                     ni_size);
++              return -ENOMEM;
++      }
++
++      memset(ni, 0, ni_size);
++
++      printf("Initializing NMBM ...\n");
++
++      ret = nmbm_attach(&nld, ni);
++      if (ret) {
++              ni = NULL;
++              return ret;
++      }
++
++      return 0;
++}
++
++int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
++{
++      size_t retlen;
++
++      if (!ni)
++              return -ENODEV;
++
++      nmbm_read_range(ni, offs, size, dst, NMBM_MODE_PLACE_OOB, &retlen);
++      if (retlen != size)
++              return -EIO;
++
++      return 0;
++}
++
++#else
+ static u8 *page_cache;
+ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
+@@ -60,6 +182,7 @@ int nand_spl_load_image(uint32_t offs, u
+       return ret;
+ }
++#endif
+ void nand_init(void)
+ {
+@@ -105,11 +228,15 @@ void nand_init(void)
+       printf("SPI-NAND: %s (%uMB)\n", cinfo.model,
+              (u32)(cinfo.chipsize >> 20));
++#ifdef CONFIG_ENABLE_NAND_NMBM
++      nmbm_init();
++#else
+       page_cache = malloc(cinfo.pagesize + cinfo.sparesize);
+       if (!page_cache) {
+               mtk_snand_cleanup(snf);
+               printf("mtk-snand-spl: failed to allocate page cache\n");
+       }
++#endif
+ }
+ void nand_deselect(void)
diff --git a/package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch b/package/boot/uboot-mediatek/patches/100-13-cmd-add-a-new-command-for-NAND-flash-debugging.patch
new file mode 100644 (file)
index 0000000..bf4ed97
--- /dev/null
@@ -0,0 +1,1118 @@
+From 88271cb3ae9c68dc200d627653df96fc557c2a64 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 10:55:35 +0800
+Subject: [PATCH 47/71] cmd: add a new command for NAND flash debugging
+
+Add a command 'nand-ext' for NAND flash debugging:
+- Dump a page with oob, with optional raw read support
+- Display all bad blocks
+- Mark a block as bad block
+- Set a bitflip on a page
+- Erase
+- Read / write data from/to any offset with any size
+- Read / write pages with oob
+- Erase, read and write support skip bad block or forced mode, support
+  raw mode, supporot auto-oob mode
+- Supports operating on a specific partition
+- No need to specify NAND device name
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/Kconfig    |    8 +
+ cmd/Makefile   |    1 +
+ cmd/nand-ext.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1071 insertions(+)
+ create mode 100644 cmd/nand-ext.c
+
+--- a/cmd/Kconfig
++++ b/cmd/Kconfig
+@@ -1260,6 +1260,14 @@ config CMD_NAND_TORTURE
+ endif # CMD_NAND
++config CMD_NAND_EXT
++      bool "nand - extended nand utility for debugging"
++      depends on !CMD_NAND
++      default y if MTD_RAW_NAND || MTD_SPI_NAND || MTK_SPI_NAND
++      select MTD_PARTITIONS
++      help
++        NAND flash R/W and debugging support.
++
+ config CMD_NMBM
+       depends on NMBM_MTD
+       bool "nmbm"
+--- a/cmd/Makefile
++++ b/cmd/Makefile
+@@ -114,6 +114,7 @@ obj-y += legacy-mtd-utils.o
+ endif
+ obj-$(CONFIG_CMD_MUX) += mux.o
+ obj-$(CONFIG_CMD_NAND) += nand.o
++obj-$(CONFIG_CMD_NAND_EXT) += nand-ext.o
+ obj-$(CONFIG_CMD_NMBM) += nmbm.o
+ obj-$(CONFIG_CMD_NET) += net.o
+ obj-$(CONFIG_CMD_NVEDIT_EFI) += nvedit_efi.o
+--- /dev/null
++++ b/cmd/nand-ext.c
+@@ -0,0 +1,1062 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (C) 2021 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <command.h>
++#include <stdbool.h>
++#include <malloc.h>
++#include <mtd.h>
++#include <dm/devres.h>
++#include <linux/types.h>
++#include <linux/mtd/mtd.h>
++
++static struct mtd_info *curr_dev;
++
++static void mtd_show_parts(struct mtd_info *mtd, int level)
++{
++      struct mtd_info *part;
++      int i;
++
++      list_for_each_entry(part, &mtd->partitions, node) {
++              for (i = 0; i < level; i++)
++                      printf("\t");
++              printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
++                     part->offset, part->offset + part->size, part->name);
++
++              mtd_show_parts(part, level + 1);
++      }
++}
++
++static void mtd_show_device(struct mtd_info *mtd)
++{
++      /* Device */
++      printf("* %s\n", mtd->name);
++#if defined(CONFIG_DM)
++      if (mtd->dev) {
++              printf("  - device: %s\n", mtd->dev->name);
++              printf("  - parent: %s\n", mtd->dev->parent->name);
++              printf("  - driver: %s\n", mtd->dev->driver->name);
++      }
++#endif
++
++      /* MTD device information */
++      printf("  - type: ");
++      switch (mtd->type) {
++      case MTD_NANDFLASH:
++              printf("NAND flash\n");
++              break;
++      case MTD_MLCNANDFLASH:
++              printf("MLC NAND flash\n");
++              break;
++      case MTD_ABSENT:
++      default:
++              printf("Not supported\n");
++              break;
++      }
++
++      printf("  - block size:        0x%x bytes\n", mtd->erasesize);
++      printf("  - page size:         0x%x bytes\n", mtd->writesize);
++      printf("  - OOB size:          %u bytes\n", mtd->oobsize);
++      printf("  - OOB available:     %u bytes\n", mtd->oobavail);
++
++      if (mtd->ecc_strength) {
++              printf("  - ECC strength:      %u bits\n", mtd->ecc_strength);
++              printf("  - ECC step size:     %u bytes\n", mtd->ecc_step_size);
++              printf("  - bitflip threshold: %u bits\n",
++                     mtd->bitflip_threshold);
++      }
++
++      printf("  - 0x%012llx-0x%012llx : \"%s\"\n",
++             mtd->offset, mtd->offset + mtd->size, mtd->name);
++
++      /* MTD partitions, if any */
++      mtd_show_parts(mtd, 1);
++}
++
++static int do_nand_list(struct cmd_tbl *cmdtp, int flag, int argc,
++                       char *const argv[])
++{
++      struct mtd_info *mtd;
++      int dev_nb = 0;
++
++      /* Ensure all devices (and their partitions) are probed */
++      mtd_probe_devices();
++
++      printf("List of NAND devices:\n");
++      mtd_for_each_device(mtd) {
++              if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
++                      continue;
++
++              if (!mtd_is_partition(mtd))
++                      mtd_show_device(mtd);
++
++              dev_nb++;
++      }
++
++      if (!dev_nb)
++              printf("No NAND MTD device found\n");
++
++      return CMD_RET_SUCCESS;
++}
++
++static struct mtd_info *nand_get_curr_dev(void)
++{
++      struct mtd_info *mtd, *first_dev = NULL;
++      int err, dev_nb = 0;
++
++      if (curr_dev) {
++              mtd = get_mtd_device(curr_dev, -1);
++              if (!IS_ERR_OR_NULL(mtd)) {
++                      __put_mtd_device(mtd);
++                      return mtd;
++              }
++
++              curr_dev = NULL;
++      }
++
++      /* Ensure all devices (and their partitions) are probed */
++      mtd_probe_devices();
++
++      mtd_for_each_device(mtd) {
++              if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH)
++                      continue;
++
++              if (!mtd_is_partition(mtd)) {
++                      if (!first_dev)
++                              first_dev = mtd;
++                      dev_nb++;
++              }
++      }
++
++      if (!dev_nb) {
++              printf("No NAND MTD device found\n");
++              return NULL;
++      }
++
++      if (dev_nb > 1) {
++              printf("No active NAND MTD device specified\n");
++              return NULL;
++      }
++
++      err = __get_mtd_device(first_dev);
++      if (err) {
++              printf("Failed to get MTD device '%s': err %d\n",
++                     first_dev->name, err);
++              return NULL;
++      }
++
++      curr_dev = first_dev;
++
++      printf("'%s' is now active device\n", first_dev->name);
++
++      return curr_dev;
++}
++
++static struct mtd_info *nand_get_part(struct mtd_info *master,
++                                     const char *name)
++{
++      struct mtd_info *slave;
++
++      list_for_each_entry(slave, &master->partitions, node) {
++              if (!strcmp(slave->name, name))
++                      return slave;
++      }
++
++      return NULL;
++}
++
++static int do_nand_info(struct cmd_tbl *cmdtp, int flag, int argc,
++                       char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev();
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      mtd_show_device(mtd);
++
++      return 0;
++}
++
++static int do_nand_select(struct cmd_tbl *cmdtp, int flag, int argc,
++                         char *const argv[])
++{
++      struct mtd_info *mtd, *old;
++
++      if (argc < 2) {
++              printf("MTD device name must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      mtd = get_mtd_device_nm(argv[1]);
++      if (!mtd) {
++              printf("MTD device '%s' not found\n", argv[1]);
++              return CMD_RET_FAILURE;
++      }
++
++      if (mtd_is_partition(mtd)) {
++              printf("Error: '%s' is a MTD partition\n", argv[1]);
++              __put_mtd_device(mtd);
++              return CMD_RET_FAILURE;
++      }
++
++      if (mtd->type != MTD_NANDFLASH && mtd->type != MTD_MLCNANDFLASH) {
++              printf("Error: '%s' is not a NAND device\n", argv[1]);
++              __put_mtd_device(mtd);
++              return CMD_RET_FAILURE;
++      }
++
++      if (mtd == curr_dev) {
++              __put_mtd_device(mtd);
++              return CMD_RET_SUCCESS;
++      }
++
++      if (curr_dev) {
++              old = get_mtd_device(curr_dev, -1);
++              if (!IS_ERR_OR_NULL(old)) {
++                      __put_mtd_device(old);
++                      __put_mtd_device(curr_dev);
++              }
++
++              curr_dev = NULL;
++      }
++
++      curr_dev = mtd;
++
++      printf("'%s' is now active device\n", curr_dev->name);
++
++      return CMD_RET_SUCCESS;
++}
++
++static void dump_buf(const u8 *data, size_t size, u64 addr)
++{
++      const u8 *p = data;
++      u32 i, chklen;
++
++      while (size) {
++              chklen = 16;
++              if (chklen > size)
++                      chklen = (u32)size;
++
++              printf("%08llx: ", addr);
++
++              for (i = 0; i < chklen; i++) {
++                      if (i && (i % 4 == 0))
++                              printf(" ");
++
++                      printf("%02x ", p[i]);
++              }
++
++              for (i = chklen; i < 16; i++) {
++                      if (i && (i % 4 == 0))
++                              printf(" ");
++
++                      printf("   ");
++              }
++              printf(" ");
++
++              for (i = 0; i < chklen; i++) {
++                      if (p[i] < 32 || p[i] >= 0x7f)
++                              printf(".");
++                      else
++                              printf("%c", p[i]);
++              }
++              printf("\n");
++
++              p += chklen;
++              size -= chklen;
++              addr += chklen;
++      }
++}
++
++static int do_nand_dump(struct cmd_tbl *cmdtp, int flag, int argc,
++                       char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev();
++      struct mtd_oob_ops io_op = {};
++      bool raw = false;
++      int ret;
++      u64 off;
++      u8 *buf;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (strstr(argv[0], ".raw"))
++              raw = true;
++
++      if (argc < 2) {
++              printf("Dump offset must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      off = simple_strtoull(argv[1], NULL, 0);
++      if (off >= mtd->size) {
++              printf("Offset 0x%llx is larger than flash size\n", off);
++              return CMD_RET_FAILURE;
++      }
++
++      off &= ~(u64)mtd->writesize_mask;
++
++      buf = malloc(mtd->writesize + mtd->oobsize);
++      if (!buf) {
++              printf("Failed to allocate buffer\n");
++              return CMD_RET_FAILURE;
++      }
++
++      io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
++      io_op.len = mtd->writesize;
++      io_op.datbuf = buf;
++      io_op.ooblen = mtd->oobsize;
++      io_op.oobbuf = buf + mtd->writesize;
++
++      ret = mtd_read_oob(mtd, off, &io_op);
++      if (ret < 0 && ret != -EUCLEAN && ret != -EBADMSG) {
++              printf("Failed to read page at 0x%llx, err %d\n", off, ret);
++              free(buf);
++              return CMD_RET_FAILURE;
++      }
++
++      printf("Dump of %spage at 0x%llx:\n", raw ? "raw " : "", off);
++      dump_buf(buf, mtd->writesize, off);
++
++      printf("\n");
++      printf("OOB:\n");
++      dump_buf(buf + mtd->writesize, mtd->oobsize, 0);
++
++      free(buf);
++
++      return CMD_RET_SUCCESS;
++}
++
++static int do_nand_bad(struct cmd_tbl *cmdtp, int flag, int argc,
++                      char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev();
++      u64 off = 0;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      while (off < mtd->size) {
++              if (mtd_block_isbad(mtd, off))
++                      printf("\t%08llx\n", off);
++
++              off += mtd->erasesize;
++      }
++
++      return 0;
++}
++
++static int do_nand_markbad(struct cmd_tbl *cmdtp, int flag, int argc,
++                          char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev();
++      u64 off;
++      int ret;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (argc < 2) {
++              printf("Missing address within a block to be marked bad\n");
++              return CMD_RET_USAGE;
++      }
++
++      off = simple_strtoull(argv[1], NULL, 0);
++      if (off >= mtd->size) {
++              printf("Offset 0x%llx is larger than flash size\n", off);
++              return CMD_RET_FAILURE;
++      }
++
++      off &= ~(u64)mtd->erasesize_mask;
++
++      ret = mtd_block_markbad(mtd, off);
++
++      if (!ret)
++              printf("Block at 0x%08llx has been marked bad\n", off);
++      else
++              printf("Failed to mark bad block at 0x%08llx\n", off);
++
++      return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
++}
++
++static int do_nand_bitflip(struct cmd_tbl *cmdtp, int flag, int argc,
++                          char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev();
++      struct mtd_oob_ops io_op = {};
++      u32 col, bit;
++      bool res;
++      u64 off;
++      u8 *buf;
++      int ret;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (argc < 2) {
++              printf("Missing address to generate bitflip\n");
++              return CMD_RET_USAGE;
++      }
++
++      off = simple_strtoull(argv[1], NULL, 0);
++      if (off >= mtd->size) {
++              printf("Offset 0x%llx is larger than flash size\n", off);
++              return CMD_RET_FAILURE;
++      }
++
++      if (argc < 3) {
++              printf("Missing column address\n");
++              return CMD_RET_USAGE;
++      }
++
++      col = simple_strtoul(argv[2], NULL, 0);
++      if (col >= mtd->writesize + mtd->oobsize) {
++              printf("Column address must be less than %u\n",
++                     mtd->writesize + mtd->oobsize);
++              return CMD_RET_FAILURE;
++      }
++
++      if (argc < 4) {
++              printf("Missing bit position\n");
++              return CMD_RET_USAGE;
++      }
++
++      bit = simple_strtoul(argv[3], NULL, 0);
++      if (bit > 7) {
++              printf("Bit position must be less than 8\n");
++              return CMD_RET_FAILURE;
++      }
++
++      off &= ~(u64)mtd->writesize_mask;
++
++      buf = malloc(mtd->writesize + mtd->oobsize);
++      if (!buf) {
++              printf("Failed to allocate buffer\n");
++              return CMD_RET_FAILURE;
++      }
++
++      io_op.mode = MTD_OPS_RAW;
++      io_op.len = mtd->writesize;
++      io_op.datbuf = buf;
++      io_op.ooblen = mtd->oobsize;
++      io_op.oobbuf = buf + mtd->writesize;
++
++      ret = mtd_read_oob(mtd, off, &io_op);
++      if (ret < 0) {
++              printf("Failed to read page at 0x%llx, err %d\n", off, ret);
++              free(buf);
++              return CMD_RET_FAILURE;
++      }
++
++      if (!(buf[col] & (1 << bit))) {
++              printf("Bit %u at byte %u is already zero\n", bit, col);
++              free(buf);
++              return CMD_RET_FAILURE;
++      }
++
++      buf[col] &= ~(1 << bit);
++
++      memset(&io_op, 0, sizeof(io_op));
++      io_op.mode = MTD_OPS_RAW;
++      io_op.len = mtd->writesize;
++      io_op.datbuf = buf;
++      io_op.ooblen = mtd->oobsize;
++      io_op.oobbuf = buf + mtd->writesize;
++
++      ret = mtd_write_oob(mtd, off, &io_op);
++
++      if (ret < 0) {
++              printf("Failed to write page at 0x%llx, err %d\n", off, ret);
++              return CMD_RET_FAILURE;
++      }
++
++      memset(&io_op, 0, sizeof(io_op));
++      io_op.mode = MTD_OPS_RAW;
++      io_op.len = mtd->writesize;
++      io_op.datbuf = buf;
++      io_op.ooblen = mtd->oobsize;
++      io_op.oobbuf = buf + mtd->writesize;
++
++      ret = mtd_read_oob(mtd, off, &io_op);
++      if (ret < 0) {
++              printf("Failed to read page at 0x%llx, err %d\n", off, ret);
++              free(buf);
++              return CMD_RET_FAILURE;
++      }
++
++      res = (buf[col] & (1 << bit)) == 0;
++      free(buf);
++
++      if (res) {
++              printf("Bit %u at byte %u has been changed to 0\n", bit, col);
++              return CMD_RET_SUCCESS;
++      }
++
++      printf("Failed to change bit %u at byte %u to 0\n", bit, col);
++      return CMD_RET_FAILURE;
++}
++
++static int do_nand_erase(struct cmd_tbl *cmdtp, int flag, int argc,
++                        char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev(), *part;
++      bool spread = false, force = false;
++      u64 off, size, end, limit;
++      struct erase_info ei;
++      char *ends;
++      int ret;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (strstr(argv[0], ".spread"))
++              spread = true;
++
++      if (strstr(argv[0], ".force"))
++              force = true;
++
++      if (spread && force) {
++              printf("spread and force must not be set at the same time\n");
++              return CMD_RET_FAILURE;
++      }
++
++      if (argc < 2) {
++              printf("Erase start offset/partition must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      part = nand_get_part(mtd, argv[1]);
++      if (part) {
++              off = part->offset;
++
++              if (argc < 3)
++                      size = part->size;
++              else
++                      size = simple_strtoull(argv[2], NULL, 0);
++
++              if (size > part->size) {
++                      printf("Erase end offset is larger than partition size\n");
++                      printf("Erase size reduced to 0x%llx\n", part->size);
++
++                      size = part->size;
++              }
++
++              limit = off + part->size;
++      } else {
++              off = simple_strtoull(argv[1], &ends, 0);
++
++              if (ends == argv[1] || *ends) {
++                      printf("Partition '%s' not found\n", argv[1]);
++                      return CMD_RET_FAILURE;
++              }
++
++              if (off >= mtd->size) {
++                      printf("Offset 0x%llx is larger than flash size\n", off);
++                      return CMD_RET_FAILURE;
++              }
++
++              if (argc < 3) {
++                      printf("Erase size offset must be specified\n");
++                      return CMD_RET_USAGE;
++              }
++
++              size = simple_strtoull(argv[2], NULL, 0);
++
++              if (off + size > mtd->size) {
++                      printf("Erase end offset is larger than flash size\n");
++
++                      size = mtd->size - off;
++                      printf("Erase size reduced to 0x%llx\n", size);
++              }
++
++              limit = mtd->size;
++      }
++
++      end = off + size;
++      off &= ~(u64)mtd->erasesize_mask;
++      end = (end + mtd->erasesize_mask) & (~(u64)mtd->erasesize_mask);
++      size = end - off;
++
++      printf("Erasing from 0x%llx to 0x%llx, size 0x%llx ...\n",
++             off, end - 1, end - off);
++
++      while (size && off < limit) {
++              if (mtd_block_isbad(mtd, off)) {
++                      printf("Bad block at 0x%llx", off);
++
++                      if (spread) {
++                              printf(" ... skipped\n");
++                              off += mtd->erasesize;
++                              continue;
++                      }
++
++                      if (!force) {
++                              printf(" ... aborted\n");
++                              return CMD_RET_FAILURE;
++                      }
++
++                      printf(" ... will be force erased\n");
++              }
++
++              memset(&ei, 0, sizeof(ei));
++
++              ei.mtd = mtd;
++              ei.addr = off;
++              ei.len = mtd->erasesize;
++              ei.scrub = force;
++
++              ret = mtd_erase(mtd, &ei);
++              if (ret) {
++                      printf("Erase failed at 0x%llx\n", off);
++                      return CMD_RET_FAILURE;
++              }
++
++              off += mtd->erasesize;
++              size -= mtd->erasesize;
++      }
++
++      printf("Succeeded\n");
++
++      return CMD_RET_SUCCESS;
++}
++
++static bool is_empty_page(const u8 *buf, size_t size)
++{
++      size_t i;
++
++      for (i = 0; i < size; i++) {
++              if (buf[i] != 0xff)
++                      return false;
++      }
++
++      return true;
++}
++
++static int do_nand_io_normal(int argc, char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev(), *part;
++      bool spread = false, force = false, raw = false, writeff = false;
++      bool read = false, checkbad = true;
++      struct mtd_oob_ops io_op = {};
++      size_t size, padding, chksz;
++      uintptr_t addr;
++      u64 off, offp;
++      char *ends;
++      u8 *buf;
++      int ret;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (!strncmp(argv[0], "read", 4))
++              read = true;
++
++      if (strstr(argv[0], ".spread"))
++              spread = true;
++
++      if (strstr(argv[0], ".force"))
++              force = true;
++
++      if (strstr(argv[0], ".raw"))
++              raw = true;
++
++      if (strstr(argv[0], ".ff"))
++              writeff = true;
++
++      if (spread && force) {
++              printf("spread and force must not be set at the same time\n");
++              return CMD_RET_FAILURE;
++      }
++
++      if (argc < 2) {
++              printf("Data address must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      addr = simple_strtoul(argv[1], NULL, 0);
++
++      if (argc < 3) {
++              printf("Flash address/partition must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      part = nand_get_part(mtd, argv[2]);
++      if (part) {
++              if (argc < 4) {
++                      off = 0;
++              } else {
++                      off = simple_strtoull(argv[3], NULL, 0);
++                      if (off + part->offset >= part->size) {
++                              printf("Offset is larger than partition size\n");
++                              return CMD_RET_FAILURE;
++                      }
++              }
++
++              if (argc < 5) {
++                      size = part->size - off;
++              } else {
++                      size = simple_strtoul(argv[4], NULL, 0);
++                      if (off + size > part->size) {
++                              printf("Data size is too large\n");
++                              return CMD_RET_FAILURE;
++                      }
++              }
++
++              off += part->offset;
++      } else {
++              off = simple_strtoull(argv[2], &ends, 0);
++
++              if (ends == argv[1] || *ends) {
++                      printf("Partition '%s' not found\n", argv[2]);
++                      return CMD_RET_FAILURE;
++              }
++
++              if (off >= mtd->size) {
++                      printf("Offset 0x%llx is larger than flash size\n", off);
++                      return CMD_RET_FAILURE;
++              }
++
++              if (argc < 4) {
++                      printf("Data size must be specified\n");
++                      return CMD_RET_USAGE;
++              }
++
++              size = simple_strtoul(argv[3], NULL, 0);
++              if (off + size > mtd->size) {
++                      printf("Data size is too large\n");
++                      return CMD_RET_FAILURE;
++              }
++      }
++
++      buf = malloc(mtd->writesize);
++      if (!buf) {
++              printf("Failed to allocate buffer\n");
++              return CMD_RET_FAILURE;
++      }
++
++      printf("%s from 0x%llx to 0x%llx, size 0x%zx ...\n",
++             read ? "Reading" : "Writing", off, off + size - 1, size);
++
++      while (size && off < mtd->size) {
++              if (checkbad || !(off & mtd->erasesize_mask)) {
++                      offp = off & ~(u64)mtd->erasesize_mask;
++
++                      if (mtd_block_isbad(mtd, offp)) {
++                              printf("Bad block at 0x%llx", offp);
++
++                              if (spread) {
++                                      printf(" ... skipped\n");
++                                      off += mtd->erasesize;
++                                      checkbad = true;
++                                      continue;
++                              }
++
++                              if (!force) {
++                                      printf(" ... aborted\n");
++                                      goto err_out;
++                              }
++
++                              printf(" ... continue\n");
++                      }
++
++                      checkbad = false;
++              }
++
++              padding = off & mtd->writesize_mask;
++              chksz = mtd->writesize - padding;
++              chksz = min_t(size_t, chksz, size);
++
++              offp = off & ~(u64)mtd->writesize_mask;
++
++              memset(&io_op, 0, sizeof(io_op));
++              io_op.mode = raw ? MTD_OPS_RAW : MTD_OPS_PLACE_OOB;
++              io_op.len = mtd->writesize;
++
++              if (chksz < mtd->writesize)
++                      io_op.datbuf = buf;
++              else
++                      io_op.datbuf = (void *)addr;
++
++              if (read) {
++                      ret = mtd_read_oob(mtd, offp, &io_op);
++                      if (ret && ret != -EUCLEAN && ret != -EBADMSG)
++                              goto io_err;
++
++                      if (chksz < mtd->writesize)
++                              memcpy((void *)addr, buf + padding, chksz);
++              } else {
++                      if (chksz < mtd->writesize) {
++                              memset(buf, 0xff, mtd->writesize);
++                              memcpy(buf + padding, (void *)addr, chksz);
++                      }
++
++                      if (is_empty_page(io_op.datbuf, io_op.len) && !writeff)
++                              ret = 0;
++                      else
++                              ret = mtd_write_oob(mtd, offp, &io_op);
++
++                      if (ret)
++                              goto io_err;
++              }
++
++              size -= chksz;
++              addr += chksz;
++              off += chksz;
++      }
++
++      if (!size) {
++              printf("Succeeded\n");
++              ret = CMD_RET_SUCCESS;
++              goto out;
++      }
++
++      printf("0x%zx byte%s remained for %s\n", size, size > 1 ? "s" : "",
++             read ? "read" : "write");
++      goto err_out;
++
++io_err:
++      printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, offp);
++
++err_out:
++      ret = CMD_RET_FAILURE;
++
++out:
++      free(buf);
++      return ret;
++}
++
++static int do_nand_io_page(int argc, char *const argv[])
++{
++      struct mtd_info *mtd = nand_get_curr_dev(), *part;
++      bool spread = false, force = false, raw = false, autooob = false;
++      bool read = false, checkbad = true, writeff = false;
++      struct mtd_oob_ops io_op = {};
++      uintptr_t addr;
++      u64 off, offp;
++      char *ends;
++      u32 count;
++      int ret;
++
++      if (!mtd)
++              return CMD_RET_FAILURE;
++
++      if (!strncmp(argv[0], "read", 4))
++              read = true;
++
++      if (strstr(argv[0], ".spread"))
++              spread = true;
++
++      if (strstr(argv[0], ".force"))
++              force = true;
++
++      if (strstr(argv[0], ".raw"))
++              raw = true;
++
++      if (strstr(argv[0], ".auto"))
++              autooob = true;
++
++      if (spread && force) {
++              printf("spread and force must not be set at the same time\n");
++              return CMD_RET_FAILURE;
++      }
++
++      if (raw && autooob) {
++              printf("raw and auto must not be set at the same time\n");
++              return CMD_RET_FAILURE;
++      }
++
++      if (argc < 2) {
++              printf("Data address must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      addr = simple_strtoul(argv[1], NULL, 0);
++
++      if (argc < 3) {
++              printf("Flash address/partition must be specified\n");
++              return CMD_RET_USAGE;
++      }
++
++      part = nand_get_part(mtd, argv[2]);
++      if (part) {
++              if (argc < 4) {
++                      printf("Partition offset / page count must be specified\n");
++                      return CMD_RET_USAGE;
++              }
++
++              if (argc < 5) {
++                      off = 0;
++
++                      count = simple_strtoul(argv[3], NULL, 0);
++                      if (part->offset + count * mtd->writesize > part->size) {
++                              printf("Page count exceeds partition size\n");
++                              return CMD_RET_FAILURE;
++                      }
++              } else {
++                      off = simple_strtoull(argv[3], NULL, 0);
++                      if (off >= part->size) {
++                              printf("Offset 0x%llx is larger than partition size\n", off);
++                              return CMD_RET_FAILURE;
++                      }
++
++                      off &= ~(u64)mtd->writesize_mask;
++
++                      count = simple_strtoul(argv[4], NULL, 0);
++                      if (part->offset + off + count * mtd->writesize > part->size) {
++                              printf("Page count exceeds partition size\n");
++                              return CMD_RET_FAILURE;
++                      }
++              }
++
++              off += part->offset;
++      } else {
++              off = simple_strtoull(argv[2], &ends, 0);
++
++              if (ends == argv[1] || *ends) {
++                      printf("Partition '%s' not found\n", argv[2]);
++                      return CMD_RET_FAILURE;
++              }
++
++              if (off >= mtd->size) {
++                      printf("Offset 0x%llx is larger than flash size\n", off);
++                      return CMD_RET_FAILURE;
++              }
++
++              off &= ~(u64)mtd->writesize_mask;
++
++              if (argc < 4) {
++                      printf("Page count must be specified\n");
++                      return CMD_RET_USAGE;
++              }
++
++              count = simple_strtoul(argv[3], NULL, 0);
++              if (off + count * mtd->writesize > mtd->size) {
++                      printf("Page count exceeds flash size\n");
++                      return CMD_RET_FAILURE;
++              }
++      }
++
++      printf("%s from 0x%llx to 0x%llx (+%u), count %u ...\n",
++             read ? "Reading" : "Writing", off,
++             off + count * mtd->writesize - 1, mtd->oobsize, count);
++
++      while (count && off < mtd->size) {
++              if (checkbad || !(off & mtd->erasesize_mask)) {
++                      offp = off & ~(u64)mtd->erasesize_mask;
++
++                      if (mtd_block_isbad(mtd, offp)) {
++                              printf("Bad block at 0x%llx", offp);
++
++                              if (spread) {
++                                      printf(" ... skipped\n");
++                                      off += mtd->erasesize;
++                                      checkbad = true;
++                                      continue;
++                              }
++
++                              if (!force) {
++                                      printf(" ... aborted\n");
++                                      return CMD_RET_FAILURE;
++                              }
++
++                              printf(" ... continue\n");
++                      }
++
++                      checkbad = false;
++              }
++
++              memset(&io_op, 0, sizeof(io_op));
++
++              if (raw)
++                      io_op.mode = MTD_OPS_RAW;
++              else if (autooob)
++                      io_op.mode = MTD_OPS_AUTO_OOB;
++              else
++                      io_op.mode = MTD_OPS_PLACE_OOB;
++
++              io_op.len = mtd->writesize;
++              io_op.ooblen = mtd->oobsize;
++              io_op.datbuf = (void *)addr;
++              io_op.oobbuf = io_op.datbuf + mtd->writesize;
++
++              if (read) {
++                      ret = mtd_read_oob(mtd, off, &io_op);
++                      if (ret && ret != -EUCLEAN && ret != -EBADMSG)
++                              goto io_err;
++              } else {
++                      if (is_empty_page((void *)addr, mtd->writesize + mtd->oobsize) && !writeff)
++                              ret = 0;
++                      else
++                              ret = mtd_write_oob(mtd, off, &io_op);
++
++                      if (ret)
++                              goto io_err;
++              }
++
++              count--;
++              addr += mtd->writesize + mtd->oobsize;
++              off += mtd->writesize;
++      }
++
++      if (!count) {
++              printf("Succeeded\n");
++              return CMD_RET_SUCCESS;
++      }
++
++      printf("%u page%s remained for %s\n", count, count > 1 ? "s" : "",
++             read ? "read" : "write");
++      return CMD_RET_FAILURE;
++
++io_err:
++      printf("%s error %d at 0x%llx\n", read ? "Read" : "Write", ret, off);
++      return CMD_RET_FAILURE;
++}
++
++static int do_nand_io(struct cmd_tbl *cmdtp, int flag, int argc,
++                     char *const argv[])
++{
++      if (strstr(argv[0], ".oob"))
++              return do_nand_io_page(argc, argv);
++
++      return do_nand_io_normal(argc, argv);
++}
++
++#ifdef CONFIG_SYS_LONGHELP
++static char nand_help_text[] =
++      "- NAND flash R/W and debugging utility\n"
++      "nand list\n"
++      "nand info - Show active NAND devices\n"
++      "nand select <name> - Select active NAND devices\n"
++      "nand dump[.raw] <off>\n"
++      "nand bad\n"
++      "nand markbad <off>\n"
++      "nand bitflip <off> <col> <bit>\n"
++      "nand erase[.spread|.force] [<off> <size>|<part> [<size>]]\n"
++      "nand read[.spread|.force][.raw] <addr> <off> <size>\n"
++      "                                <addr> <part> [<off> [<size>]]\n"
++      "nand write[.spread|.force][.raw][.ff] <addr> <off> <size>\n"
++      "                                      <addr> <part> [<off> [<size>]]\n"
++      "nand read.oob[.spread|.force][.raw|.auto] <addr> <off> <count>\n"
++      "                                          <addr> <part> [<off>] <count>\n"
++      "nand write.oob[.spread|.force][.raw|.auto][.ff] <addr> <off> <count>\n"
++      "                                                <addr> <part> [<off>] <count>\n";
++#endif
++
++U_BOOT_CMD_WITH_SUBCMDS(nand, "NAND utility",
++      nand_help_text,
++      U_BOOT_SUBCMD_MKENT(list, 1, 0, do_nand_list),
++      U_BOOT_SUBCMD_MKENT(info, 1, 0, do_nand_info),
++      U_BOOT_SUBCMD_MKENT(select, 2, 0, do_nand_select),
++      U_BOOT_SUBCMD_MKENT(dump, 2, 0, do_nand_dump),
++      U_BOOT_SUBCMD_MKENT(bad, 1, 0, do_nand_bad),
++      U_BOOT_SUBCMD_MKENT(markbad, 2, 0, do_nand_markbad),
++      U_BOOT_SUBCMD_MKENT(bitflip, 4, 0, do_nand_bitflip),
++      U_BOOT_SUBCMD_MKENT(erase, 3, 0, do_nand_erase),
++      U_BOOT_SUBCMD_MKENT(read, 5, 0, do_nand_io),
++      U_BOOT_SUBCMD_MKENT(write, 5, 0, do_nand_io)
++);
diff --git a/package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch b/package/boot/uboot-mediatek/patches/100-14-mtd-spi-nor-add-support-to-read-flash-unique-ID.patch
new file mode 100644 (file)
index 0000000..6a61045
--- /dev/null
@@ -0,0 +1,142 @@
+From c4172a95df8a57a66c70a8b9948b9600a01c4cb7 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 11:32:08 +0800
+Subject: [PATCH 49/71] mtd: spi-nor: add support to read flash unique ID
+
+This patch adds support to read unique ID from spi-nor flashes.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/spi/spi-nor-core.c | 95 ++++++++++++++++++++++++++++++++++
+ include/linux/mtd/spi-nor.h    |  2 +
+ 2 files changed, 97 insertions(+)
+
+--- a/drivers/mtd/spi/spi-nor-core.c
++++ b/drivers/mtd/spi/spi-nor-core.c
+@@ -2742,6 +2742,100 @@ static int spi_nor_init_params(struct sp
+       return 0;
+ }
++static int spi_nor_read_uuid(struct spi_nor *nor)
++{
++      u8 read_opcode, addr_width, read_dummy;
++      loff_t addr;
++      u8 *uuid;
++      u8 uuid_len;
++      int shift = 0;
++      int ret;
++      int i;
++      struct spi_mem_op op;
++
++      read_opcode = nor->read_opcode;
++      addr_width = nor->addr_width;
++      read_dummy = nor->read_dummy;
++
++      switch (JEDEC_MFR(nor->info)) {
++      case SNOR_MFR_WINBOND:
++              uuid_len = 8;
++              nor->read_opcode = 0x4b;
++              nor->addr_width = 0;
++              addr = 0x0;
++              nor->read_dummy = 4;
++              break;
++      case SNOR_MFR_GIGADEVICE:
++              uuid_len = 16;
++              nor->read_opcode = 0x4b;
++              nor->addr_width = 3;
++              addr = 0x0;
++              nor->read_dummy = 1;
++              break;
++      case CFI_MFR_ST:
++      case SNOR_MFR_MICRON:
++              uuid_len = 17;
++              shift = 3;
++              nor->read_opcode = 0x9f;
++              nor->addr_width = 0;
++              addr = 0x0;
++              nor->read_dummy = 0;
++              break;
++      case SNOR_MFR_EON:
++              uuid_len = 12;
++              nor->read_opcode = 0x5a;
++              nor->addr_width = 3;
++              addr = 0x80;
++              nor->read_dummy = 1;
++              break;
++      /* Automotive only in SPANSION's NOR devices */
++      case SNOR_MFR_SPANSION:
++              uuid_len = 11;
++              shift = 386;
++              nor->read_opcode = 0x9f;
++              nor->addr_width = 0;
++              addr = 0x0;
++              nor->read_dummy = 0;
++              break;
++      default:
++              printf("UUID not supported on this device.\n");
++              return -ENOTSUPP;
++      }
++
++      uuid = kmalloc((uuid_len + shift) * sizeof(*uuid), GFP_KERNEL);
++      if (!uuid) {
++              ret = -ENOMEM;
++              goto read_err;
++      }
++      memset(uuid, 0x0, (uuid_len + shift) * sizeof(*uuid));
++
++      op = (struct spi_mem_op)SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
++                                         SPI_MEM_OP_ADDR(nor->addr_width, addr, 0),
++                                         SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
++                                         SPI_MEM_OP_DATA_IN(uuid_len+shift, NULL, 0));
++
++      spi_nor_setup_op(nor, &op, nor->reg_proto);
++
++      ret = spi_nor_read_write_reg(nor, &op, uuid);
++      if (ret < 0) {
++              dev_dbg(nor->dev, "error %d reading %x\n", ret, nor->read_opcode);
++              goto read_err;
++      }
++
++      printf("UUID: 0x");
++      for(i = 0; i<uuid_len; i++)
++              printf("%02x", uuid[i+shift]);
++      puts("\n");
++
++read_err:
++      nor->read_opcode = read_opcode;
++      nor->addr_width = addr_width;
++      nor->read_dummy = read_dummy;
++      kfree(uuid);
++
++      return ret;
++}
++
+ static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
+ {
+       size_t i;
+@@ -3719,6 +3813,7 @@ int spi_nor_scan(struct spi_nor *nor)
+       nor->write = spi_nor_write_data;
+       nor->read_reg = spi_nor_read_reg;
+       nor->write_reg = spi_nor_write_reg;
++      nor->read_uuid = spi_nor_read_uuid;
+       nor->setup = spi_nor_default_setup;
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -28,6 +28,7 @@
+ #define SNOR_MFR_SPANSION     CFI_MFR_AMD
+ #define SNOR_MFR_SST          CFI_MFR_SST
+ #define SNOR_MFR_WINBOND      0xef /* Also used by some Spansion */
++#define SNOR_MFR_EON          CFI_MFR_EON
+ #define SNOR_MFR_CYPRESS      0x34
+ /*
+@@ -547,6 +548,7 @@ struct spi_nor {
+       void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
+       int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
+       int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
++      int (*read_uuid)(struct spi_nor *nor);
+       ssize_t (*read)(struct spi_nor *nor, loff_t from,
+                       size_t len, u_char *read_buf);
diff --git a/package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch b/package/boot/uboot-mediatek/patches/100-15-cmd-sf-add-support-to-read-flash-unique-ID.patch
new file mode 100644 (file)
index 0000000..972a022
--- /dev/null
@@ -0,0 +1,48 @@
+From e60939acbebd07161f3978d1c6f13123fdd2ebf2 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 11:27:02 +0800
+Subject: [PATCH 50/71] cmd: sf: add support to read flash unique ID
+
+This patch adds support to display unique ID from spi-nor flashes
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/sf.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/cmd/sf.c
++++ b/cmd/sf.c
+@@ -391,6 +391,14 @@ static int do_spi_protect(int argc, char
+       return ret == 0 ? 0 : 1;
+ }
++static int do_spi_flash_read_uuid(void)
++{
++      int ret = 0;
++      ret = flash->read_uuid(flash);
++
++      return ret == 0 ? 0 : 1;
++}
++
+ enum {
+       STAGE_ERASE,
+       STAGE_CHECK,
+@@ -587,6 +595,8 @@ static int do_spi_flash(struct cmd_tbl *
+               ret = do_spi_flash_erase(argc, argv);
+       else if (strcmp(cmd, "protect") == 0)
+               ret = do_spi_protect(argc, argv);
++      else if (strcmp(cmd, "uuid") == 0)
++              ret = do_spi_flash_read_uuid();
+       else if (IS_ENABLED(CONFIG_CMD_SF_TEST) && !strcmp(cmd, "test"))
+               ret = do_spi_flash_test(argc, argv);
+       else
+@@ -617,7 +627,8 @@ static const char long_help[] =
+       "                                         at `addr' to flash at `offset'\n"
+       "                                         or to start of mtd `partition'\n"
+       "sf protect lock/unlock sector len      - protect/unprotect 'len' bytes starting\n"
+-      "                                         at address 'sector'"
++      "                                         at address 'sector'\n"
++      "sf uuid                                        - read uuid from flash"
+ #ifdef CONFIG_CMD_SF_TEST
+       "\nsf test offset len           - run a very basic destructive test"
+ #endif
diff --git a/package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch b/package/boot/uboot-mediatek/patches/100-16-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch
new file mode 100644 (file)
index 0000000..ee45ff0
--- /dev/null
@@ -0,0 +1,250 @@
+From 5a15437610e8e8c68dc347845a83d0cbad80ca08 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 51/71] cmd: bootmenu: add ability to select item by shortkey
+
+Add ability to use shortkey to select item for bootmenu command
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/bootmenu.c | 34 ++++++++++++++++++++++++-----
+ common/menu.c  | 58 ++++++++++++++++++++++++++++++++++++++++++++++++--
+ include/menu.h | 12 +++++++----
+ 3 files changed, 93 insertions(+), 11 deletions(-)
+
+--- a/cmd/bootmenu.c
++++ b/cmd/bootmenu.c
+@@ -87,16 +87,17 @@ static char *bootmenu_choice_entry(void
+       struct bootmenu_data *menu = data;
+       struct bootmenu_entry *iter;
+       enum bootmenu_key key = KEY_NONE;
++      int choice = -1;
+       int esc = 0;
+       int i;
+       while (1) {
+               if (menu->delay >= 0) {
+                       /* Autoboot was not stopped */
+-                      bootmenu_autoboot_loop(menu, &key, &esc);
++                      bootmenu_autoboot_loop(menu, &key, &esc, &choice);
+               } else {
+                       /* Some key was pressed, so autoboot was stopped */
+-                      bootmenu_loop(menu, &key, &esc);
++                      bootmenu_loop(menu, &key, &esc, &choice);
+               }
+               switch (key) {
+@@ -110,6 +111,12 @@ static char *bootmenu_choice_entry(void
+                               ++menu->active;
+                       /* no menu key selected, regenerate menu */
+                       return NULL;
++              case KEY_CHOICE:
++                      menu->active = choice;
++                      if (!menu->last_choiced) {
++                              menu->last_choiced = true;
++                              return NULL;
++                      }
+               case KEY_SELECT:
+                       iter = menu->first;
+                       for (i = 0; i < menu->active; ++i)
+@@ -167,6 +174,9 @@ static int prepare_bootmenu_entry(struct
+       unsigned short int i = *index;
+       struct bootmenu_entry *entry = NULL;
+       struct bootmenu_entry *iter = *current;
++      char *choice_option;
++      char choice_char;
++      int len;
+       while ((option = bootmenu_getoption(i))) {
+@@ -181,11 +191,24 @@ static int prepare_bootmenu_entry(struct
+               if (!entry)
+                       return -ENOMEM;
+-              entry->title = strndup(option, sep - option);
++              /* Add KEY_CHOICE support: '%d. %s\0' : len --> len + 4 */
++              len = sep - option + 4;
++              choice_option = malloc(len);
++              if (!choice_option) {
++                      free(entry->title);
++                      free(entry);
++                      return -ENOMEM;
++              }
++              if (!get_choice_char(i, &choice_char))
++                      len = snprintf(choice_option, len, "%c. %s", choice_char, option);
++              else
++                      len = snprintf(choice_option, len, "   %s", option);
++              entry->title = strndup(choice_option, len);
+               if (!entry->title) {
+                       free(entry);
+                       return -ENOMEM;
+               }
++              free(choice_option);
+               entry->command = strdup(sep + 1);
+               if (!entry->command) {
+@@ -331,6 +354,7 @@ static struct bootmenu_data *bootmenu_cr
+       menu->delay = delay;
+       menu->active = 0;
+       menu->first = NULL;
++      menu->last_choiced = false;
+       default_str = env_get("bootmenu_default");
+       if (default_str)
+@@ -356,9 +380,9 @@ static struct bootmenu_data *bootmenu_cr
+               /* Add Quit entry if entering U-Boot console is disabled */
+               if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
+-                      entry->title = strdup("U-Boot console");
++                      entry->title = strdup("0. U-Boot console");
+               else
+-                      entry->title = strdup("Quit");
++                      entry->title = strdup("0. Quit");
+               if (!entry->title) {
+                       free(entry);
+--- a/common/menu.c
++++ b/common/menu.c
+@@ -47,6 +47,33 @@ struct menu {
+       int item_cnt;
+ };
++const char choice_chars[] = {
++      '1', '2', '3', '4', '5', '6', '7', '8', '9',
++      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
++      'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
++      'u', 'v', 'w', 'x', 'y', 'z'
++};
++
++static int find_choice(char choice)
++{
++      int i;
++
++      for (i = 0; i < ARRAY_SIZE(choice_chars); i++)
++              if (tolower(choice) == choice_chars[i])
++                      return i;
++
++      return -1;
++}
++
++int get_choice_char(int index, char *result)
++{
++      if (index < ARRAY_SIZE(choice_chars))
++              *result = choice_chars[index];
++      else
++              return -1;
++      return 0;
++}
++
+ /*
+  * An iterator function for menu items. callback will be called for each item
+  * in m, with m, a pointer to the item, and extra being passed to callback. If
+@@ -426,7 +453,7 @@ int menu_destroy(struct menu *m)
+ }
+ void bootmenu_autoboot_loop(struct bootmenu_data *menu,
+-                          enum bootmenu_key *key, int *esc)
++                          enum bootmenu_key *key, int *esc, int *choice)
+ {
+       int i, c;
+@@ -456,6 +483,19 @@ void bootmenu_autoboot_loop(struct bootm
+                               break;
+                       default:
+                               *key = KEY_NONE;
++                              if (*esc)
++                                      break;
++
++                              *choice = find_choice(c);
++                              if ((*choice >= 0 &&
++                                   *choice < menu->count - 1)) {
++                                      *key = KEY_CHOICE;
++                              } else if (c == '0') {
++                                      *choice = menu->count - 1;
++                                      *key = KEY_CHOICE;
++                              } else {
++                                      *key = KEY_NONE;
++                              }
+                               break;
+                       }
+@@ -475,10 +515,16 @@ void bootmenu_autoboot_loop(struct bootm
+ }
+ void bootmenu_loop(struct bootmenu_data *menu,
+-                 enum bootmenu_key *key, int *esc)
++                 enum bootmenu_key *key, int *esc, int *choice)
+ {
+       int c;
++      if (menu->last_choiced) {
++              menu->last_choiced = false;
++              *key = KEY_SELECT;
++              return;
++      }
++
+       if (*esc == 1) {
+               if (tstc()) {
+                       c = getchar();
+@@ -504,6 +550,14 @@ void bootmenu_loop(struct bootmenu_data
+               if (c == '\e') {
+                       *esc = 1;
+                       *key = KEY_NONE;
++              } else {
++                      *choice = find_choice(c);
++                      if ((*choice >= 0 && *choice < menu->count - 1)) {
++                              *key = KEY_CHOICE;
++                      } else if (c == '0') {
++                              *choice = menu->count - 1;
++                              *key = KEY_CHOICE;
++                      }
+               }
+               break;
+       case 1:
+--- a/include/menu.h
++++ b/include/menu.h
+@@ -2,10 +2,11 @@
+ /*
+  * Copyright 2010-2011 Calxeda, Inc.
+  */
+-
+ #ifndef __MENU_H__
+ #define __MENU_H__
++#include <linux/ctype.h>
++
+ struct menu;
+ struct menu *menu_create(char *title, int timeout, int prompt,
+@@ -18,6 +19,8 @@ int menu_get_choice(struct menu *m, void
+ int menu_item_add(struct menu *m, char *item_key, void *item_data);
+ int menu_destroy(struct menu *m);
+ int menu_default_choice(struct menu *m, void **choice);
++/* Add KEY_CHOICE support */
++int get_choice_char(int index, char *result);
+ /**
+  * menu_show() Show a boot menu
+@@ -40,6 +43,7 @@ struct bootmenu_data {
+       int active;                     /* active menu entry */
+       int count;                      /* total count of menu entries */
+       struct bootmenu_entry *first;   /* first menu entry */
++      bool last_choiced;
+ };
+ enum bootmenu_key {
+@@ -48,11 +52,11 @@ enum bootmenu_key {
+       KEY_DOWN,
+       KEY_SELECT,
+       KEY_QUIT,
++      KEY_CHOICE,
+ };
+ void bootmenu_autoboot_loop(struct bootmenu_data *menu,
+-                          enum bootmenu_key *key, int *esc);
++                          enum bootmenu_key *key, int *esc, int *choice);
+ void bootmenu_loop(struct bootmenu_data *menu,
+-                 enum bootmenu_key *key, int *esc);
+-
++                 enum bootmenu_key *key, int *esc, int *choice);
+ #endif /* __MENU_H__ */
diff --git a/package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch b/package/boot/uboot-mediatek/patches/100-17-common-spl-spl_nand-enable-CONFIG_SYS_NAND_U_BOOT_OF.patch
new file mode 100644 (file)
index 0000000..149a156
--- /dev/null
@@ -0,0 +1,28 @@
+From 7ab891faaaf2b6126694352d4503dc40605a6aec Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 15:10:02 +0800
+Subject: [PATCH 52/71] common: spl: spl_nand: enable
+ CONFIG_SYS_NAND_U_BOOT_OFFS undefined
+
+Enable using spl_nand with CONFIG_SYS_NAND_U_BOOT_OFFS undefined since
+mtk-snand does not require raw nand framework.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ common/spl/spl_nand.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/common/spl/spl_nand.c
++++ b/common/spl/spl_nand.c
+@@ -16,7 +16,11 @@
+ uint32_t __weak spl_nand_get_uboot_raw_page(void)
+ {
++#ifdef CONFIG_SYS_NAND_U_BOOT_OFFS
+       return CONFIG_SYS_NAND_U_BOOT_OFFS;
++#else
++      return 0;
++#endif
+ }
+ #if defined(CONFIG_SPL_NAND_RAW_ONLY)
diff --git a/package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch b/package/boot/uboot-mediatek/patches/100-18-board-mt7629-add-support-for-booting-from-SPI-NAND.patch
new file mode 100644 (file)
index 0000000..b6243db
--- /dev/null
@@ -0,0 +1,274 @@
+From 452dc98572f8353f77551bcce5a2ca8cd050f498 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 53/71] board: mt7629: add support for booting from SPI-NAND
+
+Add support for mt7629 to boot from SPI-NAND.
+Add a new defconfig for mt7629+spi-nand configuration.
+
+Signed-off-by: Weijie Gao <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 ++++
+ arch/arm/mach-mediatek/Kconfig      |   4 +-
+ board/mediatek/mt7629/Kconfig       |  40 ++++++++++
+ board/mediatek/mt7629/mt7629_rfb.c  |   5 ++
+ configs/mt7629_nand_rfb_defconfig   | 113 ++++++++++++++++++++++++++++
+ 7 files changed, 195 insertions(+), 1 deletion(-)
+ create mode 100644 board/mediatek/mt7629/Kconfig
+ create mode 100644 configs/mt7629_nand_rfb_defconfig
+
+--- a/arch/arm/dts/mt7629-rfb-u-boot.dtsi
++++ b/arch/arm/dts/mt7629-rfb-u-boot.dtsi
+@@ -40,3 +40,11 @@
+ &snfi {
+       u-boot,dm-pre-reloc;
+ };
++
++&pinctrl {
++      u-boot,dm-pre-reloc;
++};
++
++&snand {
++      u-boot,dm-pre-reloc;
++};
+--- a/arch/arm/dts/mt7629-rfb.dts
++++ b/arch/arm/dts/mt7629-rfb.dts
+@@ -47,9 +47,12 @@
+       };
+       snfi_pins: snfi-pins {
++              u-boot,dm-pre-reloc;
++
+               mux {
+                       function = "flash";
+                       groups = "snfi";
++                      u-boot,dm-pre-reloc;
+               };
+       };
+@@ -102,6 +105,13 @@
+       };
+ };
++&snand {
++      pinctrl-names = "default";
++      pinctrl-0 = <&snfi_pins>;
++      status = "okay";
++      quad-spi;
++};
++
+ &uart0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart0_pins>;
+--- a/arch/arm/dts/mt7629.dtsi
++++ b/arch/arm/dts/mt7629.dtsi
+@@ -229,6 +229,22 @@
+               #size-cells = <0>;
+       };
++      snand: snand@1100d000 {
++              compatible = "mediatek,mt7629-snand";
++              reg = <0x1100d000 0x1000>,
++                    <0x1100e000 0x1000>;
++              reg-names = "nfi", "ecc";
++              clocks = <&pericfg CLK_PERI_NFI_PD>,
++                       <&pericfg CLK_PERI_SNFI_PD>,
++                       <&pericfg CLK_PERI_NFIECC_PD>;
++              clock-names = "nfi_clk", "pad_clk", "ecc_clk";
++              assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>,
++                                <&topckgen CLK_TOP_NFI_INFRA_SEL>;
++              assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>,
++                                       <&topckgen CLK_TOP_UNIVPLL2_D8>;
++              status = "disabled";
++      };
++
+       snor: snor@11014000 {
+               compatible = "mediatek,mtk-snor";
+               reg = <0x11014000 0x1000>;
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -131,9 +131,11 @@ config SYS_CONFIG_NAME
+ config MTK_BROM_HEADER_INFO
+       string
+-      default "media=nor" if TARGET_MT8518 || TARGET_MT8512 || TARGET_MT7629 || TARGET_MT7622
++      default "media=nor" if TARGET_MT8518 || TARGET_MT8512 || TARGET_MT7622
+       default "media=emmc" if TARGET_MT8516 || TARGET_MT8365 || TARGET_MT8183
+       default "media=snand;nandinfo=2k+64" if TARGET_MT7981 || TARGET_MT7986
+       default "lk=1" if TARGET_MT7623
++source "board/mediatek/mt7629/Kconfig"
++
+ endif
+--- /dev/null
++++ b/board/mediatek/mt7629/Kconfig
+@@ -0,0 +1,40 @@
++if TARGET_MT7629
++
++config MTK_BROM_HEADER_INFO
++      string
++      default "media=nor" if BOOT_FROM_SNOR
++      default "media=snand;nandinfo=2k+64" if BOOT_FROM_SNAND_2K_64
++      default "media=snand;nandinfo=2k+128" if BOOT_FROM_SNAND_2K_128
++      default "media=snand;nandinfo=4k+128" if BOOT_FROM_SNAND_4K_128
++      default "media=snand;nandinfo=4k+256" if BOOT_FROM_SNAND_4K_256
++
++choice
++      prompt "Boot device"
++      default BOOT_FROM_SNOR
++
++config BOOT_FROM_SNOR
++      bool "SPI-NOR"
++
++config BOOT_FROM_SNAND_2K_64
++      bool "SPI-NAND (2K+64)"
++      select MT7629_BOOT_FROM_SNAND
++
++config BOOT_FROM_SNAND_2K_128
++      bool "SPI-NAND (2K+128)"
++      select MT7629_BOOT_FROM_SNAND
++
++config BOOT_FROM_SNAND_4K_128
++      bool "SPI-NAND (4K+128)"
++      select MT7629_BOOT_FROM_SNAND
++
++config BOOT_FROM_SNAND_4K_256
++      bool "SPI-NAND (4K+256)"
++      select MT7629_BOOT_FROM_SNAND
++
++endchoice
++
++config MT7629_BOOT_FROM_SNAND
++      bool
++      default n
++
++endif
+--- a/board/mediatek/mt7629/mt7629_rfb.c
++++ b/board/mediatek/mt7629/mt7629_rfb.c
+@@ -15,3 +15,8 @@ int board_init(void)
+       return 0;
+ }
++
++uint32_t spl_nand_get_uboot_raw_page(void)
++{
++      return CONFIG_SPL_PAD_TO;
++}
+--- /dev/null
++++ b/configs/mt7629_nand_rfb_defconfig
+@@ -0,0 +1,113 @@
++CONFIG_ARM=y
++CONFIG_SYS_ARCH_TIMER=y
++CONFIG_SYS_THUMB_BUILD=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_SYS_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_ENV_SIZE=0x20000
++CONFIG_ENV_OFFSET=0x0
++CONFIG_DEFAULT_DEVICE_TREE="mt7629-rfb"
++CONFIG_SPL_TEXT_BASE=0x201000
++CONFIG_TARGET_MT7629=y
++CONFIG_BOOT_FROM_SNAND_2K_64=y
++CONFIG_SPL_SERIAL=y
++CONFIG_SPL_STACK_R_ADDR=0x40800000
++CONFIG_SYS_LOAD_ADDR=0x42007f1c
++CONFIG_SPL_PAYLOAD="u-boot.img"
++CONFIG_BUILD_TARGET="u-boot-mtk.bin"
++CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
++CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x41fffef0
++CONFIG_SPL_IMAGE="spl/u-boot-spl-mtk.bin"
++CONFIG_FIT=y
++# CONFIG_AUTOBOOT is not set
++CONFIG_DEFAULT_FDT_FILE="mt7629-rfb"
++CONFIG_SYS_CONSOLE_IS_IN_ENV=y
++# CONFIG_DISPLAY_BOARDINFO is not set
++CONFIG_SPL_MAX_SIZE=0x20000
++CONFIG_SPL_FOOTPRINT_LIMIT=y
++CONFIG_SPL_MAX_FOOTPRINT=0x20000
++CONFIG_SPL_SYS_MALLOC_SIMPLE=y
++# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
++CONFIG_SPL_STACK=0x106000
++CONFIG_SPL_STACK_R=y
++CONFIG_SPL_MTD_SUPPORT=y
++CONFIG_SPL_NAND_SUPPORT=y
++CONFIG_SPL_WATCHDOG=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_PROMPT="U-Boot> "
++# CONFIG_BOOTM_NETBSD is not set
++# CONFIG_BOOTM_PLAN9 is not set
++# CONFIG_BOOTM_RTEMS is not set
++# CONFIG_BOOTM_VXWORKS is not set
++CONFIG_SYS_BOOTM_LEN=0x4000000
++CONFIG_CMD_BOOTMENU=y
++# CONFIG_CMD_ELF is not set
++# CONFIG_CMD_XIMG is not set
++CONFIG_CMD_BIND=y
++CONFIG_CMD_DM=y
++# CONFIG_CMD_FLASH is not set
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_MTD=y
++CONFIG_CMD_USB=y
++# CONFIG_CMD_SETEXPR is not set
++# CONFIG_CMD_NFS is not set
++CONFIG_CMD_PING=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_CMD_LOG=y
++CONFIG_OF_SPL_REMOVE_PROPS="interrupt-parent assigned-clocks assigned-clock-parents"
++CONFIG_ENV_OVERWRITE=y
++CONFIG_ENV_IS_IN_MTD=y
++CONFIG_ENV_MTD_NAME="u-boot-env"
++CONFIG_ENV_SIZE_REDUND=0x40000
++CONFIG_SYS_RELOC_GD_ENV_ADDR=y
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_SPL_DM_SEQ_ALIAS=y
++CONFIG_REGMAP=y
++CONFIG_SPL_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_SPL_SYSCON=y
++CONFIG_CLK=y
++CONFIG_SPL_CLK=y
++# CONFIG_MMC is not set
++CONFIG_MTD=y
++CONFIG_DM_MTD=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
++CONFIG_SPL_MTK_SPI_NAND=y
++CONFIG_DM_ETH=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PHY=y
++CONFIG_PHY_MTK_TPHY=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_SPL_PINCTRL=y
++CONFIG_SPL_PINCONF=y
++CONFIG_PINCTRL_MT7629=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_DM_REGULATOR=y
++CONFIG_DM_REGULATOR_FIXED=y
++CONFIG_RAM=y
++CONFIG_SPL_RAM=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_SPI=y
++CONFIG_DM_SPI=y
++CONFIG_SPI_MEM=y
++CONFIG_MTK_SNFI_SPI=y
++CONFIG_SYSRESET=y
++CONFIG_SPL_SYSRESET=y
++CONFIG_SYSRESET_WATCHDOG=y
++CONFIG_USB=y
++# CONFIG_SPL_DM_USB is not set
++CONFIG_USB_XHCI_HCD=y
++CONFIG_USB_XHCI_MTK=y
++CONFIG_USB_STORAGE=y
++CONFIG_WDT_MTK=y
++# CONFIG_SHA256 is not set
++# CONFIG_SPL_SHA1 is not set
++CONFIG_LZMA=y
++CONFIG_SPL_LZMA=y
++# CONFIG_EFI_LOADER is not set
diff --git a/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/100-19-board-mt7622-use-new-spi-nand-driver.patch
new file mode 100644 (file)
index 0000000..6e90f47
--- /dev/null
@@ -0,0 +1,76 @@
+From 4c1803cc08b1618d935c1386f43f43a4e9c97697 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 54/71] 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
+@@ -196,6 +196,13 @@
+       };
+ };
++&snand {
++      pinctrl-names = "default";
++      pinctrl-0 = <&snfi_pins>;
++      status = "okay";
++      quad-spi;
++};
++
+ &uart0 {
+       status = "okay";
+ };
+--- a/arch/arm/dts/mt7622.dtsi
++++ b/arch/arm/dts/mt7622.dtsi
+@@ -77,6 +77,22 @@
+               #size-cells = <0>;
+       };
++      snand: snand@1100d000 {
++              compatible = "mediatek,mt7622-snand";
++              reg = <0x1100d000 0x1000>,
++                    <0x1100e000 0x1000>;
++              reg-names = "nfi", "ecc";
++              clocks = <&pericfg CLK_PERI_NFI_PD>,
++                       <&pericfg CLK_PERI_SNFI_PD>,
++                       <&pericfg CLK_PERI_NFIECC_PD>;
++              clock-names = "nfi_clk", "pad_clk", "ecc_clk";
++              assigned-clocks = <&topckgen CLK_TOP_AXI_SEL>,
++                                <&topckgen CLK_TOP_NFI_INFRA_SEL>;
++              assigned-clock-parents = <&topckgen CLK_TOP_SYSPLL1_D2>,
++                                       <&topckgen CLK_TOP_UNIVPLL2_D8>;
++              status = "disabled";
++      };
++
+       snor: snor@11014000 {
+               compatible = "mediatek,mtk-snor";
+               reg = <0x11014000 0x1000>;
+--- a/configs/mt7622_rfb_defconfig
++++ b/configs/mt7622_rfb_defconfig
+@@ -18,6 +18,7 @@ CONFIG_LOG=y
+ CONFIG_SYS_PROMPT="MT7622> "
+ CONFIG_CMD_BOOTMENU=y
+ CONFIG_CMD_MMC=y
++CONFIG_CMD_MTD=y
+ CONFIG_CMD_PCI=y
+ CONFIG_CMD_SF_TEST=y
+ CONFIG_CMD_PING=y
+@@ -33,6 +34,10 @@ CONFIG_SYSCON=y
+ CONFIG_CLK=y
+ CONFIG_MMC_HS200_SUPPORT=y
+ CONFIG_MMC_MTK=y
++CONFIG_MTD=y
++CONFIG_DM_MTD=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
+ CONFIG_DM_SPI_FLASH=y
+ CONFIG_SPI_FLASH_EON=y
+ CONFIG_SPI_FLASH_GIGADEVICE=y
diff --git a/package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch b/package/boot/uboot-mediatek/patches/100-20-board-mt7981-add-reference-board-using-new-spi-nand-.patch
new file mode 100644 (file)
index 0000000..2cd62a2
--- /dev/null
@@ -0,0 +1,223 @@
+From d5841f8707dcb7a1f73607de67ab45dba93a56a4 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Fri, 29 Jul 2022 17:04:12 +0800
+Subject: [PATCH 55/71] board: mt7981: add reference board using new spi-nand
+ driver
+
+Add a new reference board using new spi-nand driver for SPI-NAND flash on
+SNFI interface
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ arch/arm/dts/Makefile                  |   1 +
+ arch/arm/dts/mt7981-snfi-nand-rfb.dts  | 132 +++++++++++++++++++++++++
+ configs/mt7981_snfi_nand_rfb_defconfig |  57 +++++++++++
+ 3 files changed, 190 insertions(+)
+ create mode 100644 arch/arm/dts/mt7981-snfi-nand-rfb.dts
+ create mode 100644 configs/mt7981_snfi_nand_rfb_defconfig
+
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -1206,6 +1206,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
+       mt7623n-bananapi-bpi-r2.dtb \
+       mt7629-rfb.dtb \
+       mt7981-rfb.dtb \
++      mt7981-snfi-nand-rfb.dtb \
+       mt7981-emmc-rfb.dtb \
+       mt7981-sd-rfb.dtb \
+       mt7986a-rfb.dtb \
+--- /dev/null
++++ b/arch/arm/dts/mt7981-snfi-nand-rfb.dts
+@@ -0,0 +1,132 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2021 MediaTek Inc.
++ * Author: Sam Shih <sam.shih@mediatek.com>
++ */
++
++/dts-v1/;
++#include "mt7981.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++
++/ {
++      #address-cells = <1>;
++      #size-cells = <1>;
++      model = "mt7981-rfb";
++      compatible = "mediatek,mt7981", "mediatek,mt7981-rfb";
++      chosen {
++              stdout-path = &uart0;
++              tick-timer = &timer0;
++      };
++};
++
++&uart0 {
++      status = "okay";
++};
++
++&uart1 {
++      pinctrl-names = "default";
++      pinctrl-0 = <&uart1_pins>;
++      status = "disabled";
++};
++
++&eth {
++      status = "okay";
++      mediatek,gmac-id = <0>;
++      phy-mode = "sgmii";
++      mediatek,switch = "mt7531";
++      reset-gpios = <&gpio 39 GPIO_ACTIVE_HIGH>;
++
++      fixed-link {
++              speed = <1000>;
++              full-duplex;
++      };
++};
++
++&pinctrl {
++      snfi_pins: snfi-pins-func-1 {
++              mux {
++                      function = "flash";
++                      groups = "snfi";
++              };
++
++              clk {
++                      pins = "SPI0_CLK";
++                      drive-strength = <MTK_DRIVE_8mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_00>;
++              };
++
++              conf-pu {
++                      pins = "SPI0_CS", "SPI0_HOLD", "SPI0_WP";
++                      drive-strength = <MTK_DRIVE_6mA>;
++                      bias-pull-up = <MTK_PUPD_SET_R1R0_00>;
++              };
++
++              conf-pd {
++                      pins = "SPI0_MOSI", "SPI0_MISO";
++                      drive-strength = <MTK_DRIVE_6mA>;
++                      bias-pull-down = <MTK_PUPD_SET_R1R0_00>;
++              };
++      };
++
++      spic_pins: spi1-pins-func-1 {
++              mux {
++                      function = "spi";
++                      groups = "spi1_1";
++              };
++      };
++
++      uart1_pins: spi1-pins-func-3 {
++              mux {
++                      function = "uart";
++                      groups = "uart1_2";
++              };
++      };
++
++      /* pin15 as pwm0 */
++      one_pwm_pins: one-pwm-pins {
++              mux {
++                      function = "pwm";
++                      groups = "pwm0_1";
++              };
++      };
++
++      /* pin15 as pwm0 and pin14 as pwm1 */
++      two_pwm_pins: two-pwm-pins {
++              mux {
++                      function = "pwm";
++                      groups = "pwm0_1", "pwm1_0";
++              };
++      };
++
++      /* pin15 as pwm0, pin14 as pwm1, pin7 as pwm2 */
++      three_pwm_pins: three-pwm-pins {
++              mux {
++                      function = "pwm";
++                      groups = "pwm0_1", "pwm1_0", "pwm2";
++              };
++      };
++
++      mmc0_pins_default: mmc0default {
++                mux {
++                       function = "flash";
++                       groups =  "emmc_45";
++                 };
++         };
++};
++
++&snand {
++      pinctrl-names = "default";
++      pinctrl-0 = <&snfi_pins>;
++      status = "okay";
++      quad-spi;
++};
++
++&pwm {
++      pinctrl-names = "default";
++      pinctrl-0 = <&two_pwm_pins>;
++      status = "okay";
++};
++
++&watchdog {
++      status = "disabled";
++};
+--- /dev/null
++++ b/configs/mt7981_snfi_nand_rfb_defconfig
+@@ -0,0 +1,57 @@
++CONFIG_ARM=y
++CONFIG_POSITION_INDEPENDENT=y
++CONFIG_ARCH_MEDIATEK=y
++CONFIG_SYS_TEXT_BASE=0x41e00000
++CONFIG_SYS_MALLOC_F_LEN=0x4000
++CONFIG_NR_DRAM_BANKS=1
++CONFIG_ENV_SIZE=0x20000
++CONFIG_DEFAULT_DEVICE_TREE="mt7981-snfi-nand-rfb"
++CONFIG_TARGET_MT7981=y
++CONFIG_DEBUG_UART_BASE=0x11002000
++CONFIG_DEBUG_UART_CLOCK=40000000
++CONFIG_SYS_LOAD_ADDR=0x46000000
++CONFIG_DEBUG_UART=y
++# CONFIG_AUTOBOOT is not set
++CONFIG_DEFAULT_FDT_FILE="mt7981-snfi-nand-rfb"
++CONFIG_LOGLEVEL=7
++CONFIG_LOG=y
++CONFIG_SYS_PROMPT="MT7981> "
++CONFIG_SYS_CBSIZE=512
++CONFIG_SYS_PBSIZE=1049
++# CONFIG_BOOTM_NETBSD is not set
++# CONFIG_BOOTM_PLAN9 is not set
++# CONFIG_BOOTM_RTEMS is not set
++# CONFIG_BOOTM_VXWORKS is not set
++# CONFIG_CMD_ELF is not set
++# CONFIG_CMD_UNLZ4 is not set
++# CONFIG_CMD_UNZIP is not set
++CONFIG_CMD_GPIO=y
++CONFIG_CMD_MTD=y
++CONFIG_CMD_PING=y
++CONFIG_CMD_SMC=y
++CONFIG_MTDIDS_DEFAULT="spi-nand0=spi-nand0"
++CONFIG_MTDPARTS_DEFAULT="spi-nand0:1024k(bl2),512k(u-boot-env),2048k(factory),2048k(fip),65536k(ubi)"
++CONFIG_CMD_UBI=y
++CONFIG_CMD_UBI_RENAME=y
++CONFIG_ENV_OVERWRITE=y
++CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
++CONFIG_NET_RANDOM_ETHADDR=y
++CONFIG_REGMAP=y
++CONFIG_SYSCON=y
++CONFIG_CLK=y
++# CONFIG_MMC is not set
++CONFIG_MTD=y
++CONFIG_DM_MTD=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTK_SPI_NAND_MTD=y
++CONFIG_PHY_FIXED=y
++CONFIG_DM_ETH=y
++CONFIG_MEDIATEK_ETH=y
++CONFIG_PINCTRL=y
++CONFIG_PINCONF=y
++CONFIG_PINCTRL_MT7981=y
++CONFIG_POWER_DOMAIN=y
++CONFIG_MTK_POWER_DOMAIN=y
++CONFIG_DM_SERIAL=y
++CONFIG_MTK_SERIAL=y
++CONFIG_HEXDUMP=y
diff --git a/package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch b/package/boot/uboot-mediatek/patches/100-21-mtd-spi-nor-add-more-flash-ids.patch
new file mode 100644 (file)
index 0000000..228bde1
--- /dev/null
@@ -0,0 +1,76 @@
+From a2df2df6fd1aec32572c7b30ccf5a184ec1763fd Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 27 Jul 2022 16:32:17 +0800
+Subject: [PATCH 56/71] mtd: spi-nor: add more flash ids
+
+Add more spi-nor flash ids
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/spi/spi-nor-core.c |  1 +
+ drivers/mtd/spi/spi-nor-ids.c  | 23 ++++++++++++++++++++++-
+ 2 files changed, 23 insertions(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi/spi-nor-core.c
++++ b/drivers/mtd/spi/spi-nor-core.c
+@@ -641,6 +641,7 @@ static int set_4byte(struct spi_nor *nor
+       case SNOR_MFR_ISSI:
+       case SNOR_MFR_MACRONIX:
+       case SNOR_MFR_WINBOND:
++      case SNOR_MFR_EON:
+               if (need_wren)
+                       write_enable(nor);
+--- a/drivers/mtd/spi/spi-nor-ids.c
++++ b/drivers/mtd/spi/spi-nor-ids.c
+@@ -83,7 +83,8 @@ const struct flash_info spi_nor_ids[] =
+       { INFO("en25q32b",   0x1c3016, 0, 64 * 1024,   64, 0) },
+       { INFO("en25q64",    0x1c3017, 0, 64 * 1024,  128, SECT_4K) },
+       { INFO("en25q128b",  0x1c3018, 0, 64 * 1024,  256, 0) },
+-      { INFO("en25qh128",  0x1c7018, 0, 64 * 1024,  256, 0) },
++      { INFO("en25qh128",  0x1c7018, 0, 64 * 1024,  256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { INFO("en25qh256",  0x1c7019, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("en25s64",    0x1c3817, 0, 64 * 1024,  128, SECT_4K) },
+ #endif
+ #ifdef CONFIG_SPI_FLASH_GIGADEVICE    /* GIGADEVICE */
+@@ -119,6 +120,11 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       {
++              INFO("gd25q256", 0xc84019, 0, 64 * 1024, 512,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
+               INFO("gd25lq128", 0xc86018, 0, 64 * 1024, 256,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+@@ -395,6 +401,16 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       {
++              INFO("w25q256jv", 0xef7019, 0, 64 * 1024, 512,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
++              INFO("w25q512jv", 0xef7020, 0, 64 * 1024, 1024,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
++      {
+               INFO("w25q128jw", 0xef8018, 0, 64 * 1024, 256,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+@@ -439,6 +455,11 @@ const struct flash_info spi_nor_ids[] =
+                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+       },
+       { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      {
++              INFO("w25q512", 0xef4020, 0, 64 * 1024, 1024,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++                      SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++      },
+       { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { INFO("w25h02jv", 0xef9022, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
diff --git a/package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch b/package/boot/uboot-mediatek/patches/100-22-mtd-spi-nand-backport-from-upstream-kernel.patch
new file mode 100644 (file)
index 0000000..1c83479
--- /dev/null
@@ -0,0 +1,1550 @@
+From a3c5878599d530330027412eb8be12f816ac215c Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Wed, 27 Jul 2022 16:36:13 +0800
+Subject: [PATCH 57/71] mtd: spi-nand: backport from upstream kernel
+
+Backport new features from upstream kernel
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/nand/spi/Kconfig      |   8 +
+ drivers/mtd/nand/spi/core.c       | 101 ++++++----
+ drivers/mtd/nand/spi/gigadevice.c | 322 ++++++++++++++++++++++++++----
+ drivers/mtd/nand/spi/macronix.c   | 173 +++++++++++++---
+ drivers/mtd/nand/spi/micron.c     |  50 ++---
+ drivers/mtd/nand/spi/toshiba.c    |  66 +++---
+ drivers/mtd/nand/spi/winbond.c    | 171 +++++++++++++---
+ include/linux/mtd/spinand.h       |  86 +++++---
+ 8 files changed, 753 insertions(+), 224 deletions(-)
+
+--- a/drivers/mtd/nand/spi/Kconfig
++++ b/drivers/mtd/nand/spi/Kconfig
+@@ -5,3 +5,11 @@ menuconfig MTD_SPI_NAND
+       select SPI_MEM
+       help
+         This is the framework for the SPI NAND device drivers.
++
++config MTD_SPI_NAND_W25N01KV
++      tristate "Winbond W25N01KV Support"
++      select MTD_SPI_NAND
++      default n
++      help
++        Winbond W25N01KV share the same ID with W25N01GV. However, they have
++        different attributes.
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -17,6 +17,7 @@
+ #include <linux/mtd/spinand.h>
+ #include <linux/of.h>
+ #include <linux/slab.h>
++#include <linux/string.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi-mem.h>
+ #else
+@@ -451,10 +452,11 @@ out:
+       return status & STATUS_BUSY ? -ETIMEDOUT : 0;
+ }
+-static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf)
++static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr,
++                            u8 ndummy, u8 *buf)
+ {
+-      struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf,
+-                                               SPINAND_MAX_ID_LEN);
++      struct spi_mem_op op = SPINAND_READID_OP(
++              naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN);
+       int ret;
+       ret = spi_mem_exec_op(spinand->slave, &op);
+@@ -464,18 +466,6 @@ static int spinand_read_id_op(struct spi
+       return ret;
+ }
+-static int spinand_reset_op(struct spinand_device *spinand)
+-{
+-      struct spi_mem_op op = SPINAND_RESET_OP;
+-      int ret;
+-
+-      ret = spi_mem_exec_op(spinand->slave, &op);
+-      if (ret)
+-              return ret;
+-
+-      return spinand_wait(spinand, NULL);
+-}
+-
+ static int spinand_lock_block(struct spinand_device *spinand, u8 lock)
+ {
+       return spinand_write_reg_op(spinand, REG_BLOCK_LOCK, lock);
+@@ -836,24 +826,63 @@ static const struct spinand_manufacturer
+       &winbond_spinand_manufacturer,
+ };
+-static int spinand_manufacturer_detect(struct spinand_device *spinand)
++static int spinand_manufacturer_match(struct spinand_device *spinand,
++                                    enum spinand_readid_method rdid_method)
+ {
++      u8 *id = spinand->id.data;
+       unsigned int i;
+       int ret;
+       for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) {
+-              ret = spinand_manufacturers[i]->ops->detect(spinand);
+-              if (ret > 0) {
+-                      spinand->manufacturer = spinand_manufacturers[i];
+-                      return 0;
+-              } else if (ret < 0) {
+-                      return ret;
+-              }
++              const struct spinand_manufacturer *manufacturer =
++                      spinand_manufacturers[i];
++
++              if (id[0] != manufacturer->id)
++                      continue;
++
++              ret = spinand_match_and_init(spinand,
++                                           manufacturer->chips,
++                                           manufacturer->nchips,
++                                           rdid_method);
++              if (ret < 0)
++                      continue;
++
++              spinand->manufacturer = manufacturer;
++              return 0;
+       }
+       return -ENOTSUPP;
+ }
++static int spinand_id_detect(struct spinand_device *spinand)
++{
++      u8 *id = spinand->id.data;
++      int ret;
++
++      ret = spinand_read_id_op(spinand, 0, 0, id);
++      if (ret)
++              return ret;
++      ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE);
++      if (!ret)
++              return 0;
++
++      ret = spinand_read_id_op(spinand, 1, 0, id);
++      if (ret)
++              return ret;
++      ret = spinand_manufacturer_match(spinand,
++                                       SPINAND_READID_METHOD_OPCODE_ADDR);
++      if (!ret)
++              return 0;
++
++      ret = spinand_read_id_op(spinand, 0, 1, id);
++      if (ret)
++              return ret;
++      ret = spinand_manufacturer_match(spinand,
++                                       SPINAND_READID_METHOD_OPCODE_DUMMY);
++
++      return ret;
++}
++
+ static int spinand_manufacturer_init(struct spinand_device *spinand)
+ {
+       if (spinand->manufacturer->ops->init)
+@@ -909,9 +938,9 @@ spinand_select_op_variant(struct spinand
+  * @spinand: SPI NAND object
+  * @table: SPI NAND device description table
+  * @table_size: size of the device description table
++ * @rdid_method: read id method to match
+  *
+- * Should be used by SPI NAND manufacturer drivers when they want to find a
+- * match between a device ID retrieved through the READ_ID command and an
++ * Match between a device ID retrieved through the READ_ID command and an
+  * entry in the SPI NAND description table. If a match is found, the spinand
+  * object will be initialized with information provided by the matching
+  * spinand_info entry.
+@@ -920,8 +949,10 @@ spinand_select_op_variant(struct spinand
+  */
+ int spinand_match_and_init(struct spinand_device *spinand,
+                          const struct spinand_info *table,
+-                         unsigned int table_size, u8 devid)
++                         unsigned int table_size,
++                         enum spinand_readid_method rdid_method)
+ {
++      u8 *id = spinand->id.data;
+       struct nand_device *nand = spinand_to_nand(spinand);
+       unsigned int i;
+@@ -929,13 +960,17 @@ int spinand_match_and_init(struct spinan
+               const struct spinand_info *info = &table[i];
+               const struct spi_mem_op *op;
+-              if (devid != info->devid)
++              if (rdid_method != info->devid.method)
++                      continue;
++
++              if (memcmp(id + 1, info->devid.id, info->devid.len))
+                       continue;
+               nand->memorg = table[i].memorg;
+               nand->eccreq = table[i].eccreq;
+               spinand->eccinfo = table[i].eccinfo;
+               spinand->flags = table[i].flags;
++              spinand->id.len = 1 + table[i].devid.len;
+               spinand->select_target = table[i].select_target;
+               op = spinand_select_op_variant(spinand,
+@@ -967,17 +1002,7 @@ static int spinand_detect(struct spinand
+       struct nand_device *nand = spinand_to_nand(spinand);
+       int ret;
+-      ret = spinand_reset_op(spinand);
+-      if (ret)
+-              return ret;
+-
+-      ret = spinand_read_id_op(spinand, spinand->id.data);
+-      if (ret)
+-              return ret;
+-
+-      spinand->id.len = SPINAND_MAX_ID_LEN;
+-
+-      ret = spinand_manufacturer_detect(spinand);
++      ret = spinand_id_detect(spinand);
+       if (ret) {
+               dev_err(spinand->slave->dev, "unknown raw ID %*phN\n",
+                       SPINAND_MAX_ID_LEN, spinand->id.data);
+--- a/drivers/mtd/nand/spi/gigadevice.c
++++ b/drivers/mtd/nand/spi/gigadevice.c
+@@ -22,8 +22,13 @@
+ #define GD5FXGQXXEXXG_REG_STATUS2             0xf0
++#define GD5FXGQ4UXFXXG_STATUS_ECC_MASK                (7 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS (0 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS        (1 << 4)
++#define GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR (7 << 4)
++
+ /* Q4 devices, QUADIO: Dummy bytes valid for 1 and 2 GBit variants */
+-static SPINAND_OP_VARIANTS(gd5fxgq4_read_cache_variants,
++static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+@@ -31,8 +36,17 @@ static SPINAND_OP_VARIANTS(gd5fxgq4_read
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+-/* Q5 devices, QUADIO: Dummy bytes only valid for 1 GBit variants */
+-static SPINAND_OP_VARIANTS(gd5f1gq5_read_cache_variants,
++static SPINAND_OP_VARIANTS(read_cache_variants_f,
++              SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
++
++/* For Q5 devices, QUADIO use different dummy byte settings */
++/* Q5 1Gb */
++static SPINAND_OP_VARIANTS(dummy2_read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+@@ -40,6 +54,15 @@ static SPINAND_OP_VARIANTS(gd5f1gq5_read
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++/* Q5 2Gb & 4Gb */
++static SPINAND_OP_VARIANTS(dummy4_read_cache_variants,
++              SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
++              SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
++
+ static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+               SPINAND_PROG_LOAD(true, 0, NULL, 0));
+@@ -48,7 +71,65 @@ static SPINAND_OP_VARIANTS(update_cache_
+               SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD(false, 0, NULL, 0));
+-static int gd5fxgqxxexxg_ooblayout_ecc(struct mtd_info *mtd, int section,
++static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = (16 * section) + 8;
++      region->length = 8;
++
++      return 0;
++}
++
++static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
++                                 struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      if (section) {
++              region->offset = 16 * section;
++              region->length = 8;
++      } else {
++              /* section 0 has one byte reserved for bad block mark */
++              region->offset = 1;
++              region->length = 7;
++      }
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
++      .ecc = gd5fxgq4xa_ooblayout_ecc,
++      .rfree = gd5fxgq4xa_ooblayout_free,
++};
++
++static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
++                                       u8 status)
++{
++      switch (status & STATUS_ECC_MASK) {
++      case STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
++              /* 1-7 bits are flipped. return the maximum. */
++              return 7;
++
++      case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
++              return 8;
++
++      case STATUS_ECC_UNCOR_ERROR:
++              return -EBADMSG;
++
++      default:
++              break;
++      }
++
++      return -EINVAL;
++}
++
++static int gd5fxgqx_variant2_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                      struct mtd_oob_region *region)
+ {
+       if (section)
+@@ -60,7 +141,7 @@ static int gd5fxgqxxexxg_ooblayout_ecc(s
+       return 0;
+ }
+-static int gd5fxgqxxexxg_ooblayout_free(struct mtd_info *mtd, int section,
++static int gd5fxgqx_variant2_ooblayout_free(struct mtd_info *mtd, int section,
+                                       struct mtd_oob_region *region)
+ {
+       if (section)
+@@ -73,7 +154,13 @@ static int gd5fxgqxxexxg_ooblayout_free(
+       return 0;
+ }
+-static int gd5fxgq4xexxg_ecc_get_status(struct spinand_device *spinand,
++/* Valid for Q4/Q5 and Q6 (untested) devices */
++static const struct mtd_ooblayout_ops gd5fxgqx_variant2_ooblayout = {
++      .ecc = gd5fxgqx_variant2_ooblayout_ecc,
++      .rfree = gd5fxgqx_variant2_ooblayout_free,
++};
++
++static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand,
+                                       u8 status)
+ {
+       u8 status2;
+@@ -152,59 +239,214 @@ static int gd5fxgq5xexxg_ecc_get_status(
+       return -EINVAL;
+ }
+-static const struct mtd_ooblayout_ops gd5fxgqxxexxg_ooblayout = {
+-      .ecc = gd5fxgqxxexxg_ooblayout_ecc,
+-      .rfree = gd5fxgqxxexxg_ooblayout_free,
++static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand,
++                                      u8 status)
++{
++      switch (status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) {
++      case GD5FXGQ4UXFXXG_STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case GD5FXGQ4UXFXXG_STATUS_ECC_1_3_BITFLIPS:
++              return 3;
++
++      case GD5FXGQ4UXFXXG_STATUS_ECC_UNCOR_ERROR:
++              return -EBADMSG;
++
++      default: /* (2 << 4) through (6 << 4) are 4-8 corrected errors */
++              return ((status & GD5FXGQ4UXFXXG_STATUS_ECC_MASK) >> 4) + 2;
++      }
++
++      return -EINVAL;
++}
++
++static int esmt_1_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = (16 * section) + 8;
++      region->length = 8;
++
++      return 0;
++}
++
++static int esmt_1_ooblayout_free(struct mtd_info *mtd, int section,
++                                 struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = (16 * section) + 2;
++      region->length = 6;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops esmt_1_ooblayout = {
++      .ecc = esmt_1_ooblayout_ecc,
++      .rfree = esmt_1_ooblayout_free,
+ };
+ static const struct spinand_info gigadevice_spinand_table[] = {
+-      SPINAND_INFO("GD5F1GQ4UExxG", 0xd1,
+-                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++      SPINAND_INFO("F50L1G41LB",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+-                   SPINAND_INFO_OP_VARIANTS(&gd5fxgq4_read_cache_variants,
++                   SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+-                   SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout,
+-                                   gd5fxgq4xexxg_ecc_get_status)),
+-      SPINAND_INFO("GD5F1GQ5UExxG", 0x51,
++                   SPINAND_ECCINFO(&esmt_1_ooblayout, NULL)),
++      SPINAND_INFO("GD5F1GQ4xA",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++                                   gd5fxgq4xa_ecc_get_status)),
++      SPINAND_INFO("GD5F2GQ4xA",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2),
++                   NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++                                   gd5fxgq4xa_ecc_get_status)),
++      SPINAND_INFO("GD5F4GQ4xA",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4),
++                   NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
++                                   gd5fxgq4xa_ecc_get_status)),
++      SPINAND_INFO("GD5F1GQ4UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1),
++                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq4uexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F1GQ4UFxxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
++                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq4ufxxg_ecc_get_status)),
++      SPINAND_INFO("GD5F1GQ5UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+-                   SPINAND_INFO_OP_VARIANTS(&gd5f1gq5_read_cache_variants,
++                   SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+-                   0,
+-                   SPINAND_ECCINFO(&gd5fxgqxxexxg_ooblayout,
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq5xexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F2GQ5UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52),
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq5xexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F4GQ6UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x55),
++                   NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq5xexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F1GM7UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x91),
++                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq4uexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F2GM7UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x92),
++                       NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
++                       NAND_ECCREQ(8, 512),
++                       SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                       SPINAND_HAS_QE_BIT,
++                       SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq4uexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F4GM8UExxG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x95),
++                       NAND_MEMORG(1, 2048, 128, 64, 4096, 1, 1, 1),
++                       NAND_ECCREQ(8, 512),
++                       SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                       SPINAND_HAS_QE_BIT,
++                       SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq4uexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F1GQ5UExxH",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x31),
++                       NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++                       NAND_ECCREQ(4, 512),
++                       SPINAND_INFO_OP_VARIANTS(&dummy2_read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                       SPINAND_HAS_QE_BIT,
++                       SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq5xexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F2GQ5UExxH",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x32),
++                       NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++                       NAND_ECCREQ(4, 512),
++                       SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                       SPINAND_HAS_QE_BIT,
++                       SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
++                                   gd5fxgq5xexxg_ecc_get_status)),
++      SPINAND_INFO("GD5F4GQ6UExxH",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
++                       NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
++                       NAND_ECCREQ(4, 512),
++                       SPINAND_INFO_OP_VARIANTS(&dummy4_read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                       SPINAND_HAS_QE_BIT,
++                       SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+                                    gd5fxgq5xexxg_ecc_get_status)),
+ };
+-static int gigadevice_spinand_detect(struct spinand_device *spinand)
+-{
+-      u8 *id = spinand->id.data;
+-      int ret;
+-
+-      /*
+-       * For GD NANDs, There is an address byte needed to shift in before IDs
+-       * are read out, so the first byte in raw_id is dummy.
+-       */
+-      if (id[1] != SPINAND_MFR_GIGADEVICE)
+-              return 0;
+-
+-      ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
+-                                   ARRAY_SIZE(gigadevice_spinand_table),
+-                                   id[2]);
+-      if (ret)
+-              return ret;
+-
+-      return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
+-      .detect = gigadevice_spinand_detect,
+ };
+ const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
+       .id = SPINAND_MFR_GIGADEVICE,
+       .name = "GigaDevice",
++      .chips = gigadevice_spinand_table,
++      .nchips = ARRAY_SIZE(gigadevice_spinand_table),
+       .ops = &gigadevice_spinand_manuf_ops,
+ };
+--- a/drivers/mtd/nand/spi/macronix.c
++++ b/drivers/mtd/nand/spi/macronix.c
+@@ -105,7 +105,8 @@ static int mx35lf1ge4ab_ecc_get_status(s
+ }
+ static const struct spinand_info macronix_spinand_table[] = {
+-      SPINAND_INFO("MX35LF1GE4AB", 0x12,
++      SPINAND_INFO("MX35LF1GE4AB",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -114,7 +115,8 @@ static const struct spinand_info macroni
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+                                    mx35lf1ge4ab_ecc_get_status)),
+-      SPINAND_INFO("MX35LF2GE4AB", 0x22,
++      SPINAND_INFO("MX35LF2GE4AB",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22),
+                    NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -122,7 +124,96 @@ static const struct spinand_info macroni
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
+-      SPINAND_INFO("MX35UF4GE4AD", 0xb7,
++      SPINAND_INFO("MX35LF2GE4AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x26),
++                   NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35LF4GE4AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x37),
++                   NAND_MEMORG(1, 4096, 128, 64, 2048, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35LF1G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
++                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++      SPINAND_INFO("MX35LF2G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++      SPINAND_INFO("MX35LF4G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
++                   NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)),
++      SPINAND_INFO("MX31LF1GE4BC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x1e),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX31UF1GE4BC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x9e),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++
++      SPINAND_INFO("MX35LF2G14AC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x20),
++                   NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF4G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb5),
++                   NAND_MEMORG(1, 4096, 256, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF4GE4AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb7),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -131,7 +222,28 @@ static const struct spinand_info macroni
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+                                    mx35lf1ge4ab_ecc_get_status)),
+-      SPINAND_INFO("MX35UF2GE4AD", 0xa6,
++      SPINAND_INFO("MX35UF2G14AC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa0),
++                   NAND_MEMORG(1, 2048, 64, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF2G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa4),
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF2GE4AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa6),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -140,16 +252,28 @@ static const struct spinand_info macroni
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+                                    mx35lf1ge4ab_ecc_get_status)),
+-      SPINAND_INFO("MX35UF2GE4AC", 0xa2,
++      SPINAND_INFO("MX35UF2GE4AC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xa2),
+                    NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
++                       SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF1G14AC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x90),
++                   NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+                                    mx35lf1ge4ab_ecc_get_status)),
+-      SPINAND_INFO("MX35UF1GE4AD", 0x96,
++      SPINAND_INFO("MX35UF1G24AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x94),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -158,7 +282,18 @@ static const struct spinand_info macroni
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
+                                    mx35lf1ge4ab_ecc_get_status)),
+-      SPINAND_INFO("MX35UF1GE4AC", 0x92,
++      SPINAND_INFO("MX35UF1GE4AD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x96),
++                   NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(8, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   SPINAND_HAS_QE_BIT,
++                   SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout,
++                                   mx35lf1ge4ab_ecc_get_status)),
++      SPINAND_INFO("MX35UF1GE4AC",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(4, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -170,33 +305,13 @@ static const struct spinand_info macroni
+ };
+-static int macronix_spinand_detect(struct spinand_device *spinand)
+-{
+-      u8 *id = spinand->id.data;
+-      int ret;
+-
+-      /*
+-       * Macronix SPI NAND read ID needs a dummy byte, so the first byte in
+-       * raw_id is garbage.
+-       */
+-      if (id[1] != SPINAND_MFR_MACRONIX)
+-              return 0;
+-
+-      ret = spinand_match_and_init(spinand, macronix_spinand_table,
+-                                   ARRAY_SIZE(macronix_spinand_table),
+-                                   id[2]);
+-      if (ret)
+-              return ret;
+-
+-      return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = {
+-      .detect = macronix_spinand_detect,
+ };
+ const struct spinand_manufacturer macronix_spinand_manufacturer = {
+       .id = SPINAND_MFR_MACRONIX,
+       .name = "Macronix",
++      .chips = macronix_spinand_table,
++      .nchips = ARRAY_SIZE(macronix_spinand_table),
+       .ops = &macronix_spinand_manuf_ops,
+ };
+--- a/drivers/mtd/nand/spi/micron.c
++++ b/drivers/mtd/nand/spi/micron.c
+@@ -120,7 +120,8 @@ static int micron_8_ecc_get_status(struc
+ static const struct spinand_info micron_spinand_table[] = {
+       /* M79A 2Gb 3.3V */
+-      SPINAND_INFO("MT29F2G01ABAGD", 0x24,
++      SPINAND_INFO("MT29F2G01ABAGD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -130,7 +131,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M79A 2Gb 1.8V */
+-      SPINAND_INFO("MT29F2G01ABBGD", 0x25,
++      SPINAND_INFO("MT29F2G01ABBGD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -140,7 +142,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M78A 1Gb 3.3V */
+-      SPINAND_INFO("MT29F1G01ABAFD", 0x14,
++      SPINAND_INFO("MT29F1G01ABAFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x14),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -150,7 +153,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M78A 1Gb 1.8V */
+-      SPINAND_INFO("MT29F1G01ABAFD", 0x15,
++      SPINAND_INFO("MT29F1G01ABAFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -160,7 +164,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M79A 4Gb 3.3V */
+-      SPINAND_INFO("MT29F4G01ADAGD", 0x36,
++      SPINAND_INFO("MT29F4G01ADAGD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x36),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 2),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -171,7 +176,8 @@ static const struct spinand_info micron_
+                                    micron_8_ecc_get_status),
+                    SPINAND_SELECT_TARGET(micron_select_target)),
+       /* M70A 4Gb 3.3V */
+-      SPINAND_INFO("MT29F4G01ABAFD", 0x34,
++      SPINAND_INFO("MT29F4G01ABAFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -181,7 +187,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M70A 4Gb 1.8V */
+-      SPINAND_INFO("MT29F4G01ABBFD", 0x35,
++      SPINAND_INFO("MT29F4G01ABBFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -191,7 +198,8 @@ static const struct spinand_info micron_
+                    SPINAND_ECCINFO(&micron_8_ooblayout,
+                                    micron_8_ecc_get_status)),
+       /* M70A 8Gb 3.3V */
+-      SPINAND_INFO("MT29F8G01ADAFD", 0x46,
++      SPINAND_INFO("MT29F8G01ADAFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x46),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -202,7 +210,8 @@ static const struct spinand_info micron_
+                                    micron_8_ecc_get_status),
+                    SPINAND_SELECT_TARGET(micron_select_target)),
+       /* M70A 8Gb 1.8V */
+-      SPINAND_INFO("MT29F8G01ADBFD", 0x47,
++      SPINAND_INFO("MT29F8G01ADBFD",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x47),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 2),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -214,26 +223,6 @@ static const struct spinand_info micron_
+                    SPINAND_SELECT_TARGET(micron_select_target)),
+ };
+-static int micron_spinand_detect(struct spinand_device *spinand)
+-{
+-      u8 *id = spinand->id.data;
+-      int ret;
+-
+-      /*
+-       * Micron SPI NAND read ID need a dummy byte,
+-       * so the first byte in raw_id is dummy.
+-       */
+-      if (id[1] != SPINAND_MFR_MICRON)
+-              return 0;
+-
+-      ret = spinand_match_and_init(spinand, micron_spinand_table,
+-                                   ARRAY_SIZE(micron_spinand_table), id[2]);
+-      if (ret)
+-              return ret;
+-
+-      return 1;
+-}
+-
+ static int micron_spinand_init(struct spinand_device *spinand)
+ {
+       /*
+@@ -248,12 +237,13 @@ static int micron_spinand_init(struct sp
+ }
+ static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = {
+-      .detect = micron_spinand_detect,
+       .init = micron_spinand_init,
+ };
+ const struct spinand_manufacturer micron_spinand_manufacturer = {
+       .id = SPINAND_MFR_MICRON,
+       .name = "Micron",
++      .chips = micron_spinand_table,
++      .nchips = ARRAY_SIZE(micron_spinand_table),
+       .ops = &micron_spinand_manuf_ops,
+ };
+--- a/drivers/mtd/nand/spi/toshiba.c
++++ b/drivers/mtd/nand/spi/toshiba.c
+@@ -111,7 +111,8 @@ static int tx58cxgxsxraix_ecc_get_status
+ static const struct spinand_info toshiba_spinand_table[] = {
+       /* 3.3V 1Gb (1st generation) */
+-      SPINAND_INFO("TC58CVG0S3HRAIG", 0xC2,
++      SPINAND_INFO("TC58CVG0S3HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -121,7 +122,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 3.3V 2Gb (1st generation) */
+-      SPINAND_INFO("TC58CVG1S3HRAIG", 0xCB,
++      SPINAND_INFO("TC58CVG1S3HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -131,7 +133,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 3.3V 4Gb (1st generation) */
+-      SPINAND_INFO("TC58CVG2S0HRAIG", 0xCD,
++      SPINAND_INFO("TC58CVG2S0HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -141,7 +144,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 1Gb (1st generation) */
+-      SPINAND_INFO("TC58CYG0S3HRAIG", 0xB2,
++      SPINAND_INFO("TC58CYG0S3HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -151,7 +155,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 2Gb (1st generation) */
+-      SPINAND_INFO("TC58CYG1S3HRAIG", 0xBB,
++      SPINAND_INFO("TC58CYG1S3HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -161,7 +166,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 4Gb (1st generation) */
+-      SPINAND_INFO("TC58CYG2S0HRAIG", 0xBD,
++      SPINAND_INFO("TC58CYG2S0HRAIG",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -176,7 +182,8 @@ static const struct spinand_info toshiba
+        * QE_BIT.
+        */
+       /* 3.3V 1Gb (2nd generation) */
+-      SPINAND_INFO("TC58CVG0S3HRAIJ", 0xE2,
++      SPINAND_INFO("TC58CVG0S3HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE2),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -186,7 +193,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 3.3V 2Gb (2nd generation) */
+-      SPINAND_INFO("TC58CVG1S3HRAIJ", 0xEB,
++      SPINAND_INFO("TC58CVG1S3HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xEB),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -196,7 +204,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 3.3V 4Gb (2nd generation) */
+-      SPINAND_INFO("TC58CVG2S0HRAIJ", 0xED,
++      SPINAND_INFO("TC58CVG2S0HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -206,7 +215,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 3.3V 8Gb (2nd generation) */
+-      SPINAND_INFO("TH58CVG3S0HRAIJ", 0xE4,
++      SPINAND_INFO("TH58CVG3S0HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
+                    NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -216,7 +226,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 1Gb (2nd generation) */
+-      SPINAND_INFO("TC58CYG0S3HRAIJ", 0xD2,
++      SPINAND_INFO("TC58CYG0S3HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD2),
+                    NAND_MEMORG(1, 2048, 128, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -226,7 +237,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 2Gb (2nd generation) */
+-      SPINAND_INFO("TC58CYG1S3HRAIJ", 0xDB,
++      SPINAND_INFO("TC58CYG1S3HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDB),
+                    NAND_MEMORG(1, 2048, 128, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -236,7 +248,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 4Gb (2nd generation) */
+-      SPINAND_INFO("TC58CYG2S0HRAIJ", 0xDD,
++      SPINAND_INFO("TC58CYG2S0HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xDD),
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -246,7 +259,8 @@ static const struct spinand_info toshiba
+                    SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout,
+                                    tx58cxgxsxraix_ecc_get_status)),
+       /* 1.8V 8Gb (2nd generation) */
+-      SPINAND_INFO("TH58CYG3S0HRAIJ", 0xD4,
++      SPINAND_INFO("TH58CYG3S0HRAIJ",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4),
+                    NAND_MEMORG(1, 4096, 256, 64, 4096, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -257,33 +271,13 @@ static const struct spinand_info toshiba
+                                    tx58cxgxsxraix_ecc_get_status)),
+ };
+-static int toshiba_spinand_detect(struct spinand_device *spinand)
+-{
+-      u8 *id = spinand->id.data;
+-      int ret;
+-
+-      /*
+-       * Toshiba SPI NAND read ID needs a dummy byte,
+-       * so the first byte in id is garbage.
+-       */
+-      if (id[1] != SPINAND_MFR_TOSHIBA)
+-              return 0;
+-
+-      ret = spinand_match_and_init(spinand, toshiba_spinand_table,
+-                                   ARRAY_SIZE(toshiba_spinand_table),
+-                                   id[2]);
+-      if (ret)
+-              return ret;
+-
+-      return 1;
+-}
+-
+ static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
+-      .detect = toshiba_spinand_detect,
+ };
+ const struct spinand_manufacturer toshiba_spinand_manufacturer = {
+       .id = SPINAND_MFR_TOSHIBA,
+       .name = "Toshiba",
++      .chips = toshiba_spinand_table,
++      .nchips = ARRAY_SIZE(toshiba_spinand_table),
+       .ops = &toshiba_spinand_manuf_ops,
+ };
+--- a/drivers/mtd/nand/spi/winbond.c
++++ b/drivers/mtd/nand/spi/winbond.c
+@@ -19,6 +19,25 @@
+ #define WINBOND_CFG_BUF_READ          BIT(3)
++#define W25N02_N04KV_STATUS_ECC_MASK          (3 << 4)
++#define W25N02_N04KV_STATUS_ECC_NO_BITFLIPS   (0 << 4)
++#define W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS  (1 << 4)
++#define W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS  (3 << 4)
++#define W25N02_N04KV_STATUS_ECC_UNCOR_ERROR   (2 << 4)
++
++#define W25N01_M02GV_STATUS_ECC_MASK          (3 << 4)
++#define W25N01_M02GV_STATUS_ECC_NO_BITFLIPS   (0 << 4)
++#define W25N01_M02GV_STATUS_ECC_1_BITFLIPS    (1 << 4)
++#define W25N01_M02GV_STATUS_ECC_UNCOR_ERROR   (2 << 4)
++
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++#define W25N01KV_STATUS_ECC_MASK              (3 << 4)
++#define W25N01KV_STATUS_ECC_NO_BITFLIPS               (0 << 4)
++#define W25N01KV_STATUS_ECC_1_3_BITFLIPS      (1 << 4)
++#define W25N01KV_STATUS_ECC_4_BITFLIPS                (3 << 4)
++#define W25N01KV_STATUS_ECC_UNCOR_ERROR               (2 << 4)
++#endif
++
+ static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+@@ -35,6 +54,35 @@ static SPINAND_OP_VARIANTS(update_cache_
+               SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD(false, 0, NULL, 0));
++static int w25n02kv_n04kv_ooblayout_ecc(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = (16 * section) + 64;
++      region->length = 16;
++
++      return 0;
++}
++
++static int w25n02kv_n04kv_ooblayout_free(struct mtd_info *mtd, int section,
++                                 struct mtd_oob_region *region)
++{
++      if (section > 3)
++              return -ERANGE;
++
++      region->offset = (16 * section) + 2;
++      region->length = 14;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops w25n02kv_n04kv_ooblayout = {
++      .ecc = w25n02kv_n04kv_ooblayout_ecc,
++      .rfree = w25n02kv_n04kv_ooblayout_free,
++};
++
+ static int w25m02gv_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+ {
+@@ -78,8 +126,63 @@ static int w25m02gv_select_target(struct
+       return spi_mem_exec_op(spinand->slave, &op);
+ }
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++static int w25n01kv_ecc_get_status(struct spinand_device *spinand,
++                                      u8 status)
++{
++      switch (status & W25N01KV_STATUS_ECC_MASK) {
++      case W25N01KV_STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case W25N01KV_STATUS_ECC_1_3_BITFLIPS:
++              return 3;
++
++      case W25N01KV_STATUS_ECC_4_BITFLIPS:
++              return 4;
++
++      case W25N01KV_STATUS_ECC_UNCOR_ERROR:
++              return -EBADMSG;
++
++      default:
++              break;
++      }
++
++      return -EINVAL;
++}
++#endif
++
++static int w25n02kv_n04kv_ecc_get_status(struct spinand_device *spinand,
++                                      u8 status)
++{
++      switch (status & W25N02_N04KV_STATUS_ECC_MASK) {
++      case W25N02_N04KV_STATUS_ECC_NO_BITFLIPS:
++              return 0;
++
++      case W25N02_N04KV_STATUS_ECC_1_4_BITFLIPS:
++              return 3;
++
++      case W25N02_N04KV_STATUS_ECC_5_8_BITFLIPS:
++              return 4;
++
++      /* W25N02_N04KV_use internal 8bit ECC algorithm.
++       * But the ECC strength is 4 bit requried.
++       * Return 3 if the bit bit flip count less than 5.
++       * Return 4 if the bit bit flip count more than 5 to 8.
++      */
++
++      case W25N02_N04KV_STATUS_ECC_UNCOR_ERROR:
++              return -EBADMSG;
++
++      default:
++              break;
++      }
++
++      return -EINVAL;
++}
++
+ static const struct spinand_info winbond_spinand_table[] = {
+-      SPINAND_INFO("W25M02GV", 0xAB,
++      SPINAND_INFO("W25M02GV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 2),
+                    NAND_ECCREQ(1, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -88,7 +191,19 @@ static const struct spinand_info winbond
+                    0,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
+                    SPINAND_SELECT_TARGET(w25m02gv_select_target)),
+-      SPINAND_INFO("W25N01GV", 0xAA,
++#if IS_ENABLED(CONFIG_MTD_SPI_NAND_W25N01KV)
++      SPINAND_INFO("W25N01KV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
++                   NAND_MEMORG(1, 2048, 96, 64, 1024, 1, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout, w25n01kv_ecc_get_status)),
++#else
++      SPINAND_INFO("W25N01GV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21),
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(1, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+@@ -96,32 +211,31 @@ static const struct spinand_info winbond
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
+-};
+-
+-/**
+- * winbond_spinand_detect - initialize device related part in spinand_device
+- * struct if it is a Winbond device.
+- * @spinand: SPI NAND device structure
+- */
+-static int winbond_spinand_detect(struct spinand_device *spinand)
+-{
+-      u8 *id = spinand->id.data;
+-      int ret;
+-
+-      /*
+-       * Winbond SPI NAND read ID need a dummy byte,
+-       * so the first byte in raw_id is dummy.
++#endif
++      SPINAND_INFO("W25N02KV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22),
++                   NAND_MEMORG(1, 2048, 128, 64, 2048, 2, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout,
++                                   w25n02kv_n04kv_ecc_get_status)),
++      /* W25N04KV has 2-die(lun), however, it can select die automatically.
++       * Treat it as single die here and double block size.
+        */
+-      if (id[1] != SPINAND_MFR_WINBOND)
+-              return 0;
+-
+-      ret = spinand_match_and_init(spinand, winbond_spinand_table,
+-                                   ARRAY_SIZE(winbond_spinand_table), id[2]);
+-      if (ret)
+-              return ret;
+-
+-      return 1;
+-}
++      SPINAND_INFO("W25N04KV",
++                   SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
++                   NAND_MEMORG(1, 2048, 128, 64, 4096, 2, 1, 1),
++                   NAND_ECCREQ(4, 512),
++                   SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
++                                            &write_cache_variants,
++                                            &update_cache_variants),
++                   0,
++                   SPINAND_ECCINFO(&w25n02kv_n04kv_ooblayout,
++                                   w25n02kv_n04kv_ecc_get_status)),
++};
+ static int winbond_spinand_init(struct spinand_device *spinand)
+ {
+@@ -142,12 +256,13 @@ static int winbond_spinand_init(struct s
+ }
+ static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = {
+-      .detect = winbond_spinand_detect,
+       .init = winbond_spinand_init,
+ };
+ const struct spinand_manufacturer winbond_spinand_manufacturer = {
+       .id = SPINAND_MFR_WINBOND,
+       .name = "Winbond",
++      .chips = winbond_spinand_table,
++      .nchips = ARRAY_SIZE(winbond_spinand_table),
+       .ops = &winbond_spinand_manuf_ops,
+ };
+--- a/include/linux/mtd/spinand.h
++++ b/include/linux/mtd/spinand.h
+@@ -39,15 +39,15 @@
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_NO_DATA)
+-#define SPINAND_READID_OP(ndummy, buf, len)                           \
++#define SPINAND_READID_OP(naddr, ndummy, buf, len)                    \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1),                             \
+-                 SPI_MEM_OP_NO_ADDR,                                  \
++                 SPI_MEM_OP_ADDR(naddr, 0, 1),                        \
+                  SPI_MEM_OP_DUMMY(ndummy, 1),                         \
+                  SPI_MEM_OP_DATA_IN(len, buf, 1))
+ #define SPINAND_SET_FEATURE_OP(reg, valptr)                           \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1),                             \
+-                 SPI_MEM_OP_ADDR(1, reg, 1),                          \
++                 SPI_MEM_OP_ADDR(1, reg, 1),                                  \
+                  SPI_MEM_OP_NO_DUMMY,                                 \
+                  SPI_MEM_OP_DATA_OUT(1, valptr, 1))
+@@ -75,18 +75,36 @@
+                  SPI_MEM_OP_DUMMY(ndummy, 1),                         \
+                  SPI_MEM_OP_DATA_IN(len, buf, 1))
++#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len)\
++      SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1),               \
++                 SPI_MEM_OP_ADDR(3, addr, 1),                         \
++                 SPI_MEM_OP_DUMMY(ndummy, 1),                         \
++                 SPI_MEM_OP_DATA_IN(len, buf, 1))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len)    \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),                             \
+                  SPI_MEM_OP_ADDR(2, addr, 1),                         \
+                  SPI_MEM_OP_DUMMY(ndummy, 1),                         \
+                  SPI_MEM_OP_DATA_IN(len, buf, 2))
++#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len) \
++      SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1),                             \
++                 SPI_MEM_OP_ADDR(3, addr, 1),                         \
++                 SPI_MEM_OP_DUMMY(ndummy, 1),                         \
++                 SPI_MEM_OP_DATA_IN(len, buf, 2))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len)    \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),                             \
+                  SPI_MEM_OP_ADDR(2, addr, 1),                         \
+                  SPI_MEM_OP_DUMMY(ndummy, 1),                         \
+                  SPI_MEM_OP_DATA_IN(len, buf, 4))
++#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len) \
++      SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1),                             \
++                 SPI_MEM_OP_ADDR(3, addr, 1),                         \
++                 SPI_MEM_OP_DUMMY(ndummy, 1),                         \
++                 SPI_MEM_OP_DATA_IN(len, buf, 4))
++
+ #define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len)        \
+       SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1),                             \
+                  SPI_MEM_OP_ADDR(2, addr, 2),                         \
+@@ -153,37 +171,46 @@ struct spinand_device;
+  * @data: buffer containing the id bytes. Currently 4 bytes large, but can
+  *      be extended if required
+  * @len: ID length
+- *
+- * struct_spinand_id->data contains all bytes returned after a READ_ID command,
+- * including dummy bytes if the chip does not emit ID bytes right after the
+- * READ_ID command. The responsibility to extract real ID bytes is left to
+- * struct_manufacurer_ops->detect().
+  */
+ struct spinand_id {
+       u8 data[SPINAND_MAX_ID_LEN];
+       int len;
+ };
++enum spinand_readid_method {
++      SPINAND_READID_METHOD_OPCODE,
++      SPINAND_READID_METHOD_OPCODE_ADDR,
++      SPINAND_READID_METHOD_OPCODE_DUMMY,
++};
++
++/**
++ * struct spinand_devid - SPI NAND device id structure
++ * @id: device id of current chip
++ * @len: number of bytes in device id
++ * @method: method to read chip id
++ *        There are 3 possible variants:
++ *        SPINAND_READID_METHOD_OPCODE: chip id is returned immediately
++ *        after read_id opcode.
++ *        SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after
++ *        read_id opcode + 1-byte address.
++ *        SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after
++ *        read_id opcode + 1 dummy byte.
++ */
++struct spinand_devid {
++      const u8 *id;
++      const u8 len;
++      const enum spinand_readid_method method;
++};
++
+ /**
+  * struct manufacurer_ops - SPI NAND manufacturer specific operations
+- * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed
+- *        the core calls the struct_manufacurer_ops->detect() hook of each
+- *        registered manufacturer until one of them return 1. Note that
+- *        the first thing to check in this hook is that the manufacturer ID
+- *        in struct_spinand_device->id matches the manufacturer whose
+- *        ->detect() hook has been called. Should return 1 if there's a
+- *        match, 0 if the manufacturer ID does not match and a negative
+- *        error code otherwise. When true is returned, the core assumes
+- *        that properties of the NAND chip (spinand->base.memorg and
+- *        spinand->base.eccreq) have been filled
+  * @init: initialize a SPI NAND device
+  * @cleanup: cleanup a SPI NAND device
+  *
+  * Each SPI NAND manufacturer driver should implement this interface so that
+- * NAND chips coming from this vendor can be detected and initialized properly.
++ * NAND chips coming from this vendor can be initialized properly.
+  */
+ struct spinand_manufacturer_ops {
+-      int (*detect)(struct spinand_device *spinand);
+       int (*init)(struct spinand_device *spinand);
+       void (*cleanup)(struct spinand_device *spinand);
+ };
+@@ -192,11 +219,16 @@ struct spinand_manufacturer_ops {
+  * struct spinand_manufacturer - SPI NAND manufacturer instance
+  * @id: manufacturer ID
+  * @name: manufacturer name
++ * @devid_len: number of bytes in device ID
++ * @chips: supported SPI NANDs under current manufacturer
++ * @nchips: number of SPI NANDs available in chips array
+  * @ops: manufacturer operations
+  */
+ struct spinand_manufacturer {
+       u8 id;
+       char *name;
++      const struct spinand_info *chips;
++      const size_t nchips;
+       const struct spinand_manufacturer_ops *ops;
+ };
+@@ -268,7 +300,7 @@ struct spinand_ecc_info {
+  */
+ struct spinand_info {
+       const char *model;
+-      u8 devid;
++      struct spinand_devid devid;
+       u32 flags;
+       struct nand_memory_organization memorg;
+       struct nand_ecc_req eccreq;
+@@ -282,6 +314,13 @@ struct spinand_info {
+                            unsigned int target);
+ };
++#define SPINAND_ID(__method, ...)                                     \
++      {                                                               \
++              .id = (const u8[]){ __VA_ARGS__ },                      \
++              .len = sizeof((u8[]){ __VA_ARGS__ }),                   \
++              .method = __method,                                     \
++      }
++
+ #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update)           \
+       {                                                               \
+               .read_cache = __read,                                   \
+@@ -440,9 +479,10 @@ static inline void spinand_set_ofnode(st
+ }
+ #endif /* __UBOOT__ */
+-int spinand_match_and_init(struct spinand_device *dev,
++int spinand_match_and_init(struct spinand_device *spinand,
+                          const struct spinand_info *table,
+-                         unsigned int table_size, u8 devid);
++                         unsigned int table_size,
++                         enum spinand_readid_method rdid_method);
+ int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
+ int spinand_select_target(struct spinand_device *spinand, unsigned int target);
diff --git a/package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch b/package/boot/uboot-mediatek/patches/100-23-mmc-mtk-sd-add-support-to-display-verbose-error-log.patch
new file mode 100644 (file)
index 0000000..b6a2229
--- /dev/null
@@ -0,0 +1,78 @@
+From 793bed29e78cc54d989333d756fef51efaca4e56 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Tue, 26 Jul 2022 09:29:18 +0800
+Subject: [PATCH 58/71] mmc: mtk-sd: add support to display verbose error log
+
+Add an option to enable debug log, and also display verbose error log for
+both command and data.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mmc/Kconfig  |  8 ++++++++
+ drivers/mmc/Makefile |  4 ++++
+ drivers/mmc/mtk-sd.c | 24 +++++++++++++++---------
+ 3 files changed, 27 insertions(+), 9 deletions(-)
+
+--- a/drivers/mmc/Kconfig
++++ b/drivers/mmc/Kconfig
+@@ -789,6 +789,14 @@ config MMC_MTK
+         This is needed if support for any SD/SDIO/MMC devices is required.
+         If unsure, say N.
++config MMC_MTK_DEBUG
++      bool "Display verbose error log"
++      default n
++      depends on MMC_MTK
++      help
++        Enable this option to allow verbose error log being displayed for
++        debugging.
++
+ endif
+ config FSL_ESDHC
+--- a/drivers/mmc/Makefile
++++ b/drivers/mmc/Makefile
+@@ -84,3 +84,7 @@ obj-$(CONFIG_RENESAS_SDHI)           += tmio-comm
+ obj-$(CONFIG_MMC_BCM2835)             += bcm2835_sdhost.o
+ obj-$(CONFIG_MMC_MTK)                 += mtk-sd.o
+ obj-$(CONFIG_MMC_SDHCI_F_SDH30)               += f_sdh30.o
++
++ifdef CONFIG_MMC_MTK_DEBUG
++CFLAGS_mtk-sd.o += -DDEBUG
++endif
+--- a/drivers/mmc/mtk-sd.c
++++ b/drivers/mmc/mtk-sd.c
+@@ -778,18 +778,24 @@ static int msdc_ops_send_cmd(struct udev
+       if (cmd_ret &&
+           !(cmd_ret == -EIO &&
+           (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+-          cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)))
++          cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200))) {
++              dev_dbg(dev, "MSDC start command failure with %d, cmd=%d, arg=0x%x\n",
++                      cmd_ret, cmd->cmdidx, cmd->cmdarg);
+               return cmd_ret;
+-
+-      if (data) {
+-              data_ret = msdc_start_data(host, data);
+-              if (cmd_ret)
+-                      return cmd_ret;
+-              else
+-                      return data_ret;
+       }
+-      return 0;
++      if (!data)
++              return cmd_ret;
++
++      data_ret = msdc_start_data(host, data);
++      if (cmd_ret)
++              return cmd_ret;
++
++      if (data_ret)
++              dev_dbg(dev, "MSDC start data failure with %d, cmd=%d, arg=0x%x\n",
++                      data_ret, cmd->cmdidx, cmd->cmdarg);
++
++      return data_ret;
+ }
+ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
diff --git a/package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch b/package/boot/uboot-mediatek/patches/100-24-cmd-ubi-make-volume-find-create-remove-APIs-public.patch
new file mode 100644 (file)
index 0000000..ba32fa8
--- /dev/null
@@ -0,0 +1,58 @@
+From dd66fc817f7ab7a4fcab9836a9251a8f64f329df Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 16:58:36 +0800
+Subject: [PATCH 59/71] cmd: ubi: make volume find/create/remove APIs public
+
+Export ubi_create_vol/ubi_find_volume/ubi_remove_vol to public so that they
+can be used by other programs.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/ubi.c           | 8 ++++----
+ include/ubi_uboot.h | 4 ++++
+ 2 files changed, 8 insertions(+), 4 deletions(-)
+
+--- a/cmd/ubi.c
++++ b/cmd/ubi.c
+@@ -148,8 +148,8 @@ bad:
+       return err;
+ }
+-static int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
+-                        bool skipcheck)
++int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
++                 bool skipcheck)
+ {
+       struct ubi_mkvol_req req;
+       int err;
+@@ -182,7 +182,7 @@ static int ubi_create_vol(char *volume,
+       return ubi_create_volume(ubi, &req);
+ }
+-static struct ubi_volume *ubi_find_volume(char *volume)
++struct ubi_volume *ubi_find_volume(char *volume)
+ {
+       struct ubi_volume *vol = NULL;
+       int i;
+@@ -197,7 +197,7 @@ static struct ubi_volume *ubi_find_volum
+       return NULL;
+ }
+-static int ubi_remove_vol(char *volume)
++int ubi_remove_vol(char *volume)
+ {
+       int err, reserved_pebs, i;
+       struct ubi_volume *vol;
+--- a/include/ubi_uboot.h
++++ b/include/ubi_uboot.h
+@@ -73,6 +73,10 @@ extern void ubi_exit(void);
+ extern int ubi_part(char *part_name, const char *vid_header_offset);
+ extern int ubi_volume_write(char *volume, void *buf, size_t size);
+ extern int ubi_volume_read(char *volume, char *buf, size_t size);
++extern int ubi_create_vol(char *volume, int64_t size, int dynamic, int vol_id,
++                        bool skipcheck);
++extern struct ubi_volume *ubi_find_volume(char *volume);
++extern int ubi_remove_vol(char *volume);
+ extern struct ubi_device *ubi_devices[];
+ int cmd_ubifs_mount(char *vol_name);
diff --git a/package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch b/package/boot/uboot-mediatek/patches/100-25-cmd-ubi-allow-creating-volume-with-all-free-spaces.patch
new file mode 100644 (file)
index 0000000..1d62c05
--- /dev/null
@@ -0,0 +1,27 @@
+From f6a4130959af1e6d13d616203e42ed3c894666ad Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 17:00:00 +0800
+Subject: [PATCH 60/71] cmd: ubi: allow creating volume with all free spaces
+
+Allow creating volume with all free spaces by giving a negative size value.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ cmd/ubi.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/cmd/ubi.c
++++ b/cmd/ubi.c
+@@ -161,7 +161,11 @@ int ubi_create_vol(char *volume, int64_t
+       req.vol_id = vol_id;
+       req.alignment = 1;
+-      req.bytes = size;
++
++      if (size < 0)
++              req.bytes = ubi->avail_pebs * ubi->leb_size;
++      else
++              req.bytes = size;
+       strcpy(req.name, volume);
+       req.name_len = strlen(volume);
diff --git a/package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch b/package/boot/uboot-mediatek/patches/100-26-env-ubi-add-support-to-create-environment-volume-if-.patch
new file mode 100644 (file)
index 0000000..08b14a6
--- /dev/null
@@ -0,0 +1,72 @@
+From fc0c70a7c6a088072d0c77e5a59d5e9b7754c6db Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 17:01:20 +0800
+Subject: [PATCH 61/71] env: ubi: add support to create environment volume if
+ it does not exist
+
+Add an option to allow environment volume being auto created if not exist.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ env/Kconfig |  6 ++++++
+ env/ubi.c   | 20 ++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+--- a/env/Kconfig
++++ b/env/Kconfig
+@@ -647,6 +647,12 @@ config ENV_UBI_VOLUME_REDUND
+       help
+         Name of the redundant volume that you want to store the environment in.
++config ENV_UBI_VOLUME_CREATE
++      bool "Create UBI volume if not exist"
++      depends on ENV_IS_IN_UBI
++      help
++        Create the UBI volume if it does not exist.
++
+ config ENV_UBI_VID_OFFSET
+       int "ubi environment VID offset"
+       depends on ENV_IS_IN_UBI
+--- a/env/ubi.c
++++ b/env/ubi.c
+@@ -100,6 +100,18 @@ static int env_ubi_save(void)
+ #endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
+ #endif /* CONFIG_CMD_SAVEENV */
++int __weak env_ubi_volume_create(const char *volume)
++{
++      struct ubi_volume *vol;
++
++      vol = ubi_find_volume((char *)volume);
++      if (vol)
++              return 0;
++
++      return ubi_create_vol((char *)volume, CONFIG_ENV_SIZE, true,
++                            UBI_VOL_NUM_AUTO, false);
++}
++
+ #ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
+ static int env_ubi_load(void)
+ {
+@@ -129,6 +141,11 @@ static int env_ubi_load(void)
+               return -EIO;
+       }
++      if (IS_ENABLED(CONFIG_ENV_UBI_VOLUME_CREATE)) {
++              env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME);
++              env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME_REDUND);
++      }
++
+       read1_fail = ubi_volume_read(CONFIG_ENV_UBI_VOLUME, (void *)tmp_env1,
+                                    CONFIG_ENV_SIZE);
+       if (read1_fail)
+@@ -166,6 +183,9 @@ static int env_ubi_load(void)
+               return -EIO;
+       }
++      if (IS_ENABLED(CONFIG_ENV_UBI_VOLUME_CREATE))
++              env_ubi_volume_create(CONFIG_ENV_UBI_VOLUME);
++
+       if (ubi_volume_read(CONFIG_ENV_UBI_VOLUME, buf, CONFIG_ENV_SIZE)) {
+               printf("\n** Unable to read env from %s:%s **\n",
+                      CONFIG_ENV_UBI_PART, CONFIG_ENV_UBI_VOLUME);
diff --git a/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch b/package/boot/uboot-mediatek/patches/100-27-mtd-ubi-add-support-for-UBI-end-of-filesystem-marker.patch
new file mode 100644 (file)
index 0000000..cd1794f
--- /dev/null
@@ -0,0 +1,66 @@
+From 189a2fe96931ef3ea0e187c8e9bfa589c2a0ae10 Mon Sep 17 00:00:00 2001
+From: Weijie Gao <weijie.gao@mediatek.com>
+Date: Mon, 25 Jul 2022 17:24:56 +0800
+Subject: [PATCH 62/71] mtd: ubi: add support for UBI end-of-filesystem marker
+ used by OpenWrt
+
+Add support for UBI end-of-filesystem marker used by OpenWrt to allow
+attaching a new UBI mtd partition just upgraded.
+
+Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
+---
+ drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++---
+ drivers/mtd/ubi/ubi.h    |  1 +
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/ubi/attach.c
++++ b/drivers/mtd/ubi/attach.c
+@@ -802,6 +802,13 @@ out_unlock:
+       return err;
+ }
++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech)
++{
++      return ech->padding1[0] == 'E' &&
++             ech->padding1[1] == 'O' &&
++             ech->padding1[2] == 'F';
++}
++
+ /**
+  * scan_peb - scan and process UBI headers of a PEB.
+  * @ubi: UBI device description object
+@@ -832,9 +839,21 @@ static int scan_peb(struct ubi_device *u
+               return 0;
+       }
+-      err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
+-      if (err < 0)
+-              return err;
++      if (!ai->eof_found) {
++              err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
++              if (err < 0)
++                      return err;
++
++              if (ec_hdr_has_eof(ech)) {
++                      pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n",
++                              pnum);
++                      ai->eof_found = true;
++              }
++      }
++
++      if (ai->eof_found)
++              err = UBI_IO_FF_BITFLIPS;
++
+       switch (err) {
+       case 0:
+               break;
+--- a/drivers/mtd/ubi/ubi.h
++++ b/drivers/mtd/ubi/ubi.h
+@@ -745,6 +745,7 @@ struct ubi_attach_info {
+       int mean_ec;
+       uint64_t ec_sum;
+       int ec_count;
++      bool eof_found;
+       struct kmem_cache *aeb_slab_cache;
+ };
diff --git a/package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch b/package/boot/uboot-mediatek/patches/100-28-include-configs-mt7986-h-from-SDK.patch
new file mode 100644 (file)
index 0000000..110b1bb
--- /dev/null
@@ -0,0 +1,26 @@
+--- a/include/configs/mt7986.h
++++ b/include/configs/mt7986.h
+@@ -11,6 +11,11 @@
+ #include <linux/sizes.h>
++#define CONFIG_SYS_MAXARGS            32
++#define CONFIG_SYS_BOOTM_LEN          SZ_128M
++#define CONFIG_SYS_CBSIZE             SZ_1K
++#define CONFIG_SYS_PBSIZE             (CONFIG_SYS_CBSIZE +    \
++                                      sizeof(CONFIG_SYS_PROMPT) + 16)
+ #define CONFIG_SYS_NONCACHED_MEMORY   SZ_1M
+ #define CONFIG_SYS_MMC_ENV_DEV                0
+@@ -19,6 +24,11 @@
+ /* SPL -> Uboot */
+ #define CONFIG_SYS_UBOOT_START                CONFIG_SYS_TEXT_BASE
++#define CONFIG_SYS_INIT_SP_ADDR               (CONFIG_SYS_TEXT_BASE + SZ_2M - \
++                                       GENERATED_GBL_DATA_SIZE)
++
++/* Flash */
++#define CONFIG_SYS_NAND_MAX_CHIPS     1
+ /* DRAM */
+ #define CONFIG_SYS_SDRAM_BASE         0x40000000
diff --git a/package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch b/package/boot/uboot-mediatek/patches/100-29-board-mediatek-wire-up-NMBM-support.patch
new file mode 100644 (file)
index 0000000..9808e2c
--- /dev/null
@@ -0,0 +1,240 @@
+From 6792b57b3ba61ca6d69ea4a13a58bed65fc5da87 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <daniel@makrotopia.org>
+Date: Sun, 7 Aug 2022 04:04:46 +0200
+Subject: [PATCH] board: mediatek: wire-up NMBM support
+
+---
+ board/mediatek/mt7622/mt7622_rfb.c | 38 +++++++++++++++++++++
+ board/mediatek/mt7629/mt7629_rfb.c | 38 +++++++++++++++++++++
+ board/mediatek/mt7981/mt7981_rfb.c | 52 ++++++++++++++++++++++++++++
+ board/mediatek/mt7986/mt7986_rfb.c | 54 ++++++++++++++++++++++++++++++
+ 4 files changed, 182 insertions(+)
+
+--- a/board/mediatek/mt7622/mt7622_rfb.c
++++ b/board/mediatek/mt7622/mt7622_rfb.c
+@@ -10,6 +10,11 @@
+ #include <init.h>
+ #include <asm/global_data.h>
++#include <mtd.h>
++#include <linux/mtd/mtd.h>
++#include <nmbm/nmbm.h>
++#include <nmbm/nmbm-mtd.h>
++
+ DECLARE_GLOBAL_DATA_PTR;
+ int board_init(void)
+@@ -24,3 +29,36 @@ int board_late_init(void)
+       env_relocate();
+       return 0;
+ }
++
++int board_nmbm_init(void)
++{
++#ifdef CONFIG_ENABLE_NAND_NMBM
++      struct mtd_info *lower, *upper;
++      int ret;
++
++      printf("\n");
++      printf("Initializing NMBM ...\n");
++
++      mtd_probe_devices();
++
++      lower = get_mtd_device_nm("spi-nand0");
++      if (IS_ERR(lower) || !lower) {
++              printf("Lower MTD device 'spi-nand0' not found\n");
++              return 0;
++      }
++
++      ret = nmbm_attach_mtd(lower,
++                            NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK,
++                            CONFIG_NMBM_MAX_RATIO,
++                            CONFIG_NMBM_MAX_BLOCKS, &upper);
++
++      printf("\n");
++
++      if (ret)
++              return 0;
++
++      add_mtd_device(upper);
++#endif
++
++      return 0;
++}
+--- a/board/mediatek/mt7629/mt7629_rfb.c
++++ b/board/mediatek/mt7629/mt7629_rfb.c
+@@ -6,6 +6,11 @@
+ #include <common.h>
+ #include <asm/global_data.h>
++#include <mtd.h>
++#include <linux/mtd/mtd.h>
++#include <nmbm/nmbm.h>
++#include <nmbm/nmbm-mtd.h>
++
+ DECLARE_GLOBAL_DATA_PTR;
+ int board_init(void)
+@@ -20,3 +25,36 @@ uint32_t spl_nand_get_uboot_raw_page(voi
+ {
+       return CONFIG_SPL_PAD_TO;
+ }
++
++int board_nmbm_init(void)
++{
++#ifdef CONFIG_ENABLE_NAND_NMBM
++      struct mtd_info *lower, *upper;
++      int ret;
++
++      printf("\n");
++      printf("Initializing NMBM ...\n");
++
++      mtd_probe_devices();
++
++      lower = get_mtd_device_nm("spi-nand0");
++      if (IS_ERR(lower) || !lower) {
++              printf("Lower MTD device 'spi-nand0' not found\n");
++              return 0;
++      }
++
++      ret = nmbm_attach_mtd(lower,
++                            NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK,
++                            CONFIG_NMBM_MAX_RATIO,
++                            CONFIG_NMBM_MAX_BLOCKS, &upper);
++
++      printf("\n");
++
++      if (ret)
++              return 0;
++
++      add_mtd_device(upper);
++#endif
++
++      return 0;
++}
+--- a/board/mediatek/mt7981/mt7981_rfb.c
++++ b/board/mediatek/mt7981/mt7981_rfb.c
+@@ -4,7 +4,59 @@
+  * Author: Sam Shih <sam.shih@mediatek.com>
+  */
++#include <common.h>
++#include <config.h>
++#include <env.h>
++#include <init.h>
++#include <asm/global_data.h>
++
++#include <mtd.h>
++#include <linux/mtd/mtd.h>
++#include <nmbm/nmbm.h>
++#include <nmbm/nmbm-mtd.h>
++
++DECLARE_GLOBAL_DATA_PTR;
++
+ int board_init(void)
+ {
++      gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
++      return 0;
++}
++
++int board_late_init(void)
++{
++      gd->env_valid = 1; //to load environment variable from persistent store
++      env_relocate();
++      return 0;
++}
++
++int board_nmbm_init(void)
++{
++#ifdef CONFIG_ENABLE_NAND_NMBM
++      struct mtd_info *lower, *upper;
++      int ret;
++
++      printf("\n");
++      printf("Initializing NMBM ...\n");
++
++      mtd_probe_devices();
++
++      lower = get_mtd_device_nm("spi-nand0");
++      if (IS_ERR(lower) || !lower) {
++              printf("Lower MTD device 'spi-nand0' not found\n");
++              return 0;
++      }
++
++      ret = nmbm_attach_mtd(lower, NMBM_F_CREATE, CONFIG_NMBM_MAX_RATIO,
++              CONFIG_NMBM_MAX_BLOCKS, &upper);
++
++      printf("\n");
++
++      if (ret)
++              return 0;
++
++      add_mtd_device(upper);
++#endif
++
+       return 0;
+ }
+--- a/board/mediatek/mt7986/mt7986_rfb.c
++++ b/board/mediatek/mt7986/mt7986_rfb.c
+@@ -4,7 +4,61 @@
+  * Author: Sam Shih <sam.shih@mediatek.com>
+  */
++#include <common.h>
++#include <config.h>
++#include <env.h>
++#include <init.h>
++#include <asm/global_data.h>
++
++#include <mtd.h>
++#include <linux/mtd/mtd.h>
++#include <nmbm/nmbm.h>
++#include <nmbm/nmbm-mtd.h>
++
++DECLARE_GLOBAL_DATA_PTR;
++
+ int board_init(void)
+ {
++      gd->bd->bi_boot_params = CONFIG_SYS_SDRAM_BASE + 0x100;
++      return 0;
++}
++
++int board_late_init(void)
++{
++      gd->env_valid = 1; //to load environment variable from persistent store
++      env_relocate();
++      return 0;
++}
++
++int board_nmbm_init(void)
++{
++#ifdef CONFIG_ENABLE_NAND_NMBM
++      struct mtd_info *lower, *upper;
++      int ret;
++
++      printf("\n");
++      printf("Initializing NMBM ...\n");
++
++      mtd_probe_devices();
++
++      lower = get_mtd_device_nm("spi-nand0");
++      if (IS_ERR(lower) || !lower) {
++              printf("Lower MTD device 'spi-nand0' not found\n");
++              return 0;
++      }
++
++      ret = nmbm_attach_mtd(lower,
++                            NMBM_F_CREATE | NMBM_F_EMPTY_PAGE_ECC_OK,
++                            CONFIG_NMBM_MAX_RATIO,
++                            CONFIG_NMBM_MAX_BLOCKS, &upper);
++
++      printf("\n");
++
++      if (ret)
++              return 0;
++
++      add_mtd_device(upper);
++#endif
++
+       return 0;
+ }
index 6d4af1f174fed0f7de5216a1a932b419179e8499..5fb896d92d22bb567783166937627154f73bf267 100644 (file)
@@ -1,6 +1,6 @@
 --- a/tools/Makefile
 +++ b/tools/Makefile
-@@ -120,7 +120,6 @@ dumpimage-mkimage-objs := aisimage.o \
+@@ -121,7 +121,6 @@ dumpimage-mkimage-objs := aisimage.o \
                        imximage.o \
                        imx8image.o \
                        imx8mimage.o \
diff --git a/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch b/package/boot/uboot-mediatek/patches/210-cmd-bootmenu-add-ability-to-select-item-by-shortkey.patch
deleted file mode 100644 (file)
index dc9c9ae..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-From afea25576fc92d562b248b783cf03564eb4521da 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 12/12] cmd: bootmenu: add ability to select item by shortkey
-
-Add ability to use shortkey to select item for bootmenu command
-
-Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
----
- cmd/bootmenu.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-----
- 1 file changed, 70 insertions(+), 7 deletions(-)
-
---- a/cmd/bootmenu.c
-+++ b/cmd/bootmenu.c
-@@ -14,6 +14,7 @@
- #include <menu.h>
- #include <watchdog.h>
- #include <malloc.h>
-+#include <linux/ctype.h>
- #include <linux/delay.h>
- #include <linux/string.h>
-@@ -87,16 +88,17 @@ static char *bootmenu_choice_entry(void
-       struct bootmenu_data *menu = data;
-       struct bootmenu_entry *iter;
-       enum bootmenu_key key = KEY_NONE;
-+      int choice = -1;
-       int esc = 0;
-       int i;
-       while (1) {
-               if (menu->delay >= 0) {
-                       /* Autoboot was not stopped */
--                      bootmenu_autoboot_loop(menu, &key, &esc);
-+                      bootmenu_autoboot_loop(menu, &key, &esc, &choice);
-               } else {
-                       /* Some key was pressed, so autoboot was stopped */
--                      bootmenu_loop(menu, &key, &esc);
-+                      bootmenu_loop(menu, &key, &esc, &choice);
-               }
-               switch (key) {
-@@ -110,6 +112,12 @@ static char *bootmenu_choice_entry(void
-                               ++menu->active;
-                       /* no menu key selected, regenerate menu */
-                       return NULL;
-+              case KEY_CHOICE:
-+                      menu->active = choice;
-+                      if (!menu->last_choiced) {
-+                              menu->last_choiced = true;
-+                              return NULL;
-+                      }
-               case KEY_SELECT:
-                       iter = menu->first;
-                       for (i = 0; i < menu->active; ++i)
-@@ -181,12 +189,19 @@ static int prepare_bootmenu_entry(struct
-               if (!entry)
-                       return -ENOMEM;
--              entry->title = strndup(option, sep - option);
-+              entry->title = malloc((sep - option) + 4);
-               if (!entry->title) {
-                       free(entry);
-                       return -ENOMEM;
-               }
-+              if (i < ARRAY_SIZE(choice_chars)) {
-+                      sprintf(entry->title, "%c. %.*s", choice_chars[i],
-+                              (int)(sep - option), option);
-+              } else {
-+                      sprintf(entry->title, "   %.*s", (int)(sep - option), option);
-+              }
-+
-               entry->command = strdup(sep + 1);
-               if (!entry->command) {
-                       free(entry->title);
-@@ -331,6 +346,7 @@ static struct bootmenu_data *bootmenu_cr
-       menu->delay = delay;
-       menu->active = 0;
-       menu->first = NULL;
-+      menu->last_choiced = false;
-       default_str = env_get("bootmenu_default");
-       if (default_str)
-@@ -356,9 +372,9 @@ static struct bootmenu_data *bootmenu_cr
-               /* Add Quit entry if entering U-Boot console is disabled */
-               if (!IS_ENABLED(CONFIG_BOOTMENU_DISABLE_UBOOT_CONSOLE))
--                      entry->title = strdup("U-Boot console");
-+                      entry->title = strdup("0. U-Boot console");
-               else
--                      entry->title = strdup("Quit");
-+                      entry->title = strdup("0. Quit");
-               if (!entry->title) {
-                       free(entry);
---- a/common/menu.c
-+++ b/common/menu.c
-@@ -9,6 +9,7 @@
- #include <cli.h>
- #include <malloc.h>
- #include <errno.h>
-+#include <linux/ctype.h>
- #include <linux/delay.h>
- #include <linux/list.h>
- #include <watchdog.h>
-@@ -47,6 +48,17 @@ struct menu {
-       int item_cnt;
- };
-+static int find_choice(char choice)
-+{
-+      int i;
-+
-+      for (i = 0; i < ARRAY_SIZE(choice_chars); i++)
-+              if (tolower(choice) == choice_chars[i])
-+                      return i;
-+
-+      return -1;
-+}
-+
- /*
-  * An iterator function for menu items. callback will be called for each item
-  * in m, with m, a pointer to the item, and extra being passed to callback. If
-@@ -426,7 +438,7 @@ int menu_destroy(struct menu *m)
- }
- void bootmenu_autoboot_loop(struct bootmenu_data *menu,
--                          enum bootmenu_key *key, int *esc)
-+                          enum bootmenu_key *key, int *esc, int *choice)
- {
-       int i, c;
-@@ -456,6 +468,19 @@ void bootmenu_autoboot_loop(struct bootm
-                               break;
-                       default:
-                               *key = KEY_NONE;
-+                              if (*esc)
-+                                      break;
-+
-+                              *choice = find_choice(c);
-+                              if ((*choice >= 0 &&
-+                                   *choice < menu->count - 1)) {
-+                                      *key = KEY_CHOICE;
-+                              } else if (c == '0') {
-+                                      *choice = menu->count - 1;
-+                                      *key = KEY_CHOICE;
-+                              } else {
-+                                      *key = KEY_NONE;
-+                              }
-                               break;
-                       }
-@@ -475,10 +500,16 @@ void bootmenu_autoboot_loop(struct bootm
- }
- void bootmenu_loop(struct bootmenu_data *menu,
--                 enum bootmenu_key *key, int *esc)
-+                 enum bootmenu_key *key, int *esc, int *choice)
- {
-       int c;
-+      if (menu->last_choiced) {
-+              menu->last_choiced = false;
-+              *key = KEY_SELECT;
-+              return;
-+      }
-+
-       if (*esc == 1) {
-               if (tstc()) {
-                       c = getchar();
-@@ -504,6 +535,14 @@ void bootmenu_loop(struct bootmenu_data
-               if (c == '\e') {
-                       *esc = 1;
-                       *key = KEY_NONE;
-+              } else {
-+                      *choice = find_choice(c);
-+                      if ((*choice >= 0 && *choice < menu->count - 1)) {
-+                              *key = KEY_CHOICE;
-+                      } else if (c == '0') {
-+                              *choice = menu->count - 1;
-+                              *key = KEY_CHOICE;
-+                      }
-               }
-               break;
-       case 1:
---- a/include/menu.h
-+++ b/include/menu.h
-@@ -40,6 +40,7 @@ struct bootmenu_data {
-       int active;                     /* active menu entry */
-       int count;                      /* total count of menu entries */
-       struct bootmenu_entry *first;   /* first menu entry */
-+      bool last_choiced;
- };
- enum bootmenu_key {
-@@ -48,11 +49,19 @@ enum bootmenu_key {
-       KEY_DOWN,
-       KEY_SELECT,
-       KEY_QUIT,
-+      KEY_CHOICE,
- };
- void bootmenu_autoboot_loop(struct bootmenu_data *menu,
--                          enum bootmenu_key *key, int *esc);
-+                          enum bootmenu_key *key, int *esc, int *choice);
- void bootmenu_loop(struct bootmenu_data *menu,
--                 enum bootmenu_key *key, int *esc);
-+                 enum bootmenu_key *key, int *esc, int *choice);
-+
-+static const char choice_chars[] = {
-+      '1', '2', '3', '4', '5', '6', '7', '8', '9',
-+      'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
-+      'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
-+      'u', 'v', 'w', 'x', 'y', 'z'
-+};
- #endif /* __MENU_H__ */
index f1e60c640756997dd57b0cfe4fc86ffa202c3c93..72926ea2e7fefd206a6b9ecbf7de9ec11b86ad21 100644 (file)
@@ -1,6 +1,6 @@
 --- a/cmd/bootmenu.c
 +++ b/cmd/bootmenu.c
-@@ -431,7 +431,11 @@ static void menu_display_statusline(stru
+@@ -439,7 +439,11 @@ static void menu_display_statusline(stru
        printf(ANSI_CURSOR_POSITION, 1, 1);
        puts(ANSI_CLEAR_LINE);
        printf(ANSI_CURSOR_POSITION, 2, 3);
@@ -13,7 +13,7 @@
        puts(ANSI_CLEAR_LINE_TO_END);
        printf(ANSI_CURSOR_POSITION, 3, 1);
        puts(ANSI_CLEAR_LINE);
-@@ -516,6 +520,7 @@ static enum bootmenu_ret bootmenu_show(i
+@@ -524,6 +528,7 @@ static enum bootmenu_ret bootmenu_show(i
                return BOOTMENU_RET_FAIL;
        }
  
@@ -23,7 +23,7 @@
                        goto cleanup;
 --- a/include/menu.h
 +++ b/include/menu.h
-@@ -40,6 +40,7 @@ struct bootmenu_data {
+@@ -43,6 +43,7 @@ struct bootmenu_data {
        int active;                     /* active menu entry */
        int count;                      /* total count of menu entries */
        struct bootmenu_entry *first;   /* first menu entry */
index 544767e2fa2ac5d9ffc27893017f1e884949df77..4ffbc17208f12c338a2b463694601ef60db2a042 100644 (file)
@@ -15,7 +15,7 @@
        help
 --- a/cmd/nvedit.c
 +++ b/cmd/nvedit.c
-@@ -408,6 +408,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in
+@@ -409,6 +409,60 @@ int do_env_ask(struct cmd_tbl *cmdtp, in
  }
  #endif
  
@@ -76,7 +76,7 @@
  #if defined(CONFIG_CMD_ENV_CALLBACK)
  static int print_static_binding(const char *var_name, const char *callback_name,
                                void *priv)
-@@ -1231,6 +1285,9 @@ static struct cmd_tbl cmd_env_sub[] = {
+@@ -1232,6 +1286,9 @@ static struct cmd_tbl cmd_env_sub[] = {
        U_BOOT_CMD_MKENT(load, 1, 0, do_env_load, "", ""),
  #endif
        U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_env_print, "", ""),
@@ -86,7 +86,7 @@
  #if defined(CONFIG_CMD_RUN)
        U_BOOT_CMD_MKENT(run, CONFIG_SYS_MAXARGS, 1, do_run, "", ""),
  #endif
-@@ -1322,6 +1379,9 @@ static char env_help_text[] =
+@@ -1323,6 +1380,9 @@ static char env_help_text[] =
  #if defined(CONFIG_CMD_NVEDIT_EFI)
        "env print -e [-guid guid] [-n] [name ...] - print UEFI environment\n"
  #endif
@@ -96,7 +96,7 @@
  #if defined(CONFIG_CMD_RUN)
        "env run var [...] - run commands in an environment variable\n"
  #endif
-@@ -1431,6 +1491,17 @@ U_BOOT_CMD(
+@@ -1432,6 +1492,17 @@ U_BOOT_CMD(
  );
  #endif
  
index 8630fc16cef9bb9c2dfa4bc7067b95f46d18b09d..9fae6d056f607780ff2cb5bfa0ea26282e3d8167 100644 (file)
@@ -15,9 +15,9 @@
 +#define CONFIG_RESET_BUTTON_LABEL "reset"
 +#endif
  
- DECLARE_GLOBAL_DATA_PTR;
-@@ -20,7 +27,19 @@ int board_init(void)
+ #include <mtd.h>
+ #include <linux/mtd/mtd.h>
+@@ -25,7 +32,19 @@ int board_init(void)
  
  int board_late_init(void)
  {
@@ -40,9 +40,9 @@
  }
 --- a/arch/arm/mach-mediatek/Kconfig
 +++ b/arch/arm/mach-mediatek/Kconfig
-@@ -136,4 +136,8 @@ config MTK_BROM_HEADER_INFO
-       default "media=snand;nandinfo=2k+64" if TARGET_MT7981 || TARGET_MT7986
-       default "lk=1" if TARGET_MT7623
+@@ -138,4 +138,8 @@ config MTK_BROM_HEADER_INFO
+ source "board/mediatek/mt7629/Kconfig"
  
 +config RESET_BUTTON_LABEL
 +      string "Button to trigger factory reset"
diff --git a/package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch b/package/boot/uboot-mediatek/patches/350-add-support-for-Winbond-W25Q512JV.patch
deleted file mode 100644 (file)
index da746bb..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/drivers/mtd/spi/spi-nor-ids.c
-+++ b/drivers/mtd/spi/spi-nor-ids.c
-@@ -376,6 +376,8 @@ const struct flash_info spi_nor_ids[] =
-                       SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
-       },
-       { INFO("w25q256", 0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-+      { INFO("w25q512jv", 0xef4020, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ |
-+                      SPI_NOR_HAS_TB | SPI_NOR_HAS_LOCK) },
-       { INFO("w25m512jw", 0xef6119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-       { INFO("w25m512jv", 0xef7119, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
- #endif
index a30baf007478e3c5870ce8542aa052641f37f7bb..8279d250f3e0599c9b0e68f01aab97359b0ca601 100644 (file)
  
  DECLARE_GLOBAL_DATA_PTR;
  
-@@ -392,6 +393,20 @@ static int initr_onenand(void)
+@@ -406,6 +407,20 @@ static int initr_onenand(void)
  }
  #endif
  
  #ifdef CONFIG_MMC
  static int initr_mmc(void)
  {
-@@ -703,6 +718,9 @@ static init_fnc_t init_sequence_r[] = {
- #ifdef CONFIG_CMD_ONENAND
-       initr_onenand,
+@@ -720,6 +735,9 @@ static init_fnc_t init_sequence_r[] = {
+ #ifdef CONFIG_NMBM_MTD
+       initr_nmbm,
  #endif
 +#ifdef CONFIG_SPI_FLASH
 +      initr_spiflash,
diff --git a/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch b/package/boot/uboot-mediatek/patches/600-ubi-detect-eof-marker.patch
deleted file mode 100644 (file)
index 5d312ec..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
---- a/drivers/mtd/ubi/attach.c
-+++ b/drivers/mtd/ubi/attach.c
-@@ -802,6 +802,13 @@ out_unlock:
-       return err;
- }
-+static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech)
-+{
-+      return ech->padding1[0] == 'E' &&
-+             ech->padding1[1] == 'O' &&
-+             ech->padding1[2] == 'F';
-+}
-+
- /**
-  * scan_peb - scan and process UBI headers of a PEB.
-  * @ubi: UBI device description object
-@@ -832,9 +839,21 @@ static int scan_peb(struct ubi_device *u
-               return 0;
-       }
--      err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
--      if (err < 0)
--              return err;
-+      if (!ai->eof_found) {
-+              err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0);
-+              if (err < 0)
-+                      return err;
-+
-+              if (ec_hdr_has_eof(ech)) {
-+                      pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n",
-+                              pnum);
-+                      ai->eof_found = true;
-+              }
-+      }
-+
-+      if (ai->eof_found)
-+              err = UBI_IO_FF_BITFLIPS;
-+
-       switch (err) {
-       case 0:
-               break;
---- a/drivers/mtd/ubi/ubi.h
-+++ b/drivers/mtd/ubi/ubi.h
-@@ -745,6 +745,7 @@ struct ubi_attach_info {
-       int mean_ec;
-       uint64_t ec_sum;
-       int ec_count;
-+      bool eof_found;
-       struct kmem_cache *aeb_slab_cache;
- };