mediatek: add uboot
authorJohn Crispin <john@phrozen.org>
Mon, 4 May 2020 14:31:19 +0000 (16:31 +0200)
committerJohn Crispin <john@phrozen.org>
Mon, 4 May 2020 14:31:19 +0000 (16:31 +0200)
Signed-off-by: John Crispin <john@phrozen.org>
package/boot/uboot-mediatek/Makefile [new file with mode: 0644]
package/boot/uboot-mediatek/patches/001-eth-mtk-add-mt7531-switch.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch [new file with mode: 0644]
package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch [new file with mode: 0644]

diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile
new file mode 100644 (file)
index 0000000..a825652
--- /dev/null
@@ -0,0 +1,41 @@
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_VERSION:=2020.04
+PKG_RELEASE:=1
+PKG_HASH:=fe732aaf037d9cc3c0909bad8362af366ae964bbdac6913a34081ff4ad565372
+
+include $(INCLUDE_DIR)/u-boot.mk
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/host-build.mk
+
+define U-Boot/Default
+  BUILD_TARGET:=mediatek
+  UBOOT_IMAGE:=u-boot-mtk.bin
+  DEFAULT:=y
+endef
+
+define U-Boot/mt7629
+  NAME:=MT7629
+  BUILD_SUBTARGET:=mt7629
+  UBOOT_CONFIG:=mt7629_rfb
+endef
+
+define U-Boot/mt7622
+  NAME:=MT7622
+  BUILD_SUBTARGET:=mt7622
+  UBOOT_CONFIG:=mt7622_rfb
+endef
+
+UBOOT_TARGETS := mt7629 mt7622
+
+UBOOT_MAKE_FLAGS += $(UBOOT_IMAGE)
+
+Build/Exports:=$(Host/Exports)
+
+define Build/InstallDev
+       $(INSTALL_DIR) $(STAGING_DIR_IMAGE)
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/$(UBOOT_IMAGE) $(STAGING_DIR_IMAGE)/$(BUILD_VARIANT)-$(UBOOT_IMAGE)
+endef
+
+$(eval $(call BuildPackage/U-Boot))
diff --git a/package/boot/uboot-mediatek/patches/001-eth-mtk-add-mt7531-switch.patch b/package/boot/uboot-mediatek/patches/001-eth-mtk-add-mt7531-switch.patch
new file mode 100644 (file)
index 0000000..ced1ca9
--- /dev/null
@@ -0,0 +1,1074 @@
+From patchwork Tue Feb 18 08:49:37 2020
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Landen Chao <landen.chao@mediatek.com>
+X-Patchwork-Id: 1239956
+X-Patchwork-Delegate: trini@ti.com
+Return-Path: <u-boot-bounces@lists.denx.de>
+X-Original-To: incoming@patchwork.ozlabs.org
+Delivered-To: patchwork-incoming@bilbo.ozlabs.org
+Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized)
+ smtp.mailfrom=lists.denx.de
+ (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01;
+ helo=phobos.denx.de;
+ envelope-from=u-boot-bounces@lists.denx.de;
+ receiver=<UNKNOWN>)
+Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none)
+ header.from=mediatek.com
+Authentication-Results: ozlabs.org; dkim=pass (1024-bit key;
+ unprotected) header.d=mediatek.com header.i=@mediatek.com
+ header.a=rsa-sha256 header.s=dk header.b=NQKfnebM; 
+ dkim-atps=neutral
+Received: from phobos.denx.de (phobos.denx.de
+ [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])
+ (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
+ key-exchange X25519 server-signature RSA-PSS (4096 bits))
+ (No client certificate requested)
+ by ozlabs.org (Postfix) with ESMTPS id 48MJX54ymTz9sRf
+ for <incoming@patchwork.ozlabs.org>;
+ Tue, 18 Feb 2020 22:28:53 +1100 (AEDT)
+Received: from h2850616.stratoserver.net (localhost [IPv6:::1])
+ by phobos.denx.de (Postfix) with ESMTP id A68D8810D8;
+ Tue, 18 Feb 2020 12:28:47 +0100 (CET)
+Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none)
+ header.from=mediatek.com
+Authentication-Results: phobos.denx.de;
+ spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de
+Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key;
+ unprotected) header.d=mediatek.com header.i=@mediatek.com
+ header.b="NQKfnebM"; dkim-atps=neutral
+Received: by phobos.denx.de (Postfix, from userid 109)
+ id 767F881217; Tue, 18 Feb 2020 09:50:00 +0100 (CET)
+X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de
+X-Spam-Level: 
+X-Spam-Status: No, score=0.5 required=5.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,MIME_BASE64_TEXT,RDNS_NONE,SPF_HELO_NONE,
+ UNPARSEABLE_RELAY,URIBL_BLOCKED autolearn=no autolearn_force=no
+ version=3.4.2
+Received: from mailgw01.mediatek.com (unknown [210.61.82.183])
+ by phobos.denx.de (Postfix) with ESMTP id 40ADE811FB
+ for <u-boot@lists.denx.de>; Tue, 18 Feb 2020 09:49:46 +0100 (CET)
+Authentication-Results: phobos.denx.de;
+ dmarc=pass (p=none dis=none) header.from=mediatek.com
+Authentication-Results: phobos.denx.de;
+ spf=pass smtp.mailfrom=landen.chao@mediatek.com
+X-UUID: ddaa8deb3c7d4a24ba5cbcb30775fbfd-20200218
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
+ d=mediatek.com; s=dk; 
+ h=Content-Transfer-Encoding:Content-Type:MIME-Version:References:In-Reply-To:Message-ID:Date:Subject:CC:To:From;
+ bh=qvWp4DdC1p5SZD1XJPV3kwdXTU9lBi4hbWNZQoO2oLI=; 
+ b=NQKfnebM0mIfPJuncOxa6aNY+g5wFOnlozbm/o8ViKaVNbkd8YSB0ISWOerj8lALM7E7sSP797r514G3R4EAQPPvuAm5B2wi2VuYKssTxHkvYJvCrALXp4wmQtGF+gJytfdWuHx6Np0wfbjTsLqw+JUtcDi/0jAa8TQ5y9UHr9Q=;
+X-UUID: ddaa8deb3c7d4a24ba5cbcb30775fbfd-20200218
+Received: from mtkcas07.mediatek.inc [(172.21.101.84)] by
+ mailgw01.mediatek.com (envelope-from <landen.chao@mediatek.com>)
+ (Cellopoint E-mail Firewall v4.1.10 Build 0809 with TLS)
+ with ESMTP id 827129897; Tue, 18 Feb 2020 16:49:43 +0800
+Received: from MTKCAS06.mediatek.inc (172.21.101.30) by
+ mtkmbs05n1.mediatek.inc (172.21.101.15) with Microsoft SMTP Server
+ (TLS) id 15.0.1395.4; Tue, 18 Feb 2020 16:48:45 +0800
+Received: from mtksdccf07.mediatek.inc (172.21.84.99) by MTKCAS06.mediatek.inc
+ (172.21.101.73) with Microsoft SMTP Server id 15.0.1395.4 via
+ Frontend Transport; Tue, 18 Feb 2020 16:47:41 +0800
+From: Landen Chao <landen.chao@mediatek.com>
+To: Ryder Lee <ryder.lee@mediatek.com>, Weijie Gao <weijie.gao@mediatek.com>, 
+ GSS_MTK_Uboot_upstream <GSS_MTK_Uboot_upstream@mediatek.com>,
+ Joe Hershberger <joe.hershberger@ni.com>, <u-boot@lists.denx.de>
+CC: <frank-w@public-files.de>, <steven.liu@mediatek.com>,
+ <landen.chao@mediatek.com>, <mark-mc.lee@mediatek.com>
+Subject: [U-Boot 1/1] eth: mtk-eth: add mt7531 switch support in mediatek eth
+ driver
+Date: Tue, 18 Feb 2020 16:49:37 +0800
+Message-ID: <f10c8e2e72fda3ac9d8d240b128f28c52cf06f3f.1582015221.git.landen.chao@mediatek.com>
+X-Mailer: git-send-email 2.18.0
+In-Reply-To: <cover.1582015221.git.landen.chao@mediatek.com>
+References: <cover.1582015221.git.landen.chao@mediatek.com>
+MIME-Version: 1.0
+X-MTK: N
+X-Mailman-Approved-At: Tue, 18 Feb 2020 12:28:45 +0100
+X-BeenThere: u-boot@lists.denx.de
+X-Mailman-Version: 2.1.30rc1
+Precedence: list
+List-Id: U-Boot discussion <u-boot.lists.denx.de>
+List-Unsubscribe: <https://lists.denx.de/options/u-boot>,
+ <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>
+List-Archive: <https://lists.denx.de/pipermail/u-boot/>
+List-Post: <mailto:u-boot@lists.denx.de>
+List-Help: <mailto:u-boot-request@lists.denx.de?subject=help>
+List-Subscribe: <https://lists.denx.de/listinfo/u-boot>,
+ <mailto:u-boot-request@lists.denx.de?subject=subscribe>
+Errors-To: u-boot-bounces@lists.denx.de
+Sender: "U-Boot" <u-boot-bounces@lists.denx.de>
+X-Virus-Scanned: clamav-milter 0.102.1 at phobos.denx.de
+X-Virus-Status: Clean
+
+mt7531 is a 7-ports switch with 5 embedded giga phys, and uses the same
+MAC design of mt7530. The cpu port6 supports SGMII only. The cpu port5
+supports RGMII or SGMII in different model.
+
+mt7531 is connected to mt7622 via both RGMII and SGMII interfaces.
+In this patch, mt7531 cpu port5 or port6 is configured to maximum
+capability to align CPU MAC setting.
+
+The dts has been committed in the commit 6efa450565cdc ("arm: dts:
+mediatek: add ethernet and sgmii dts node for mt7622")
+
+Signed-off-by: Landen Chao <landen.chao@mediatek.com>
+Tested-by: Frank Wunderlich <frank-w@public-files.de>
+---
+ drivers/net/mtk_eth.c | 580 +++++++++++++++++++++++++++++++++---------
+ drivers/net/mtk_eth.h | 124 ++++++++-
+ 2 files changed, 577 insertions(+), 127 deletions(-)
+
+diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c
+index edfa5d1ce8..f7d34a2e6b 100644
+--- a/drivers/net/mtk_eth.c
++++ b/drivers/net/mtk_eth.c
+@@ -30,10 +30,12 @@
+ #define RX_TOTAL_BUF_SIZE     (NUM_RX_DESC * PKTSIZE_ALIGN)
+ #define TOTAL_PKT_BUF_SIZE    (TX_TOTAL_BUF_SIZE + RX_TOTAL_BUF_SIZE)
+-#define MT7530_NUM_PHYS               5
+-#define MT7530_DFL_SMI_ADDR   31
++#define MT753X_NUM_PHYS               5
++#define MT753X_NUM_PORTS      7
++#define MT753X_DFL_SMI_ADDR   31
++#define MT753X_SMI_ADDR_MASK  0x1f
+-#define MT7530_PHY_ADDR(base, addr) \
++#define MT753X_PHY_ADDR(base, addr) \
+       (((base) + (addr)) & 0x1f)
+ #define GDMA_FWD_TO_CPU \
+@@ -131,7 +133,8 @@ struct pdma_txdesc {
+ enum mtk_switch {
+       SW_NONE,
+-      SW_MT7530
++      SW_MT7530,
++      SW_MT7531
+ };
+ enum mtk_soc {
+@@ -173,8 +176,8 @@ struct mtk_eth_priv {
+       enum mtk_switch sw;
+       int (*switch_init)(struct mtk_eth_priv *priv);
+-      u32 mt7530_smi_addr;
+-      u32 mt7530_phy_base;
++      u32 mt753x_smi_addr;
++      u32 mt753x_phy_base;
+       struct gpio_desc rst_gpio;
+       int mcm;
+@@ -349,6 +352,174 @@ static int mtk_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad,
+       return priv->mii_write(priv, addr, MII_MMD_ADDR_DATA_REG, val);
+ }
++/*
++ * MT7530 Internal Register Address Bits
++ * -------------------------------------------------------------------
++ * | 15  14  13  12  11  10   9   8   7   6 | 5   4   3   2 | 1   0  |
++ * |----------------------------------------|---------------|--------|
++ * |              Page Address              |  Reg Address  | Unused |
++ * -------------------------------------------------------------------
++ */
++
++static int mt753x_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data)
++{
++      int ret, low_word, high_word;
++
++      /* Write page address */
++      ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6);
++      if (ret)
++              return ret;
++
++      /* Read low word */
++      low_word = mtk_mii_read(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf);
++      if (low_word < 0)
++              return low_word;
++
++      /* Read high word */
++      high_word = mtk_mii_read(priv, priv->mt753x_smi_addr, 0x10);
++      if (high_word < 0)
++              return high_word;
++
++      if (data)
++              *data = ((u32)high_word << 16) | (low_word & 0xffff);
++
++      return 0;
++}
++
++static int mt753x_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data)
++{
++      int ret;
++
++      /* Write page address */
++      ret = mtk_mii_write(priv, priv->mt753x_smi_addr, 0x1f, reg >> 6);
++      if (ret)
++              return ret;
++
++      /* Write low word */
++      ret = mtk_mii_write(priv, priv->mt753x_smi_addr, (reg >> 2) & 0xf,
++                          data & 0xffff);
++      if (ret)
++              return ret;
++
++      /* Write high word */
++      return mtk_mii_write(priv, priv->mt753x_smi_addr, 0x10, data >> 16);
++}
++
++static void mt753x_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
++                         u32 set)
++{
++      u32 val;
++
++      mt753x_reg_read(priv, reg, &val);
++      val &= ~clr;
++      val |= set;
++      mt753x_reg_write(priv, reg, val);
++}
++
++/* Indirect MDIO clause 22/45 access */
++static int mt7531_mii_rw(struct mtk_eth_priv *priv, int phy, int reg, u16 data,
++                       u32 cmd, u32 st)
++{
++      ulong timeout;
++      u32 val, timeout_ms;
++      int ret = 0;
++
++      val = (st << MDIO_ST_S) |
++            ((cmd << MDIO_CMD_S) & MDIO_CMD_M) |
++            ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) |
++            ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M);
++
++      if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR)
++              val |= data & MDIO_RW_DATA_M;
++
++      mt753x_reg_write(priv, MT7531_PHY_IAC, val | PHY_ACS_ST);
++
++      timeout_ms = 100;
++      timeout = get_timer(0);
++      while (1) {
++              mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
++
++              if ((val & PHY_ACS_ST) == 0)
++                      break;
++
++              if (get_timer(timeout) > timeout_ms)
++                      return -ETIMEDOUT;
++      }
++
++      if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) {
++              mt753x_reg_read(priv, MT7531_PHY_IAC, &val);
++              ret = val & MDIO_RW_DATA_M;
++      }
++
++      return ret;
++}
++
++static int mt7531_mii_ind_read(struct mtk_eth_priv *priv, u8 phy, u8 reg)
++{
++      u8 phy_addr;
++
++      if (phy >= MT753X_NUM_PHYS)
++              return -EINVAL;
++
++      phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy);
++
++      return mt7531_mii_rw(priv, phy_addr, reg, 0, MDIO_CMD_READ,
++                           MDIO_ST_C22);
++}
++
++static int mt7531_mii_ind_write(struct mtk_eth_priv *priv, u8 phy, u8 reg,
++                              u16 val)
++{
++      u8 phy_addr;
++
++      if (phy >= MT753X_NUM_PHYS)
++              return -EINVAL;
++
++      phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, phy);
++
++      return mt7531_mii_rw(priv, phy_addr, reg, val, MDIO_CMD_WRITE,
++                           MDIO_ST_C22);
++}
++
++int mt7531_mmd_ind_read(struct mtk_eth_priv *priv, u8 addr, u8 devad, u16 reg)
++{
++      u8 phy_addr;
++      int ret;
++
++      if (addr >= MT753X_NUM_PHYS)
++              return -EINVAL;
++
++      phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr);
++
++      ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
++                          MDIO_ST_C45);
++      if (ret)
++              return ret;
++
++      return mt7531_mii_rw(priv, phy_addr, devad, 0, MDIO_CMD_READ_C45,
++                           MDIO_ST_C45);
++}
++
++static int mt7531_mmd_ind_write(struct mtk_eth_priv *priv, u8 addr, u8 devad,
++                              u16 reg, u16 val)
++{
++      u8 phy_addr;
++      int ret;
++
++      if (addr >= MT753X_NUM_PHYS)
++              return 0;
++
++      phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, addr);
++
++      ret = mt7531_mii_rw(priv, phy_addr, devad, reg, MDIO_CMD_ADDR,
++                          MDIO_ST_C45);
++      if (ret)
++              return ret;
++
++      return mt7531_mii_rw(priv, phy_addr, devad, val, MDIO_CMD_WRITE,
++                           MDIO_ST_C45);
++}
++
+ static int mtk_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
+ {
+       struct mtk_eth_priv *priv = bus->priv;
+@@ -387,6 +558,12 @@ static int mtk_mdio_register(struct udevice *dev)
+               priv->mmd_read = mtk_mmd_ind_read;
+               priv->mmd_write = mtk_mmd_ind_write;
+               break;
++      case SW_MT7531:
++              priv->mii_read = mt7531_mii_ind_read;
++              priv->mii_write = mt7531_mii_ind_write;
++              priv->mmd_read = mt7531_mmd_ind_read;
++              priv->mmd_write = mt7531_mmd_ind_write;
++              break;
+       default:
+               priv->mii_read = mtk_mii_read;
+               priv->mii_write = mtk_mii_write;
+@@ -410,75 +587,18 @@ static int mtk_mdio_register(struct udevice *dev)
+       return 0;
+ }
+-/*
+- * MT7530 Internal Register Address Bits
+- * -------------------------------------------------------------------
+- * | 15  14  13  12  11  10   9   8   7   6 | 5   4   3   2 | 1   0  |
+- * |----------------------------------------|---------------|--------|
+- * |              Page Address              |  Reg Address  | Unused |
+- * -------------------------------------------------------------------
+- */
+-
+-static int mt7530_reg_read(struct mtk_eth_priv *priv, u32 reg, u32 *data)
++static int mt753x_core_reg_read(struct mtk_eth_priv *priv, u32 reg)
+ {
+-      int ret, low_word, high_word;
++      u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0);
+-      /* Write page address */
+-      ret = mtk_mii_write(priv, priv->mt7530_smi_addr, 0x1f, reg >> 6);
+-      if (ret)
+-              return ret;
+-
+-      /* Read low word */
+-      low_word = mtk_mii_read(priv, priv->mt7530_smi_addr, (reg >> 2) & 0xf);
+-      if (low_word < 0)
+-              return low_word;
+-
+-      /* Read high word */
+-      high_word = mtk_mii_read(priv, priv->mt7530_smi_addr, 0x10);
+-      if (high_word < 0)
+-              return high_word;
+-
+-      if (data)
+-              *data = ((u32)high_word << 16) | (low_word & 0xffff);
+-
+-      return 0;
++      return priv->mmd_read(priv, phy_addr, 0x1f, reg);
+ }
+-static int mt7530_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 data)
++static void mt753x_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val)
+ {
+-      int ret;
+-
+-      /* Write page address */
+-      ret = mtk_mii_write(priv, priv->mt7530_smi_addr, 0x1f, reg >> 6);
+-      if (ret)
+-              return ret;
++      u8 phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, 0);
+-      /* Write low word */
+-      ret = mtk_mii_write(priv, priv->mt7530_smi_addr, (reg >> 2) & 0xf,
+-                          data & 0xffff);
+-      if (ret)
+-              return ret;
+-
+-      /* Write high word */
+-      return mtk_mii_write(priv, priv->mt7530_smi_addr, 0x10, data >> 16);
+-}
+-
+-static void mt7530_reg_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
+-                         u32 set)
+-{
+-      u32 val;
+-
+-      mt7530_reg_read(priv, reg, &val);
+-      val &= ~clr;
+-      val |= set;
+-      mt7530_reg_write(priv, reg, val);
+-}
+-
+-static void mt7530_core_reg_write(struct mtk_eth_priv *priv, u32 reg, u32 val)
+-{
+-      u8 phy_addr = MT7530_PHY_ADDR(priv->mt7530_phy_base, 0);
+-
+-      mtk_mmd_ind_write(priv, phy_addr, 0x1f, reg, val);
++      priv->mmd_write(priv, phy_addr, 0x1f, reg, val);
+ }
+ static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode)
+@@ -496,46 +616,46 @@ static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode)
+       }
+       /* Disable MT7530 core clock */
+-      mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0);
++      mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, 0);
+       /* Disable MT7530 PLL */
+-      mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1,
++      mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1,
+                             (2 << RG_GSWPLL_POSDIV_200M_S) |
+                             (32 << RG_GSWPLL_FBKDIV_200M_S));
+       /* For MT7530 core clock = 500Mhz */
+-      mt7530_core_reg_write(priv, CORE_GSWPLL_GRP2,
++      mt753x_core_reg_write(priv, CORE_GSWPLL_GRP2,
+                             (1 << RG_GSWPLL_POSDIV_500M_S) |
+                             (25 << RG_GSWPLL_FBKDIV_500M_S));
+       /* Enable MT7530 PLL */
+-      mt7530_core_reg_write(priv, CORE_GSWPLL_GRP1,
++      mt753x_core_reg_write(priv, CORE_GSWPLL_GRP1,
+                             (2 << RG_GSWPLL_POSDIV_200M_S) |
+                             (32 << RG_GSWPLL_FBKDIV_200M_S) |
+                             RG_GSWPLL_EN_PRE);
+       udelay(20);
+-      mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
++      mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG, REG_GSWCK_EN);
+       /* Setup the MT7530 TRGMII Tx Clock */
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1);
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP6, 0);
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta);
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta);
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP5, ncpo1);
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP6, 0);
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP10, ssc_delta);
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP11, ssc_delta);
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP4, RG_SYSPLL_DDSFBK_EN |
+                             RG_SYSPLL_BIAS_EN | RG_SYSPLL_BIAS_LPF_EN);
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP2,
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP2,
+                             RG_SYSPLL_EN_NORMAL | RG_SYSPLL_VODEN |
+                             (1 << RG_SYSPLL_POSDIV_S));
+-      mt7530_core_reg_write(priv, CORE_PLL_GROUP7,
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP7,
+                             RG_LCDDS_PCW_NCPO_CHG | (3 << RG_LCCDS_C_S) |
+                             RG_LCDDS_PWDB | RG_LCDDS_ISO_EN);
+       /* Enable MT7530 core clock */
+-      mt7530_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG,
++      mt753x_core_reg_write(priv, CORE_TRGMII_GSW_CLK_CG,
+                             REG_GSWCK_EN | REG_TRGMIICK_EN);
+       return 0;
+@@ -551,46 +671,33 @@ static int mt7530_setup(struct mtk_eth_priv *priv)
+       mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
+                      ETHSYS_TRGMII_CLK_SEL362_5, 0);
+-      /* Global reset switch */
+-      if (priv->mcm) {
+-              reset_assert(&priv->rst_mcm);
+-              udelay(1000);
+-              reset_deassert(&priv->rst_mcm);
+-              mdelay(1000);
+-      } else if (dm_gpio_is_valid(&priv->rst_gpio)) {
+-              dm_gpio_set_value(&priv->rst_gpio, 0);
+-              udelay(1000);
+-              dm_gpio_set_value(&priv->rst_gpio, 1);
+-              mdelay(1000);
+-      }
+-
+       /* Modify HWTRAP first to allow direct access to internal PHYs */
+-      mt7530_reg_read(priv, HWTRAP_REG, &val);
++      mt753x_reg_read(priv, HWTRAP_REG, &val);
+       val |= CHG_TRAP;
+       val &= ~C_MDIO_BPS;
+-      mt7530_reg_write(priv, MHWTRAP_REG, val);
++      mt753x_reg_write(priv, MHWTRAP_REG, val);
+       /* Calculate the phy base address */
+       val = ((val & SMI_ADDR_M) >> SMI_ADDR_S) << 3;
+-      priv->mt7530_phy_base = (val | 0x7) + 1;
++      priv->mt753x_phy_base = (val | 0x7) + 1;
+       /* Turn off PHYs */
+-      for (i = 0; i < MT7530_NUM_PHYS; i++) {
+-              phy_addr = MT7530_PHY_ADDR(priv->mt7530_phy_base, i);
++      for (i = 0; i < MT753X_NUM_PHYS; i++) {
++              phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
+               phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
+               phy_val |= BMCR_PDOWN;
+               priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
+       }
+       /* Force MAC link down before reset */
+-      mt7530_reg_write(priv, PCMR_REG(5), FORCE_MODE);
+-      mt7530_reg_write(priv, PCMR_REG(6), FORCE_MODE);
++      mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
++      mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE);
+       /* MT7530 reset */
+-      mt7530_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
++      mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
+       udelay(100);
+-      val = (1 << IPG_CFG_S) |
++      val = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+             MAC_MODE | FORCE_MODE |
+             MAC_TX_EN | MAC_RX_EN |
+             BKOFF_EN | BACKPR_EN |
+@@ -598,53 +705,280 @@ static int mt7530_setup(struct mtk_eth_priv *priv)
+             FORCE_DPX | FORCE_LINK;
+       /* MT7530 Port6: Forced 1000M/FD, FC disabled */
+-      mt7530_reg_write(priv, PCMR_REG(6), val);
++      mt753x_reg_write(priv, PMCR_REG(6), val);
+       /* MT7530 Port5: Forced link down */
+-      mt7530_reg_write(priv, PCMR_REG(5), FORCE_MODE);
++      mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE);
+       /* MT7530 Port6: Set to RGMII */
+-      mt7530_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII);
++      mt753x_reg_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_M, P6_INTF_MODE_RGMII);
+       /* Hardware Trap: Enable Port6, Disable Port5 */
+-      mt7530_reg_read(priv, HWTRAP_REG, &val);
++      mt753x_reg_read(priv, HWTRAP_REG, &val);
+       val |= CHG_TRAP | LOOPDET_DIS | P5_INTF_DIS |
+              (P5_INTF_SEL_GMAC5 << P5_INTF_SEL_S) |
+              (P5_INTF_MODE_RGMII << P5_INTF_MODE_S);
+       val &= ~(C_MDIO_BPS | P6_INTF_DIS);
+-      mt7530_reg_write(priv, MHWTRAP_REG, val);
++      mt753x_reg_write(priv, MHWTRAP_REG, val);
+       /* Setup switch core pll */
+       mt7530_pad_clk_setup(priv, priv->phy_interface);
+       /* Lower Tx Driving for TRGMII path */
+       for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
+-              mt7530_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
++              mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
+                                (8 << TD_DM_DRVP_S) | (8 << TD_DM_DRVN_S));
+       for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
+-              mt7530_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
++              mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
++
++      /* Turn on PHYs */
++      for (i = 0; i < MT753X_NUM_PHYS; i++) {
++              phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
++              phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
++              phy_val &= ~BMCR_PDOWN;
++              priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
++      }
++
++      return 0;
++}
++
++static void mt7531_core_pll_setup(struct mtk_eth_priv *priv, int mcm)
++{
++      /* Step 1 : Disable MT7531 COREPLL */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, 0);
++
++      /* Step 2: switch to XTAL output */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_CLKSW, SW_CLKSW);
++
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, 0);
++
++      /* Step 3: disable PLLGP and enable program PLLGP */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_EN, SW_PLLGP, SW_PLLGP);
++
++      /* Step 4: program COREPLL output frequency to 500MHz */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_POSDIV_M,
++                     2 << RG_COREPLL_POSDIV_S);
++      udelay(25);
++
++      /* Currently, support XTAL 25Mhz only */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_M,
++                     0x140000 << RG_COREPLL_SDM_PCW_S);
++
++      /* Set feedback divide ratio update signal to high */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG,
++                     RG_COREPLL_SDM_PCW_CHG);
++
++      /* Wait for at least 16 XTAL clocks */
++      udelay(10);
++
++      /* Step 5: set feedback divide ratio update signal to low */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_SDM_PCW_CHG, 0);
++
++      /* add enable 325M clock for SGMII */
++      mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000);
++
++      /* add enable 250SSC clock for RGMII */
++      mt753x_reg_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000);
++
++      /*Step 6: Enable MT7531 PLL */
++      mt753x_reg_rmw(priv, MT7531_PLLGP_CR0, RG_COREPLL_EN, RG_COREPLL_EN);
++
++      mt753x_reg_rmw(priv, MT7531_PLLGP_EN, EN_COREPLL, EN_COREPLL);
++
++      udelay(25);
++}
++
++static int mt7531_port_sgmii_init(struct mtk_eth_priv *priv,
++                                u32 port)
++{
++      if (port != 5 && port != 6) {
++              printf("mt7531: port %d is not a SGMII port\n", port);
++              return -EINVAL;
++      }
++
++      /* Set SGMII GEN2 speed(2.5G) */
++      mt753x_reg_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port),
++                     SGMSYS_SPEED_2500, SGMSYS_SPEED_2500);
++
++      /* Disable SGMII AN */
++      mt753x_reg_rmw(priv, MT7531_PCS_CONTROL_1(port),
++                     SGMII_AN_ENABLE, 0);
++
++      /* SGMII force mode setting */
++      mt753x_reg_write(priv, MT7531_SGMII_MODE(port), SGMII_FORCE_MODE);
++
++      /* Release PHYA power down state */
++      mt753x_reg_rmw(priv, MT7531_QPHY_PWR_STATE_CTRL(port),
++                     SGMII_PHYA_PWD, 0);
++
++      return 0;
++}
++
++static int mt7531_port_rgmii_init(struct mtk_eth_priv *priv, u32 port)
++{
++      u32 val;
++
++      if (port != 5) {
++              printf("error: RGMII mode is not available for port %d\n",
++                     port);
++              return -EINVAL;
++      }
++
++      mt753x_reg_read(priv, MT7531_CLKGEN_CTRL, &val);
++      val |= GP_CLK_EN;
++      val &= ~GP_MODE_M;
++      val |= GP_MODE_RGMII << GP_MODE_S;
++      val |= TXCLK_NO_REVERSE;
++      val |= RXCLK_NO_DELAY;
++      val &= ~CLK_SKEW_IN_M;
++      val |= CLK_SKEW_IN_NO_CHANGE << CLK_SKEW_IN_S;
++      val &= ~CLK_SKEW_OUT_M;
++      val |= CLK_SKEW_OUT_NO_CHANGE << CLK_SKEW_OUT_S;
++      mt753x_reg_write(priv, MT7531_CLKGEN_CTRL, val);
++
++      return 0;
++}
++
++static void mt7531_phy_setting(struct mtk_eth_priv *priv)
++{
++      int i;
++      u32 val;
++
++      for (i = 0; i < MT753X_NUM_PHYS; i++) {
++              /* Enable HW auto downshift */
++              priv->mii_write(priv, i, 0x1f, 0x1);
++              val = priv->mii_read(priv, i, PHY_EXT_REG_14);
++              val |= PHY_EN_DOWN_SHFIT;
++              priv->mii_write(priv, i, PHY_EXT_REG_14, val);
++
++              /* PHY link down power saving enable */
++              val = priv->mii_read(priv, i, PHY_EXT_REG_17);
++              val |= PHY_LINKDOWN_POWER_SAVING_EN;
++              priv->mii_write(priv, i, PHY_EXT_REG_17, val);
++
++              val = priv->mmd_read(priv, i, 0x1e, PHY_DEV1E_REG_0C6);
++              val &= ~PHY_POWER_SAVING_M;
++              val |= PHY_POWER_SAVING_TX << PHY_POWER_SAVING_S;
++              priv->mmd_write(priv, i, 0x1e, PHY_DEV1E_REG_0C6, val);
++      }
++}
++
++static int mt7531_setup(struct mtk_eth_priv *priv)
++{
++      u16 phy_addr, phy_val;
++      u32 val;
++      u32 pmcr;
++      u32 port5_sgmii;
++      int i;
++
++      priv->mt753x_phy_base = (priv->mt753x_smi_addr + 1) &
++                              MT753X_SMI_ADDR_MASK;
++
++      /* Turn off PHYs */
++      for (i = 0; i < MT753X_NUM_PHYS; i++) {
++              phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
++              phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
++              phy_val |= BMCR_PDOWN;
++              priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
++      }
++
++      /* Force MAC link down before reset */
++      mt753x_reg_write(priv, PMCR_REG(5), FORCE_MODE_LNK);
++      mt753x_reg_write(priv, PMCR_REG(6), FORCE_MODE_LNK);
++
++      /* Switch soft reset */
++      mt753x_reg_write(priv, SYS_CTRL_REG, SW_SYS_RST | SW_REG_RST);
++      udelay(100);
++
++      /* Enable MDC input Schmitt Trigger */
++      mt753x_reg_rmw(priv, MT7531_SMT0_IOLB, SMT_IOLB_5_SMI_MDC_EN,
++                     SMT_IOLB_5_SMI_MDC_EN);
++
++      mt7531_core_pll_setup(priv, priv->mcm);
++
++      mt753x_reg_read(priv, MT7531_TOP_SIG_SR, &val);
++      port5_sgmii = !!(val & PAD_DUAL_SGMII_EN);
++
++      /* port5 support either RGMII or SGMII, port6 only support SGMII. */
++      switch (priv->phy_interface) {
++      case PHY_INTERFACE_MODE_RGMII:
++              if (!port5_sgmii)
++                      mt7531_port_rgmii_init(priv, 5);
++              break;
++      case PHY_INTERFACE_MODE_SGMII:
++              mt7531_port_sgmii_init(priv, 6);
++              if (port5_sgmii)
++                      mt7531_port_sgmii_init(priv, 5);
++              break;
++      default:
++              break;
++      }
++
++      pmcr = MT7531_FORCE_MODE |
++             (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
++             MAC_MODE | MAC_TX_EN | MAC_RX_EN |
++             BKOFF_EN | BACKPR_EN |
++             FORCE_RX_FC | FORCE_TX_FC |
++             (SPEED_1000M << FORCE_SPD_S) | FORCE_DPX |
++             FORCE_LINK;
++
++      mt753x_reg_write(priv, PMCR_REG(5), pmcr);
++      mt753x_reg_write(priv, PMCR_REG(6), pmcr);
+       /* Turn on PHYs */
+-      for (i = 0; i < MT7530_NUM_PHYS; i++) {
+-              phy_addr = MT7530_PHY_ADDR(priv->mt7530_phy_base, i);
++      for (i = 0; i < MT753X_NUM_PHYS; i++) {
++              phy_addr = MT753X_PHY_ADDR(priv->mt753x_phy_base, i);
+               phy_val = priv->mii_read(priv, phy_addr, MII_BMCR);
+               phy_val &= ~BMCR_PDOWN;
+               priv->mii_write(priv, phy_addr, MII_BMCR, phy_val);
+       }
++      mt7531_phy_setting(priv);
++
++      /* Enable Internal PHYs */
++      val = mt753x_core_reg_read(priv, CORE_PLL_GROUP4);
++      val |= MT7531_BYPASS_MODE;
++      val &= ~MT7531_POWER_ON_OFF;
++      mt753x_core_reg_write(priv, CORE_PLL_GROUP4, val);
++
++      return 0;
++}
++
++int mt753x_switch_init(struct mtk_eth_priv *priv)
++{
++      int ret;
++      int i;
++
++      /* Global reset switch */
++      if (priv->mcm) {
++              reset_assert(&priv->rst_mcm);
++              udelay(1000);
++              reset_deassert(&priv->rst_mcm);
++              mdelay(1000);
++      } else if (dm_gpio_is_valid(&priv->rst_gpio)) {
++              dm_gpio_set_value(&priv->rst_gpio, 0);
++              udelay(1000);
++              dm_gpio_set_value(&priv->rst_gpio, 1);
++              mdelay(1000);
++      }
++
++      ret = priv->switch_init(priv);
++      if (ret)
++              return ret;
++
+       /* Set port isolation */
+-      for (i = 0; i < 8; i++) {
++      for (i = 0; i < MT753X_NUM_PORTS; i++) {
+               /* Set port matrix mode */
+               if (i != 6)
+-                      mt7530_reg_write(priv, PCR_REG(i),
++                      mt753x_reg_write(priv, PCR_REG(i),
+                                        (0x40 << PORT_MATRIX_S));
+               else
+-                      mt7530_reg_write(priv, PCR_REG(i),
++                      mt753x_reg_write(priv, PCR_REG(i),
+                                        (0x3f << PORT_MATRIX_S));
+               /* Set port mode to user port */
+-              mt7530_reg_write(priv, PVC_REG(i),
++              mt753x_reg_write(priv, PVC_REG(i),
+                                (0x8100 << STAG_VPID_S) |
+                                (VLAN_ATTR_USER << VLAN_ATTR_S));
+       }
+@@ -658,7 +992,7 @@ static void mtk_phy_link_adjust(struct mtk_eth_priv *priv)
+       u8 flowctrl;
+       u32 mcr;
+-      mcr = (1 << IPG_CFG_S) |
++      mcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+             (MAC_RX_PKT_LEN_1536 << MAC_RX_PKT_LEN_S) |
+             MAC_MODE | FORCE_MODE |
+             MAC_TX_EN | MAC_RX_EN |
+@@ -803,7 +1137,7 @@ static void mtk_mac_init(struct mtk_eth_priv *priv)
+                      ge_mode << SYSCFG0_GE_MODE_S(priv->gmac_id));
+       if (priv->force_mode) {
+-              mcr = (1 << IPG_CFG_S) |
++              mcr = (IPG_96BIT_WITH_SHORT_IPG << IPG_CFG_S) |
+                     (MAC_RX_PKT_LEN_1536 << MAC_RX_PKT_LEN_S) |
+                     MAC_MODE | FORCE_MODE |
+                     MAC_TX_EN | MAC_RX_EN |
+@@ -1050,7 +1384,7 @@ static int mtk_eth_probe(struct udevice *dev)
+               return mtk_phy_probe(dev);
+       /* Initialize switch */
+-      return priv->switch_init(priv);
++      return mt753x_switch_init(priv);
+ }
+ static int mtk_eth_remove(struct udevice *dev)
+@@ -1159,7 +1493,11 @@ static int mtk_eth_ofdata_to_platdata(struct udevice *dev)
+               if (!strcmp(str, "mt7530")) {
+                       priv->sw = SW_MT7530;
+                       priv->switch_init = mt7530_setup;
+-                      priv->mt7530_smi_addr = MT7530_DFL_SMI_ADDR;
++                      priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR;
++              } else if (!strcmp(str, "mt7531")) {
++                      priv->sw = SW_MT7531;
++                      priv->switch_init = mt7531_setup;
++                      priv->mt753x_smi_addr = MT753X_DFL_SMI_ADDR;
+               } else {
+                       printf("error: unsupported switch\n");
+                       return -EINVAL;
+diff --git a/drivers/net/mtk_eth.h b/drivers/net/mtk_eth.h
+index 9bb037d440..f2940c9996 100644
+--- a/drivers/net/mtk_eth.h
++++ b/drivers/net/mtk_eth.h
+@@ -34,7 +34,9 @@
+ /* SGMII subsystem config registers */
+ #define SGMSYS_PCS_CONTROL_1          0x0
++#define SGMII_LINK_STATUS             BIT(18)
+ #define SGMII_AN_ENABLE                       BIT(12)
++#define SGMII_AN_RESTART              BIT(9)
+ #define SGMSYS_SGMII_MODE             0x20
+ #define SGMII_FORCE_MODE              0x31120019
+@@ -139,6 +141,11 @@
+ #define FORCE_DPX                     BIT(1)
+ #define FORCE_LINK                    BIT(0)
++/* Values of IPG_CFG */
++#define IPG_96BIT                     0
++#define IPG_96BIT_WITH_SHORT_IPG      1
++#define IPG_64BIT                     2
++
+ /* MAC_RX_PKT_LEN: Max RX packet length */
+ #define MAC_RX_PKT_LEN_1518           0
+ #define MAC_RX_PKT_LEN_1536           1
+@@ -178,17 +185,73 @@
+ #define VLAN_ATTR_TRANSLATION         2
+ #define VLAN_ATTR_TRANSPARENT         3
+-#define PCMR_REG(p)                   (0x3000 + (p) * 0x100)
+-/* XXX: all fields are defined under GMAC_PORT_MCR */
+-
++#define PMCR_REG(p)                   (0x3000 + (p) * 0x100)
++/* XXX: all fields of MT7530 are defined under GMAC_PORT_MCR
++ * MT7531 specific fields are defined below
++ */
++#define FORCE_MODE_EEE1G              BIT(25)
++#define FORCE_MODE_EEE100             BIT(26)
++#define FORCE_MODE_TX_FC              BIT(27)
++#define FORCE_MODE_RX_FC              BIT(28)
++#define FORCE_MODE_DPX                        BIT(29)
++#define FORCE_MODE_SPD                        BIT(30)
++#define FORCE_MODE_LNK                        BIT(31)
++#define MT7531_FORCE_MODE             FORCE_MODE_EEE1G | FORCE_MODE_EEE100 |\
++                                      FORCE_MODE_TX_FC | FORCE_MODE_RX_FC | \
++                                      FORCE_MODE_DPX   | FORCE_MODE_SPD | \
++                                      FORCE_MODE_LNK
++
++/* MT7531 SGMII Registers */
++#define MT7531_SGMII_REG_BASE         0x5000
++#define MT7531_SGMII_REG_PORT_BASE    0x1000
++#define MT7531_SGMII_REG(p, r)                (MT7531_SGMII_REG_BASE + \
++                                      (p) * MT7531_SGMII_REG_PORT_BASE + (r))
++#define MT7531_PCS_CONTROL_1(p)               MT7531_SGMII_REG(((p) - 5), 0x00)
++#define MT7531_SGMII_MODE(p)          MT7531_SGMII_REG(((p) - 5), 0x20)
++#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(((p) - 5), 0xe8)
++#define MT7531_PHYA_CTRL_SIGNAL3(p)   MT7531_SGMII_REG(((p) - 5), 0x128)
++/* XXX: all fields of MT7531 SGMII  are defined under SGMSYS */
++
++/* MT753x System Control Register */
+ #define SYS_CTRL_REG                  0x7000
+ #define SW_PHY_RST                    BIT(2)
+ #define SW_SYS_RST                    BIT(1)
+ #define SW_REG_RST                    BIT(0)
+-#define NUM_TRGMII_CTRL                       5
++/* MT7531  */
++#define MT7531_PHY_IAC                        0x701c
++/* XXX: all fields are defined under GMAC_PIAC_REG */
++
++#define MT7531_CLKGEN_CTRL            0x7500
++#define CLK_SKEW_OUT_S                        8
++#define CLK_SKEW_OUT_M                        0x300
++#define CLK_SKEW_IN_S                 6
++#define CLK_SKEW_IN_M                 0xc0
++#define RXCLK_NO_DELAY                        BIT(5)
++#define TXCLK_NO_REVERSE              BIT(4)
++#define GP_MODE_S                     1
++#define GP_MODE_M                     0x06
++#define GP_CLK_EN                     BIT(0)
++
++/* Values of GP_MODE */
++#define GP_MODE_RGMII                 0
++#define GP_MODE_MII                   1
++#define GP_MODE_REV_MII                       2
++
++/* Values of CLK_SKEW_IN */
++#define CLK_SKEW_IN_NO_CHANGE         0
++#define CLK_SKEW_IN_DELAY_100PPS      1
++#define CLK_SKEW_IN_DELAY_200PPS      2
++#define CLK_SKEW_IN_REVERSE           3
++
++/* Values of CLK_SKEW_OUT */
++#define CLK_SKEW_OUT_NO_CHANGE                0
++#define CLK_SKEW_OUT_DELAY_100PPS     1
++#define CLK_SKEW_OUT_DELAY_200PPS     2
++#define CLK_SKEW_OUT_REVERSE          3
+ #define HWTRAP_REG                    0x7800
++/* MT7530 Modified Hardware Trap Status Registers */
+ #define MHWTRAP_REG                   0x7804
+ #define CHG_TRAP                      BIT(16)
+ #define LOOPDET_DIS                   BIT(14)
+@@ -222,6 +285,8 @@
+ #define P6_INTF_MODE_RGMII            0
+ #define P6_INTF_MODE_TRGMII           1
++#define NUM_TRGMII_CTRL                       5
++
+ #define MT7530_TRGMII_RD(n)           (0x7a10 + (n) * 8)
+ #define RD_TAP_S                      0
+ #define RD_TAP_M                      0x7f
+@@ -229,8 +294,34 @@
+ #define MT7530_TRGMII_TD_ODT(n)               (0x7a54 + (n) * 8)
+ /* XXX: all fields are defined under GMAC_TRGMII_TD_ODT */
+-/* MT7530 GPHY MDIO Indirect Access Registers */
++/* TOP Signals Status Register */
++#define MT7531_TOP_SIG_SR             0x780c
++#define PAD_MCM_SMI_EN                        BIT(0)
++#define PAD_DUAL_SGMII_EN             BIT(1)
++
++/* MT7531 PLLGP Registers */
++#define MT7531_PLLGP_EN                       0x7820
++#define EN_COREPLL                    BIT(2)
++#define SW_CLKSW                      BIT(1)
++#define SW_PLLGP                      BIT(0)
++
++#define MT7531_PLLGP_CR0              0x78a8
++#define RG_COREPLL_EN                 BIT(22)
++#define RG_COREPLL_POSDIV_S           23
++#define RG_COREPLL_POSDIV_M           0x3800000
++#define RG_COREPLL_SDM_PCW_S          1
++#define RG_COREPLL_SDM_PCW_M          0x3ffffe
++#define RG_COREPLL_SDM_PCW_CHG                BIT(0)
++
++/* MT7531 RGMII and SGMII PLL clock */
++#define MT7531_ANA_PLLGP_CR2          0x78b0
++#define MT7531_ANA_PLLGP_CR5          0x78bc
++
++/* MT7531 GPIO GROUP IOLB SMT0 Control */
++#define MT7531_SMT0_IOLB              0x7f04
++#define SMT_IOLB_5_SMI_MDC_EN         BIT(5)
++/* MT7530 GPHY MDIO Indirect Access Registers */
+ #define MII_MMD_ACC_CTL_REG           0x0d
+ #define MMD_CMD_S                     14
+ #define MMD_CMD_M                     0xc000
+@@ -246,7 +337,6 @@
+ #define MII_MMD_ADDR_DATA_REG         0x0e
+ /* MT7530 GPHY MDIO MMD Registers */
+-
+ #define CORE_PLL_GROUP2                       0x401
+ #define RG_SYSPLL_EN_NORMAL           BIT(15)
+ #define RG_SYSPLL_VODEN                       BIT(14)
+@@ -254,6 +344,8 @@
+ #define RG_SYSPLL_POSDIV_M            0x60
+ #define CORE_PLL_GROUP4                       0x403
++#define MT7531_BYPASS_MODE            BIT(4)
++#define MT7531_POWER_ON_OFF           BIT(5)
+ #define RG_SYSPLL_DDSFBK_EN           BIT(12)
+ #define RG_SYSPLL_BIAS_EN             BIT(11)
+ #define RG_SYSPLL_BIAS_LPF_EN         BIT(10)
+@@ -298,4 +390,24 @@
+ #define REG_GSWCK_EN                  BIT(0)
+ #define REG_TRGMIICK_EN                       BIT(1)
++/* Extend PHY Control Register 3 */
++#define PHY_EXT_REG_14                        0x14
++
++/* Fields of PHY_EXT_REG_14 */
++#define PHY_EN_DOWN_SHFIT             BIT(4)
++
++/* Extend PHY Control Register 4 */
++#define PHY_EXT_REG_17                        0x17
++
++/* Fields of PHY_EXT_REG_17 */
++#define PHY_LINKDOWN_POWER_SAVING_EN  BIT(4)
++
++/* PHY RXADC Control Register 7 */
++#define PHY_DEV1E_REG_0C6             0x0c6
++
++/* Fields of PHY_DEV1E_REG_0C6 */
++#define PHY_POWER_SAVING_S            8
++#define PHY_POWER_SAVING_M            0x300
++#define PHY_POWER_SAVING_TX           0x0
++
+ #endif /* _MTK_ETH_H_ */
diff --git a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch
new file mode 100644 (file)
index 0000000..dc3ebaf
--- /dev/null
@@ -0,0 +1,8659 @@
+From de8b6cf615be20b25d0f3c817866de2c0d46a704 Mon Sep 17 00:00:00 2001
+From: Sam Shih <sam.shih@mediatek.com>
+Date: Mon, 20 Apr 2020 17:10:05 +0800
+Subject: [PATCH 1/3] nand: add spi nand driver
+
+Add spi nand driver support for mt7622 based on nfi controller
+
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ drivers/mtd/Kconfig                           |    7 +
+ drivers/mtd/Makefile                          |    4 +
+ drivers/mtd/nand/raw/nand.c                   |    2 +
+ drivers/mtd/nandx/NOTICE                      |   52 +
+ drivers/mtd/nandx/Nandx.config                |   17 +
+ drivers/mtd/nandx/Nandx.mk                    |   91 ++
+ drivers/mtd/nandx/README                      |   31 +
+ drivers/mtd/nandx/core/Nandx.mk               |   38 +
+ drivers/mtd/nandx/core/core_io.c              |  735 +++++++++
+ drivers/mtd/nandx/core/core_io.h              |   39 +
+ drivers/mtd/nandx/core/nand/device_spi.c      |  200 +++
+ drivers/mtd/nandx/core/nand/device_spi.h      |  132 ++
+ drivers/mtd/nandx/core/nand/nand_spi.c        |  526 +++++++
+ drivers/mtd/nandx/core/nand/nand_spi.h        |   35 +
+ drivers/mtd/nandx/core/nand_base.c            |  304 ++++
+ drivers/mtd/nandx/core/nand_base.h            |   71 +
+ drivers/mtd/nandx/core/nand_chip.c            |  272 ++++
+ drivers/mtd/nandx/core/nand_chip.h            |  103 ++
+ drivers/mtd/nandx/core/nand_device.c          |  285 ++++
+ drivers/mtd/nandx/core/nand_device.h          |  608 ++++++++
+ drivers/mtd/nandx/core/nfi.h                  |   51 +
+ drivers/mtd/nandx/core/nfi/nfi_base.c         | 1357 +++++++++++++++++
+ drivers/mtd/nandx/core/nfi/nfi_base.h         |   95 ++
+ drivers/mtd/nandx/core/nfi/nfi_regs.h         |  114 ++
+ drivers/mtd/nandx/core/nfi/nfi_spi.c          |  689 +++++++++
+ drivers/mtd/nandx/core/nfi/nfi_spi.h          |   44 +
+ drivers/mtd/nandx/core/nfi/nfi_spi_regs.h     |   64 +
+ drivers/mtd/nandx/core/nfi/nfiecc.c           |  510 +++++++
+ drivers/mtd/nandx/core/nfi/nfiecc.h           |   90 ++
+ drivers/mtd/nandx/core/nfi/nfiecc_regs.h      |   51 +
+ drivers/mtd/nandx/driver/Nandx.mk             |   18 +
+ drivers/mtd/nandx/driver/bbt/bbt.c            |  408 +++++
+ drivers/mtd/nandx/driver/uboot/driver.c       |  574 +++++++
+ drivers/mtd/nandx/include/Nandx.mk            |   16 +
+ drivers/mtd/nandx/include/internal/bbt.h      |   62 +
+ .../mtd/nandx/include/internal/nandx_core.h   |  250 +++
+ .../mtd/nandx/include/internal/nandx_errno.h  |   40 +
+ .../mtd/nandx/include/internal/nandx_util.h   |  221 +++
+ drivers/mtd/nandx/include/uboot/nandx_os.h    |   78 +
+ include/configs/mt7622.h                      |   25 +
+ 40 files changed, 8309 insertions(+)
+ create mode 100644 drivers/mtd/nandx/NOTICE
+ create mode 100644 drivers/mtd/nandx/Nandx.config
+ create mode 100644 drivers/mtd/nandx/Nandx.mk
+ create mode 100644 drivers/mtd/nandx/README
+ create mode 100644 drivers/mtd/nandx/core/Nandx.mk
+ create mode 100644 drivers/mtd/nandx/core/core_io.c
+ create mode 100644 drivers/mtd/nandx/core/core_io.h
+ create mode 100644 drivers/mtd/nandx/core/nand/device_spi.c
+ create mode 100644 drivers/mtd/nandx/core/nand/device_spi.h
+ create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.c
+ create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.h
+ create mode 100644 drivers/mtd/nandx/core/nand_base.c
+ create mode 100644 drivers/mtd/nandx/core/nand_base.h
+ create mode 100644 drivers/mtd/nandx/core/nand_chip.c
+ create mode 100644 drivers/mtd/nandx/core/nand_chip.h
+ create mode 100644 drivers/mtd/nandx/core/nand_device.c
+ create mode 100644 drivers/mtd/nandx/core/nand_device.h
+ create mode 100644 drivers/mtd/nandx/core/nfi.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.c
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_regs.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.c
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.c
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.h
+ create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc_regs.h
+ create mode 100644 drivers/mtd/nandx/driver/Nandx.mk
+ create mode 100644 drivers/mtd/nandx/driver/bbt/bbt.c
+ create mode 100644 drivers/mtd/nandx/driver/uboot/driver.c
+ create mode 100644 drivers/mtd/nandx/include/Nandx.mk
+ create mode 100644 drivers/mtd/nandx/include/internal/bbt.h
+ create mode 100644 drivers/mtd/nandx/include/internal/nandx_core.h
+ create mode 100644 drivers/mtd/nandx/include/internal/nandx_errno.h
+ create mode 100644 drivers/mtd/nandx/include/internal/nandx_util.h
+ create mode 100644 drivers/mtd/nandx/include/uboot/nandx_os.h
+
+diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
+index 5e7571cf3d..34a59b44b9 100644
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -101,6 +101,13 @@ config HBMC_AM654
+        This is the driver for HyperBus controller on TI's AM65x and
+        other SoCs
++config MTK_SPI_NAND
++      tristate "Mediatek SPI Nand"
++      depends on DM_MTD
++      help
++        This option will support SPI Nand device via Mediatek
++        NFI controller.
++
+ source "drivers/mtd/nand/Kconfig"
+ source "drivers/mtd/spi/Kconfig"
+diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
+index 318788c5e2..1df1031b23 100644
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -41,3 +41,7 @@ obj-$(CONFIG_$(SPL_TPL_)SPI_FLASH_SUPPORT) += spi/
+ obj-$(CONFIG_SPL_UBI) += ubispl/
+ endif
++
++ifeq ($(CONFIG_MTK_SPI_NAND), y)
++include $(srctree)/drivers/mtd/nandx/Nandx.mk
++endif
+diff --git a/drivers/mtd/nand/raw/nand.c b/drivers/mtd/nand/raw/nand.c
+index 026419e4e6..4be0c7d8f3 100644
+--- a/drivers/mtd/nand/raw/nand.c
++++ b/drivers/mtd/nand/raw/nand.c
+@@ -91,8 +91,10 @@ static void nand_init_chip(int i)
+       if (board_nand_init(nand))
+               return;
++#ifndef CONFIG_MTK_SPI_NAND
+       if (nand_scan(mtd, maxchips))
+               return;
++#endif
+       nand_register(i, mtd);
+ }
+diff --git a/drivers/mtd/nandx/NOTICE b/drivers/mtd/nandx/NOTICE
+new file mode 100644
+index 0000000000..1a06ca3867
+--- /dev/null
++++ b/drivers/mtd/nandx/NOTICE
+@@ -0,0 +1,52 @@
++
++/*
++ * Nandx - Mediatek Common Nand Driver
++ * Copyright (C) 2017 MediaTek Inc.
++ *
++ * Nandx is dual licensed: you can use it either under the terms of
++ * the GPL, or the BSD license, at your option.
++ *
++ *  a) This program is free software; you can redistribute it and/or modify
++ *     it under the terms of the GNU General Public License version 2 as
++ *     published by the Free Software Foundation.
++ *
++ *     This library is distributed in the hope that it will be useful,
++ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *     GNU General Public License for more details.
++ *
++ *     This program is distributed in the hope that it will be useful,
++ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
++ *     See http://www.gnu.org/licenses/gpl-2.0.html for more details.
++ *
++ * Alternatively,
++ *
++ *  b) Redistribution and use in source and binary forms, with or
++ *     without modification, are permitted provided that the following
++ *     conditions are met:
++ *
++ *     1. Redistributions of source code must retain the above
++ *        copyright notice, this list of conditions and the following
++ *        disclaimer.
++ *     2. Redistributions in binary form must reproduce the above
++ *        copyright notice, this list of conditions and the following
++ *        disclaimer in the documentation and/or other materials
++ *        provided with the distribution.
++ *
++ *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
++ *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
++ *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
++ *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++ *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
++ *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++ *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
++ *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
++ *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
++ *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
++ *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
++ *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++ */
++
++####################################################################################################
+\ No newline at end of file
+diff --git a/drivers/mtd/nandx/Nandx.config b/drivers/mtd/nandx/Nandx.config
+new file mode 100644
+index 0000000000..35705ee28d
+--- /dev/null
++++ b/drivers/mtd/nandx/Nandx.config
+@@ -0,0 +1,17 @@
++NANDX_SIMULATOR_SUPPORT := n
++NANDX_CTP_SUPPORT := n
++NANDX_DA_SUPPORT := n
++NANDX_PRELOADER_SUPPORT := n
++NANDX_LK_SUPPORT := n
++NANDX_KERNEL_SUPPORT := n
++NANDX_BROM_SUPPORT := n
++NANDX_UBOOT_SUPPORT := y
++NANDX_BBT_SUPPORT := y
++
++NANDX_NAND_SPI := y
++NANDX_NAND_SLC := n
++NANDX_NAND_MLC := n
++NANDX_NAND_TLC := n
++NANDX_NFI_BASE := y
++NANDX_NFI_ECC := y
++NANDX_NFI_SPI := y
+diff --git a/drivers/mtd/nandx/Nandx.mk b/drivers/mtd/nandx/Nandx.mk
+new file mode 100644
+index 0000000000..f5a6f2a628
+--- /dev/null
++++ b/drivers/mtd/nandx/Nandx.mk
+@@ -0,0 +1,91 @@
++#
++# Copyright (C) 2017 MediaTek Inc.
++# Licensed under either
++#     BSD Licence, (see NOTICE for more details)
++#     GNU General Public License, version 2.0, (see NOTICE for more details)
++#
++
++nandx_dir := $(shell dirname $(lastword $(MAKEFILE_LIST)))
++include $(nandx_dir)/Nandx.config
++
++ifeq ($(NANDX_SIMULATOR_SUPPORT), y)
++sim-obj :=
++sim-inc :=
++nandx-obj := sim-obj
++nandx-prefix := .
++nandx-postfix := %.o
++sim-inc += -I$(nandx-prefix)/include/internal
++sim-inc += -I$(nandx-prefix)/include/simulator
++endif
++
++ifeq ($(NANDX_CTP_SUPPORT), y)
++nandx-obj := C_SRC_FILES
++nandx-prefix := $(nandx_dir)
++nandx-postfix := %.c
++INC_DIRS += $(nandx_dir)/include/internal
++INC_DIRS += $(nandx_dir)/include/ctp
++endif
++
++ifeq ($(NANDX_DA_SUPPORT), y)
++nandx-obj := obj-y
++nandx-prefix := $(nandx_dir)
++nandx-postfix := %.o
++INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/internal
++INCLUDE_PATH += $(TOPDIR)/platform/$(CODE_BASE)/dev/nand/nandx/include/da
++endif
++
++ifeq ($(NANDX_PRELOADER_SUPPORT), y)
++nandx-obj := MOD_SRC
++nandx-prefix := $(nandx_dir)
++nandx-postfix := %.c
++C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/internal
++C_OPTION += -I$(MTK_PATH_PLATFORM)/src/drivers/nandx/include/preloader
++endif
++
++ifeq ($(NANDX_LK_SUPPORT), y)
++nandx-obj := MODULE_SRCS
++nandx-prefix := $(nandx_dir)
++nandx-postfix := %.c
++GLOBAL_INCLUDES += $(nandx_dir)/include/internal
++GLOBAL_INCLUDES += $(nandx_dir)/include/lk
++endif
++
++ifeq ($(NANDX_KERNEL_SUPPORT), y)
++nandx-obj := obj-y
++nandx-prefix := nandx
++nandx-postfix := %.o
++ccflags-y += -I$(nandx_dir)/include/internal
++ccflags-y += -I$(nandx_dir)/include/kernel
++endif
++
++ifeq ($(NANDX_UBOOT_SUPPORT), y)
++nandx-obj := obj-y
++nandx-prefix := nandx
++nandx-postfix := %.o
++ccflags-y += -I$(nandx_dir)/include/internal
++ccflags-y += -I$(nandx_dir)/include/uboot
++endif
++
++nandx-y :=
++include $(nandx_dir)/core/Nandx.mk
++nandx-target := $(nandx-prefix)/core/$(nandx-postfix)
++$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y))
++
++
++nandx-y :=
++include $(nandx_dir)/driver/Nandx.mk
++nandx-target := $(nandx-prefix)/driver/$(nandx-postfix)
++$(nandx-obj) += $(patsubst %.c, $(nandx-target), $(nandx-y))
++
++ifeq ($(NANDX_SIMULATOR_SUPPORT), y)
++cc := gcc
++CFLAGS += $(sim-inc)
++
++.PHONY:nandx
++nandx: $(sim-obj)
++      $(cc)  $(sim-obj) -o nandx
++
++.PHONY:clean
++clean:
++      rm -rf $(sim-obj) nandx
++endif
+diff --git a/drivers/mtd/nandx/README b/drivers/mtd/nandx/README
+new file mode 100644
+index 0000000000..0feaeaeb88
+--- /dev/null
++++ b/drivers/mtd/nandx/README
+@@ -0,0 +1,31 @@
++
++                          NAND2.0
++                ===============================
++
++    NAND2.0 is a common nand driver which designed for accessing
++different type of NANDs(SLC, SPI-NAND, MLC, TLC) on various OS. This
++driver can work on mostly SoCs of Mediatek.
++
++    Although there already has a common nand driver, it doesn't cover
++SPI-NAND, and not match our IC-Verification's reqirement. We need
++a driver that can be exten or cut easily.
++
++    This driver is base on NANDX & SLC. We try to refactor structures,
++and make them inheritable. We also refactor some operations' flow
++principally for adding SPI-NAND support.
++
++    This driver's architecture is like:
++
++          Driver @LK/Uboot/DA...           |IC verify/other purposes
++    ----------------------------------------------------------------
++      partition       |        BBM         |
++    -------------------------------------- |       extend_core
++             nandx_core/core_io            |
++    ----------------------------------------------------------------
++             nand_chip/nand_base           |
++    -------------------------------------- |        extend_nfi
++      nand_device     |    nfi/nfi_base    |
++
++    Any block of above graph can be extended at your will, if you
++want add new feature into this code, please make sure that your code
++would follow the framework, and we will be appreciated about it.
+diff --git a/drivers/mtd/nandx/core/Nandx.mk b/drivers/mtd/nandx/core/Nandx.mk
+new file mode 100644
+index 0000000000..7a5661c044
+--- /dev/null
++++ b/drivers/mtd/nandx/core/Nandx.mk
+@@ -0,0 +1,38 @@
++#
++# Copyright (C) 2017 MediaTek Inc.
++# Licensed under either
++#     BSD Licence, (see NOTICE for more details)
++#     GNU General Public License, version 2.0, (see NOTICE for more details)
++#
++
++nandx-y += nand_device.c
++nandx-y += nand_base.c
++nandx-y += nand_chip.c
++nandx-y += core_io.c
++
++nandx-header-y += nand_device.h
++nandx-header-y += nand_base.h
++nandx-header-y += nand_chip.h
++nandx-header-y += core_io.h
++nandx-header-y += nfi.h
++
++nandx-$(NANDX_NAND_SPI) += nand/device_spi.c
++nandx-$(NANDX_NAND_SPI) += nand/nand_spi.c
++nandx-$(NANDX_NAND_SLC) += nand/device_slc.c
++nandx-$(NANDX_NAND_SLC) += nand/nand_slc.c
++
++nandx-header-$(NANDX_NAND_SPI) += nand/device_spi.h
++nandx-header-$(NANDX_NAND_SPI) += nand/nand_spi.h
++nandx-header-$(NANDX_NAND_SLC) += nand/device_slc.h
++nandx-header-$(NANDX_NAND_SLC) += nand/nand_slc.h
++
++nandx-$(NANDX_NFI_BASE) += nfi/nfi_base.c
++nandx-$(NANDX_NFI_ECC) += nfi/nfiecc.c
++nandx-$(NANDX_NFI_SPI) += nfi/nfi_spi.c
++
++nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_base.h
++nandx-header-$(NANDX_NFI_BASE) += nfi/nfi_regs.h
++nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc.h
++nandx-header-$(NANDX_NFI_ECC) += nfi/nfiecc_regs.h
++nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi.h
++nandx-header-$(NANDX_NFI_SPI) += nfi/nfi_spi_regs.h
+diff --git a/drivers/mtd/nandx/core/core_io.c b/drivers/mtd/nandx/core/core_io.c
+new file mode 100644
+index 0000000000..716eeed38d
+--- /dev/null
++++ b/drivers/mtd/nandx/core/core_io.c
+@@ -0,0 +1,735 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++/*NOTE: switch cache/multi*/
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "nand_chip.h"
++#include "core_io.h"
++
++static struct nandx_desc *g_nandx;
++
++static inline bool is_sector_align(u64 val)
++{
++      return reminder(val, g_nandx->chip->sector_size) ? false : true;
++}
++
++static inline bool is_page_align(u64 val)
++{
++      return reminder(val, g_nandx->chip->page_size) ? false : true;
++}
++
++static inline bool is_block_align(u64 val)
++{
++      return reminder(val, g_nandx->chip->block_size) ? false : true;
++}
++
++static inline u32 page_sectors(void)
++{
++      return div_down(g_nandx->chip->page_size, g_nandx->chip->sector_size);
++}
++
++static inline u32 sector_oob(void)
++{
++      return div_down(g_nandx->chip->oob_size, page_sectors());
++}
++
++static inline u32 sector_padded_size(void)
++{
++      return g_nandx->chip->sector_size + g_nandx->chip->sector_spare_size;
++}
++
++static inline u32 page_padded_size(void)
++{
++      return page_sectors() * sector_padded_size();
++}
++
++static inline u32 offset_to_padded_col(u64 offset)
++{
++      struct nandx_desc *nandx = g_nandx;
++      u32 col, sectors;
++
++      col = reminder(offset, nandx->chip->page_size);
++      sectors = div_down(col, nandx->chip->sector_size);
++
++      return col + sectors * nandx->chip->sector_spare_size;
++}
++
++static inline u32 offset_to_row(u64 offset)
++{
++      return div_down(offset, g_nandx->chip->page_size);
++}
++
++static inline u32 offset_to_col(u64 offset)
++{
++      return reminder(offset, g_nandx->chip->page_size);
++}
++
++static inline u32 oob_upper_size(void)
++{
++      return g_nandx->ecc_en ? g_nandx->chip->oob_size :
++             g_nandx->chip->sector_spare_size * page_sectors();
++}
++
++static inline bool is_upper_oob_align(u64 val)
++{
++      return reminder(val, oob_upper_size()) ? false : true;
++}
++
++#define prepare_op(_op, _row, _col, _len, _data, _oob) \
++      do { \
++              (_op).row = (_row); \
++              (_op).col = (_col); \
++              (_op).len = (_len); \
++              (_op).data = (_data); \
++              (_op).oob = (_oob); \
++      } while (0)
++
++static int operation_multi(enum nandx_op_mode mode, u8 *data, u8 *oob,
++                         u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++      u32 row = offset_to_row(offset);
++      u32 col = offset_to_padded_col(offset);
++
++      if (nandx->mode == NANDX_IDLE) {
++              nandx->mode = mode;
++              nandx->ops_current = 0;
++      } else if (nandx->mode != mode) {
++              pr_info("forbid mixed operations.\n");
++              return -EOPNOTSUPP;
++      }
++
++      prepare_op(nandx->ops[nandx->ops_current], row, col, len, data, oob);
++      nandx->ops_current++;
++
++      if (nandx->ops_current == nandx->ops_multi_len)
++              return nandx_sync();
++
++      return nandx->ops_multi_len - nandx->ops_current;
++}
++
++static int operation_sequent(enum nandx_op_mode mode, u8 *data, u8 *oob,
++                           u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++      struct nand_chip *chip = nandx->chip;
++      u32 row = offset_to_row(offset);
++      func_chip_ops chip_ops;
++      u8 *ref_data = data, *ref_oob = oob;
++      int align, ops, row_step;
++      int i, rem;
++
++      align = data ? chip->page_size : oob_upper_size();
++      ops = data ? div_down(len, align) : div_down(len, oob_upper_size());
++      row_step = 1;
++
++      switch (mode) {
++      case NANDX_ERASE:
++              chip_ops = chip->erase_block;
++              align = chip->block_size;
++              ops = div_down(len, align);
++              row_step = chip->block_pages;
++              break;
++
++      case NANDX_READ:
++              chip_ops = chip->read_page;
++              break;
++
++      case NANDX_WRITE:
++              chip_ops = chip->write_page;
++              break;
++
++      default:
++              return -EINVAL;
++      }
++
++      if (!data) {
++              ref_data = nandx->head_buf;
++              memset(ref_data, 0xff, chip->page_size);
++      }
++
++      if (!oob) {
++              ref_oob = nandx->head_buf + chip->page_size;
++              memset(ref_oob, 0xff, oob_upper_size());
++      }
++
++      for (i = 0; i < ops; i++) {
++              prepare_op(nandx->ops[nandx->ops_current],
++                         row + i * row_step, 0, align, ref_data, ref_oob);
++              nandx->ops_current++;
++              /* if data or oob is null, nandx->head_buf or
++               * nandx->head_buf + chip->page_size should not been used
++               * so, here it is safe to use the buf.
++               */
++              ref_data = data ? ref_data + chip->page_size : nandx->head_buf;
++              ref_oob = oob ? ref_oob + oob_upper_size() :
++                        nandx->head_buf + chip->page_size;
++      }
++
++      if (nandx->mode == NANDX_WRITE) {
++              rem = reminder(nandx->ops_current, nandx->min_write_pages);
++              if (rem)
++                      return nandx->min_write_pages - rem;
++      }
++
++      nandx->ops_current = 0;
++      return chip_ops(chip, nandx->ops, ops);
++}
++
++static int read_pages(u8 *data, u8 *oob, u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++      struct nand_chip *chip = nandx->chip;
++      struct nandx_split64 split = {0};
++      u8 *ref_data = data, *ref_oob;
++      u32 row, col;
++      int ret = 0, i, ops;
++      u32 head_offset = 0;
++      u64 val;
++
++      if (!data)
++              return operation_sequent(NANDX_READ, NULL, oob, offset, len);
++
++      ref_oob = oob ? oob : nandx->head_buf + chip->page_size;
++
++      nandx_split(&split, offset, len, val, chip->page_size);
++
++      if (split.head_len) {
++              row = offset_to_row(split.head);
++              col = offset_to_col(split.head);
++              prepare_op(nandx->ops[nandx->ops_current], row, 0,
++                         chip->page_size,
++                         nandx->head_buf, ref_oob);
++              nandx->ops_current++;
++
++              head_offset = col;
++
++              ref_data += split.head_len;
++              ref_oob = oob ? ref_oob + oob_upper_size() :
++                        nandx->head_buf + chip->page_size;
++      }
++
++      if (split.body_len) {
++              ops = div_down(split.body_len, chip->page_size);
++              row = offset_to_row(split.body);
++              for (i = 0; i < ops; i++) {
++                      prepare_op(nandx->ops[nandx->ops_current],
++                                 row + i, 0, chip->page_size,
++                                 ref_data, ref_oob);
++                      nandx->ops_current++;
++                      ref_data += chip->page_size;
++                      ref_oob = oob ? ref_oob + oob_upper_size() :
++                                nandx->head_buf + chip->page_size;
++              }
++      }
++
++      if (split.tail_len) {
++              row = offset_to_row(split.tail);
++              prepare_op(nandx->ops[nandx->ops_current], row, 0,
++                         chip->page_size, nandx->tail_buf, ref_oob);
++              nandx->ops_current++;
++      }
++
++      ret = chip->read_page(chip, nandx->ops, nandx->ops_current);
++
++      if (split.head_len)
++              memcpy(data, nandx->head_buf + head_offset, split.head_len);
++      if (split.tail_len)
++              memcpy(ref_data, nandx->tail_buf, split.tail_len);
++
++      nandx->ops_current = 0;
++      return ret;
++}
++
++int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++
++      if (!len || len > nandx->info.total_size)
++              return -EINVAL;
++      if (div_up(len, nandx->chip->page_size) > nandx->ops_len)
++              return -EINVAL;
++      if (!data && !oob)
++              return -EINVAL;
++      /**
++       * as design, oob not support partial read
++       * and, the length of oob buf should be oob size aligned
++       */
++      if (!data && !is_upper_oob_align(len))
++              return -EINVAL;
++
++      if (g_nandx->multi_en) {
++              /* as design, there only 2 buf for partial read,
++               * if partial read allowed for multi read,
++               * there are not enough buf
++               */
++              if (!is_sector_align(offset))
++                      return -EINVAL;
++              if (data && !is_sector_align(len))
++                      return -EINVAL;
++              return operation_multi(NANDX_READ, data, oob, offset, len);
++      }
++
++      nandx->ops_current = 0;
++      nandx->mode = NANDX_IDLE;
++      return read_pages(data, oob, offset, len);
++}
++
++static int write_pages(u8 *data, u8 *oob, u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++      struct nand_chip *chip = nandx->chip;
++      struct nandx_split64 split = {0};
++      int ret, rem, i, ops;
++      u32 row, col;
++      u8 *ref_oob = oob;
++      u64 val;
++
++      nandx->mode = NANDX_WRITE;
++
++      if (!data)
++              return operation_sequent(NANDX_WRITE, NULL, oob, offset, len);
++
++      if (!oob) {
++              ref_oob = nandx->head_buf + chip->page_size;
++              memset(ref_oob, 0xff, oob_upper_size());
++      }
++
++      nandx_split(&split, offset, len, val, chip->page_size);
++
++      /*NOTE: slc can support sector write, here copy too many data.*/
++      if (split.head_len) {
++              row = offset_to_row(split.head);
++              col = offset_to_col(split.head);
++              memset(nandx->head_buf, 0xff, page_padded_size());
++              memcpy(nandx->head_buf + col, data, split.head_len);
++              prepare_op(nandx->ops[nandx->ops_current], row, 0,
++                         chip->page_size, nandx->head_buf, ref_oob);
++              nandx->ops_current++;
++
++              data += split.head_len;
++              ref_oob = oob ? ref_oob + oob_upper_size() :
++                        nandx->head_buf + chip->page_size;
++      }
++
++      if (split.body_len) {
++              row = offset_to_row(split.body);
++              ops = div_down(split.body_len, chip->page_size);
++              for (i = 0; i < ops; i++) {
++                      prepare_op(nandx->ops[nandx->ops_current],
++                                 row + i, 0, chip->page_size, data, ref_oob);
++                      nandx->ops_current++;
++                      data += chip->page_size;
++                      ref_oob = oob ? ref_oob + oob_upper_size() :
++                                nandx->head_buf + chip->page_size;
++              }
++      }
++
++      if (split.tail_len) {
++              row = offset_to_row(split.tail);
++              memset(nandx->tail_buf, 0xff, page_padded_size());
++              memcpy(nandx->tail_buf, data, split.tail_len);
++              prepare_op(nandx->ops[nandx->ops_current], row, 0,
++                         chip->page_size, nandx->tail_buf, ref_oob);
++              nandx->ops_current++;
++      }
++
++      rem = reminder(nandx->ops_current, nandx->min_write_pages);
++      if (rem)
++              return nandx->min_write_pages - rem;
++
++      ret = chip->write_page(chip, nandx->ops, nandx->ops_current);
++
++      nandx->ops_current = 0;
++      nandx->mode = NANDX_IDLE;
++      return ret;
++}
++
++int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++
++      if (!len || len > nandx->info.total_size)
++              return -EINVAL;
++      if (div_up(len, nandx->chip->page_size) > nandx->ops_len)
++              return -EINVAL;
++      if (!data && !oob)
++              return -EINVAL;
++      if (!data && !is_upper_oob_align(len))
++              return -EINVAL;
++
++      if (nandx->multi_en) {
++              if (!is_page_align(offset))
++                      return -EINVAL;
++              if (data && !is_page_align(len))
++                      return -EINVAL;
++
++              return operation_multi(NANDX_WRITE, data, oob, offset, len);
++      }
++
++      return write_pages(data, oob, offset, len);
++}
++
++int nandx_erase(u64 offset, size_t len)
++{
++      struct nandx_desc *nandx = g_nandx;
++
++      if (!len || len > nandx->info.total_size)
++              return -EINVAL;
++      if (div_down(len, nandx->chip->block_size) > nandx->ops_len)
++              return -EINVAL;
++      if (!is_block_align(offset) || !is_block_align(len))
++              return -EINVAL;
++
++      if (g_nandx->multi_en)
++              return operation_multi(NANDX_ERASE, NULL, NULL, offset, len);
++
++      nandx->ops_current = 0;
++      nandx->mode = NANDX_IDLE;
++      return operation_sequent(NANDX_ERASE, NULL, NULL, offset, len);
++}
++
++int nandx_sync(void)
++{
++      struct nandx_desc *nandx = g_nandx;
++      struct nand_chip *chip = nandx->chip;
++      func_chip_ops chip_ops;
++      int ret, i, rem;
++
++      if (!nandx->ops_current)
++              return 0;
++
++      rem = reminder(nandx->ops_current, nandx->ops_multi_len);
++      if (nandx->multi_en && rem) {
++              ret = -EIO;
++              goto error;
++      }
++
++      switch (nandx->mode) {
++      case NANDX_IDLE:
++              return 0;
++      case NANDX_ERASE:
++              chip_ops = chip->erase_block;
++              break;
++      case NANDX_READ:
++              chip_ops = chip->read_page;
++              break;
++      case NANDX_WRITE:
++              chip_ops = chip->write_page;
++              break;
++      default:
++              return -EINVAL;
++      }
++
++      rem = reminder(nandx->ops_current, nandx->min_write_pages);
++      if (!nandx->multi_en && nandx->mode == NANDX_WRITE && rem) {
++              /* in one process of program, only allow 2 pages to do partial
++               * write, here we supposed 1st buf would be used, and 2nd
++               * buf should be not used.
++               */
++              memset(nandx->tail_buf, 0xff,
++                     chip->page_size + oob_upper_size());
++              for (i = 0; i < rem; i++) {
++                      prepare_op(nandx->ops[nandx->ops_current],
++                                 nandx->ops[nandx->ops_current - 1].row + 1,
++                                 0, chip->page_size, nandx->tail_buf,
++                                 nandx->tail_buf + chip->page_size);
++                      nandx->ops_current++;
++              }
++      }
++
++      ret = chip_ops(nandx->chip, nandx->ops, nandx->ops_current);
++
++error:
++      nandx->mode = NANDX_IDLE;
++      nandx->ops_current = 0;
++
++      return ret;
++}
++
++int nandx_ioctl(int cmd, void *arg)
++{
++      struct nandx_desc *nandx = g_nandx;
++      struct nand_chip *chip = nandx->chip;
++      int ret = 0;
++
++      switch (cmd) {
++      case CORE_CTRL_NAND_INFO:
++              *(struct nandx_info *)arg = nandx->info;
++              break;
++
++      case CHIP_CTRL_OPS_MULTI:
++              ret = chip->chip_ctrl(chip, cmd, arg);
++              if (!ret)
++                      nandx->multi_en = *(bool *)arg;
++              break;
++
++      case NFI_CTRL_ECC:
++              ret = chip->chip_ctrl(chip, cmd, arg);
++              if (!ret)
++                      nandx->ecc_en = *(bool *)arg;
++              break;
++
++      default:
++              ret = chip->chip_ctrl(chip, cmd, arg);
++              break;
++      }
++
++      return ret;
++}
++
++bool nandx_is_bad_block(u64 offset)
++{
++      struct nandx_desc *nandx = g_nandx;
++
++      prepare_op(nandx->ops[0], offset_to_row(offset), 0,
++                 nandx->chip->page_size, nandx->head_buf,
++                 nandx->head_buf + nandx->chip->page_size);
++
++      return nandx->chip->is_bad_block(nandx->chip, nandx->ops, 1);
++}
++
++int nandx_suspend(void)
++{
++      return g_nandx->chip->suspend(g_nandx->chip);
++}
++
++int nandx_resume(void)
++{
++      return g_nandx->chip->resume(g_nandx->chip);
++}
++
++int nandx_init(struct nfi_resource *res)
++{
++      struct nand_chip *chip;
++      struct nandx_desc *nandx;
++      int ret = 0;
++
++      if (!res)
++              return -EINVAL;
++
++      chip = nand_chip_init(res);
++      if (!chip) {
++              pr_info("nand chip init fail.\n");
++              return -EFAULT;
++      }
++
++      nandx = (struct nandx_desc *)mem_alloc(1, sizeof(struct nandx_desc));
++      if (!nandx)
++              return -ENOMEM;
++
++      g_nandx = nandx;
++
++      nandx->chip = chip;
++      nandx->min_write_pages = chip->min_program_pages;
++      nandx->ops_multi_len = nandx->min_write_pages * chip->plane_num;
++      nandx->ops_len = chip->block_pages * chip->plane_num;
++      nandx->ops = mem_alloc(1, sizeof(struct nand_ops) * nandx->ops_len);
++      if (!nandx->ops) {
++              ret = -ENOMEM;
++              goto ops_error;
++      }
++
++#if NANDX_BULK_IO_USE_DRAM
++      nandx->head_buf = NANDX_CORE_BUF_ADDR;
++#else
++      nandx->head_buf = mem_alloc(2, page_padded_size());
++#endif
++      if (!nandx->head_buf) {
++              ret = -ENOMEM;
++              goto buf_error;
++      }
++      nandx->tail_buf = nandx->head_buf + page_padded_size();
++      memset(nandx->head_buf, 0xff, 2 * page_padded_size());
++      nandx->multi_en = false;
++      nandx->ecc_en = false;
++      nandx->ops_current = 0;
++      nandx->mode = NANDX_IDLE;
++
++      nandx->info.max_io_count = nandx->ops_len;
++      nandx->info.min_write_pages = nandx->min_write_pages;
++      nandx->info.plane_num = chip->plane_num;
++      nandx->info.oob_size = chip->oob_size;
++      nandx->info.page_parity_size = chip->sector_spare_size * page_sectors();
++      nandx->info.page_size = chip->page_size;
++      nandx->info.block_size = chip->block_size;
++      nandx->info.total_size = chip->block_size * chip->block_num;
++      nandx->info.fdm_ecc_size = chip->fdm_ecc_size;
++      nandx->info.fdm_reg_size = chip->fdm_reg_size;
++      nandx->info.ecc_strength = chip->ecc_strength;
++      nandx->info.sector_size = chip->sector_size;
++
++      return 0;
++
++buf_error:
++#if !NANDX_BULK_IO_USE_DRAM
++      mem_free(nandx->head_buf);
++#endif
++ops_error:
++      mem_free(nandx);
++
++      return ret;
++}
++
++void nandx_exit(void)
++{
++      nand_chip_exit(g_nandx->chip);
++#if !NANDX_BULK_IO_USE_DRAM
++      mem_free(g_nandx->head_buf);
++#endif
++      mem_free(g_nandx->ops);
++      mem_free(g_nandx);
++}
++
++#ifdef NANDX_UNIT_TEST
++static void dump_buf(u8 *buf, u32 len)
++{
++      u32 i;
++
++      pr_info("dump buf@0x%X start", (u32)buf);
++      for (i = 0; i < len; i++) {
++              if (!reminder(i, 16))
++                      pr_info("\n0x");
++              pr_info("%x ", buf[i]);
++      }
++      pr_info("\ndump buf done.\n");
++}
++
++int nandx_unit_test(u64 offset, size_t len)
++{
++      u8 *src_buf, *dst_buf;
++      u32 i, j;
++      int ret;
++
++      if (!len || len > g_nandx->chip->block_size)
++              return -EINVAL;
++
++#if NANDX_BULK_IO_USE_DRAM
++      src_buf = NANDX_UT_SRC_ADDR;
++      dst_buf = NANDX_UT_DST_ADDR;
++
++#else
++      src_buf = mem_alloc(1, g_nandx->chip->page_size);
++      if (!src_buf)
++              return -ENOMEM;
++      dst_buf = mem_alloc(1, g_nandx->chip->page_size);
++      if (!dst_buf) {
++              mem_free(src_buf);
++              return -ENOMEM;
++      }
++#endif
++
++      pr_info("%s: src_buf address 0x%x, dst_buf address 0x%x\n",
++               __func__, (int)((unsigned long)src_buf),
++               (int)((unsigned long)dst_buf));
++
++      memset(dst_buf, 0, g_nandx->chip->page_size);
++      pr_info("read page 0 data...!\n");
++      ret = nandx_read(dst_buf, NULL, 0, g_nandx->chip->page_size);
++      if (ret < 0) {
++              pr_info("read fail with ret %d\n", ret);
++      } else {
++              pr_info("read page success!\n");
++      }
++
++      for (i = 0; i < g_nandx->chip->page_size; i++) {
++              src_buf[i] = 0x5a;
++      }
++
++      ret = nandx_erase(offset, g_nandx->chip->block_size);
++      if (ret < 0) {
++              pr_info("erase fail with ret %d\n", ret);
++              goto error;
++      }
++
++      for (j = 0; j < g_nandx->chip->block_pages; j++) {
++              memset(dst_buf, 0, g_nandx->chip->page_size);
++              pr_info("check data after erase...!\n");
++              ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
++              if (ret < 0) {
++                      pr_info("read fail with ret %d\n", ret);
++                      goto error;
++              }
++
++              for (i = 0; i < g_nandx->chip->page_size; i++) {
++                      if (dst_buf[i] != 0xff) {
++                              pr_info("read after erase, check fail @%d\n", i);
++                              pr_info("all data should be 0xff\n");
++                              ret = -ENANDERASE;
++                              dump_buf(dst_buf, 128);
++                              //goto error;
++                              break;
++                      }
++              }
++
++              pr_info("write data...!\n");
++              ret = nandx_write(src_buf, NULL, offset, g_nandx->chip->page_size);
++              if (ret < 0) {
++                      pr_info("write fail with ret %d\n", ret);
++                      goto error;
++              }
++
++              memset(dst_buf, 0, g_nandx->chip->page_size);
++              pr_info("read data...!\n");
++              ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
++              if (ret < 0) {
++                      pr_info("read fail with ret %d\n", ret);
++                      goto error;
++              }
++
++              for (i = 0; i < g_nandx->chip->page_size; i++) {
++                      if (dst_buf[i] != src_buf[i]) {
++                              pr_info("read after write, check fail @%d\n", i);
++                              pr_info("dst_buf should be same as src_buf\n");
++                              ret = -EIO;
++                              dump_buf(src_buf + i, 128);
++                              dump_buf(dst_buf + i, 128);
++                              break;
++                      }
++              }
++
++              pr_err("%s %d %s@%d\n", __func__, __LINE__, ret?"Failed":"OK", j);
++              if (ret)
++                      break;
++
++              offset += g_nandx->chip->page_size;
++      }
++
++      ret = nandx_erase(offset, g_nandx->chip->block_size);
++      if (ret < 0) {
++              pr_info("erase fail with ret %d\n", ret);
++              goto error;
++      }
++
++      memset(dst_buf, 0, g_nandx->chip->page_size);
++      ret = nandx_read(dst_buf, NULL, offset, g_nandx->chip->page_size);
++      if (ret < 0) {
++              pr_info("read fail with ret %d\n", ret);
++              goto error;
++      }
++
++      for (i = 0; i < g_nandx->chip->page_size; i++) {
++              if (dst_buf[i] != 0xff) {
++                      pr_info("read after erase, check fail\n");
++                      pr_info("all data should be 0xff\n");
++                      ret = -ENANDERASE;
++                      dump_buf(dst_buf, 128);
++                      goto error;
++              }
++      }
++
++      return 0;
++
++error:
++#if !NANDX_BULK_IO_USE_DRAM
++      mem_free(src_buf);
++      mem_free(dst_buf);
++#endif
++      return ret;
++}
++#endif
+diff --git a/drivers/mtd/nandx/core/core_io.h b/drivers/mtd/nandx/core/core_io.h
+new file mode 100644
+index 0000000000..edcb60908a
+--- /dev/null
++++ b/drivers/mtd/nandx/core/core_io.h
+@@ -0,0 +1,39 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __CORE_IO_H__
++#define __CORE_IO_H__
++
++typedef int (*func_chip_ops)(struct nand_chip *, struct nand_ops *,
++                           int);
++
++enum nandx_op_mode {
++      NANDX_IDLE,
++      NANDX_WRITE,
++      NANDX_READ,
++      NANDX_ERASE
++};
++
++struct nandx_desc {
++      struct nand_chip *chip;
++      struct nandx_info info;
++      enum nandx_op_mode mode;
++
++      bool multi_en;
++      bool ecc_en;
++
++      struct nand_ops *ops;
++      int ops_len;
++      int ops_multi_len;
++      int ops_current;
++      int min_write_pages;
++
++      u8 *head_buf;
++      u8 *tail_buf;
++};
++
++#endif /* __CORE_IO_H__ */
+diff --git a/drivers/mtd/nandx/core/nand/device_spi.c b/drivers/mtd/nandx/core/nand/device_spi.c
+new file mode 100644
+index 0000000000..db338c28c2
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand/device_spi.c
+@@ -0,0 +1,200 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "../nand_device.h"
++#include "device_spi.h"
++
++/* spi nand basic commands */
++static struct nand_cmds spi_cmds = {
++      .reset = 0xff,
++      .read_id = 0x9f,
++      .read_status = 0x0f,
++      .read_param_page = 0x03,
++      .set_feature = 0x1f,
++      .get_feature = 0x0f,
++      .read_1st = 0x13,
++      .read_2nd = -1,
++      .random_out_1st = 0x03,
++      .random_out_2nd = -1,
++      .program_1st = 0x02,
++      .program_2nd = 0x10,
++      .erase_1st = 0xd8,
++      .erase_2nd = -1,
++      .read_cache = 0x30,
++      .read_cache_last = 0x3f,
++      .program_cache = 0x02
++};
++
++/* spi nand extend commands */
++static struct spi_extend_cmds spi_extend_cmds = {
++      .die_select = 0xc2,
++      .write_enable = 0x06
++};
++
++/* means the start bit of addressing type */
++static struct nand_addressing spi_addressing = {
++      .row_bit_start = 0,
++      .block_bit_start = 0,
++      .plane_bit_start = 12,
++      .lun_bit_start = 0,
++};
++
++/* spi nand endurance */
++static struct nand_endurance spi_endurance = {
++      .pe_cycle = 100000,
++      .ecc_req = 1,
++      .max_bitflips = 1
++};
++
++/* array_busy, write_protect, erase_fail, program_fail */
++static struct nand_status spi_status[] = {
++      {.array_busy = BIT(0),
++      .write_protect = BIT(1),
++      .erase_fail = BIT(2),
++      .program_fail = BIT(3)}
++};
++
++/* measure time by the us */
++static struct nand_array_timing spi_array_timing = {
++      .tRST = 500,
++      .tWHR = 1,
++      .tR = 25,
++      .tRCBSY = 25,
++      .tFEAT = 1,
++      .tPROG = 600,
++      .tPCBSY = 600,
++      .tBERS = 10000,
++      .tDBSY = 1
++};
++
++/* spi nand device table */
++static struct device_spi spi_nand[] = {
++      {
++              NAND_DEVICE("W25N01GV",
++                          NAND_PACK_ID(0xef, 0xaa, 0x21, 0, 0, 0, 0, 0),
++                          3, 0, 3, 3,
++                          1, 1, 1, 1024, KB(128), KB(2), 64, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 0),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      },
++      {
++              NAND_DEVICE("MX35LF1G",
++                          NAND_PACK_ID(0xc2, 0x12, 0x21, 0, 0, 0, 0, 0),
++                          2, 0, 3, 3,
++                          1, 1, 1, 1024, KB(128), KB(2), 64, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      },
++      {
++              NAND_DEVICE("MT29F4G01ABAFDWB",
++                          NAND_PACK_ID(0x2c, 0x34, 0, 0, 0, 0, 0, 0),
++                          2, 0, 3, 3,
++                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      },
++      {
++              NAND_DEVICE("GD5F4GQ4UB",
++                          NAND_PACK_ID(0xc8, 0xd4, 0, 0, 0, 0, 0, 0),
++                          2, 0, 3, 3,
++                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      },
++      {
++              NAND_DEVICE("TC58CVG2S0HRAIJ",
++                          NAND_PACK_ID(0x98, 0xED, 0x51, 0, 0, 0, 0, 0),
++                          3, 0, 3, 3,
++                          1, 1, 1, 2048, KB(256), KB(4), 256, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 1),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      },
++      {
++              NAND_DEVICE("NO-DEVICE",
++                          NAND_PACK_ID(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, 0,
++                          0, 0, 0, 0, 0, 0, 0, 1,
++                          &spi_cmds, &spi_addressing, &spi_status[0],
++                          &spi_endurance, &spi_array_timing),
++              {
++                      NAND_SPI_PROTECT(0xa0, 1, 2, 6),
++                      NAND_SPI_CONFIG(0xb0, 4, 6, 0),
++                      NAND_SPI_STATUS(0xc0, 4, 5),
++                      NAND_SPI_CHARACTER(0xff, 0xff, 0xff, 0xff)
++              },
++              &spi_extend_cmds, 0xff, 0xff
++      }
++};
++
++u8 spi_replace_rx_cmds(u8 mode)
++{
++      u8 rx_replace_cmds[] = {0x03, 0x3b, 0x6b, 0xbb, 0xeb};
++
++      return rx_replace_cmds[mode];
++}
++
++u8 spi_replace_tx_cmds(u8 mode)
++{
++      u8 tx_replace_cmds[] = {0x02, 0x32};
++
++      return tx_replace_cmds[mode];
++}
++
++u8 spi_replace_rx_col_cycle(u8 mode)
++{
++      u8 rx_replace_col_cycle[] = {3, 3, 3, 3, 4};
++
++      return rx_replace_col_cycle[mode];
++}
++
++u8 spi_replace_tx_col_cycle(u8 mode)
++{
++      u8 tx_replace_col_cycle[] = {2, 2};
++
++      return tx_replace_col_cycle[mode];
++}
++
++struct nand_device *nand_get_device(int index)
++{
++      return &spi_nand[index].dev;
++}
++
+diff --git a/drivers/mtd/nandx/core/nand/device_spi.h b/drivers/mtd/nandx/core/nand/device_spi.h
+new file mode 100644
+index 0000000000..1676b61fc8
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand/device_spi.h
+@@ -0,0 +1,132 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __DEVICE_SPI_H__
++#define __DEVICE_SPI_H__
++
++/*
++ * extend commands
++ * @die_select: select nand device die command
++ * @write_enable: enable write command before write data to spi nand
++ *    spi nand device will auto to be disable after write done
++ */
++struct spi_extend_cmds {
++      short die_select;
++      short write_enable;
++};
++
++/*
++ * protection feature register
++ * @addr: register address
++ * @wp_en_bit: write protection enable bit
++ * @bp_start_bit: block protection mask start bit
++ * @bp_end_bit: block protection mask end bit
++ */
++struct feature_protect {
++      u8 addr;
++      u8 wp_en_bit;
++      u8 bp_start_bit;
++      u8 bp_end_bit;
++};
++
++/*
++ * configuration feature register
++ * @addr: register address
++ * @ecc_en_bit: in-die ecc enable bit
++ * @otp_en_bit: enter otp access mode bit
++ * @need_qe: quad io enable bit
++ */
++struct feature_config {
++      u8 addr;
++      u8 ecc_en_bit;
++      u8 otp_en_bit;
++      u8 need_qe;
++};
++
++/*
++ * status feature register
++ * @addr: register address
++ * @ecc_start_bit: ecc status mask start bit for error bits number
++ * @ecc_end_bit: ecc status mask end bit for error bits number
++ * note that:
++ *   operations status (ex. array busy status) could see on struct nand_status
++ */
++struct feature_status {
++      u8 addr;
++      u8 ecc_start_bit;
++      u8 ecc_end_bit;
++};
++
++/*
++ * character feature register
++ * @addr: register address
++ * @die_sel_bit: die select bit
++ * @drive_start_bit: drive strength mask start bit
++ * @drive_end_bit: drive strength mask end bit
++ */
++struct feature_character {
++      u8 addr;
++      u8 die_sel_bit;
++      u8 drive_start_bit;
++      u8 drive_end_bit;
++};
++
++/*
++ * spi features
++ * @protect: protection feature register
++ * @config: configuration feature register
++ * @status: status feature register
++ * @character: character feature register
++ */
++struct spi_features {
++      struct feature_protect protect;
++      struct feature_config config;
++      struct feature_status status;
++      struct feature_character character;
++};
++
++/*
++ * device_spi
++ *    configurations of spi nand device table
++ * @dev: base information of nand device
++ * @feature: feature information for spi nand
++ * @extend_cmds: extended the nand base commands
++ * @tx_mode_mask: tx mode mask for chip read
++ * @rx_mode_mask: rx mode mask for chip write
++ */
++struct device_spi {
++      struct nand_device dev;
++      struct spi_features feature;
++      struct spi_extend_cmds *extend_cmds;
++
++      u8 tx_mode_mask;
++      u8 rx_mode_mask;
++};
++
++#define NAND_SPI_PROTECT(addr, wp_en_bit, bp_start_bit, bp_end_bit) \
++      {addr, wp_en_bit, bp_start_bit, bp_end_bit}
++
++#define NAND_SPI_CONFIG(addr, ecc_en_bit, otp_en_bit, need_qe) \
++      {addr, ecc_en_bit, otp_en_bit, need_qe}
++
++#define NAND_SPI_STATUS(addr, ecc_start_bit, ecc_end_bit) \
++      {addr, ecc_start_bit, ecc_end_bit}
++
++#define NAND_SPI_CHARACTER(addr, die_sel_bit, drive_start_bit, drive_end_bit) \
++      {addr, die_sel_bit, drive_start_bit, drive_end_bit}
++
++static inline struct device_spi *device_to_spi(struct nand_device *dev)
++{
++      return container_of(dev, struct device_spi, dev);
++}
++
++u8 spi_replace_rx_cmds(u8 mode);
++u8 spi_replace_tx_cmds(u8 mode);
++u8 spi_replace_rx_col_cycle(u8 mode);
++u8 spi_replace_tx_col_cycle(u8 mode);
++
++#endif /* __DEVICE_SPI_H__ */
+diff --git a/drivers/mtd/nandx/core/nand/nand_spi.c b/drivers/mtd/nandx/core/nand/nand_spi.c
+new file mode 100644
+index 0000000000..2ae03e1cf4
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand/nand_spi.c
+@@ -0,0 +1,526 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "../nand_chip.h"
++#include "../nand_device.h"
++#include "../nfi.h"
++#include "../nand_base.h"
++#include "device_spi.h"
++#include "nand_spi.h"
++
++#define READY_TIMEOUT   500000 /* us */
++
++static int nand_spi_read_status(struct nand_base *nand)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      u8 status;
++
++      nand->get_feature(nand, dev->feature.status.addr, &status, 1);
++
++      return status;
++}
++
++static int nand_spi_wait_ready(struct nand_base *nand, u32 timeout)
++{
++      u64 now, end;
++      int status;
++
++      end = get_current_time_us() + timeout;
++
++      do {
++              status = nand_spi_read_status(nand);
++              status &= nand->dev->status->array_busy;
++              now = get_current_time_us();
++
++              if (now > end)
++                      break;
++      } while (status);
++
++      return status ? -EBUSY : 0;
++}
++
++static int nand_spi_set_op_mode(struct nand_base *nand, u8 mode)
++{
++      struct nand_spi *spi_nand = base_to_spi(nand);
++      struct nfi *nfi = nand->nfi;
++      int ret = 0;
++
++      if (spi_nand->op_mode != mode) {
++              ret = nfi->nfi_ctrl(nfi, SNFI_CTRL_OP_MODE, (void *)&mode);
++              spi_nand->op_mode = mode;
++      }
++
++      return ret;
++}
++
++static int nand_spi_set_config(struct nand_base *nand, u8 addr, u8 mask,
++                             bool en)
++{
++      u8 configs = 0;
++
++      nand->get_feature(nand, addr, &configs, 1);
++
++      if (en)
++              configs |= mask;
++      else
++              configs &= ~mask;
++
++      nand->set_feature(nand, addr, &configs, 1);
++
++      configs = 0;
++      nand->get_feature(nand, addr, &configs, 1);
++
++      return (configs & mask) == en ? 0 : -EFAULT;
++}
++
++static int nand_spi_die_select(struct nand_base *nand, int *row)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nfi *nfi = nand->nfi;
++      int lun_blocks, block_pages, lun, blocks;
++      int page = *row, ret = 0;
++      u8 param = 0, die_sel;
++
++      if (nand->dev->lun_num < 2)
++              return 0;
++
++      block_pages = nand_block_pages(nand->dev);
++      lun_blocks = nand_lun_blocks(nand->dev);
++      blocks = div_down(page, block_pages);
++      lun = div_down(blocks, lun_blocks);
++
++      if (dev->extend_cmds->die_select == -1) {
++              die_sel = (u8)(lun << dev->feature.character.die_sel_bit);
++              nand->get_feature(nand, dev->feature.character.addr, &param, 1);
++              param |= die_sel;
++              nand->set_feature(nand, dev->feature.character.addr, &param, 1);
++              param = 0;
++              nand->get_feature(nand, dev->feature.character.addr, &param, 1);
++              ret = (param & die_sel) ? 0 : -EFAULT;
++      } else {
++              nfi->reset(nfi);
++              nfi->send_cmd(nfi, dev->extend_cmds->die_select);
++              nfi->send_addr(nfi, lun, 0, 1, 0);
++              nfi->trigger(nfi);
++      }
++
++      *row =  page - (lun_blocks * block_pages) * lun;
++
++      return ret;
++}
++
++static int nand_spi_select_device(struct nand_base *nand, int cs)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      return parent->select_device(nand, cs);
++}
++
++static int nand_spi_reset(struct nand_base *nand)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      parent->reset(nand);
++
++      return nand_spi_wait_ready(nand, READY_TIMEOUT);
++}
++
++static int nand_spi_read_id(struct nand_base *nand, u8 *id, int count)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      return parent->read_id(nand, id, count);
++}
++
++static int nand_spi_read_param_page(struct nand_base *nand, u8 *data,
++                                  int count)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nfi *nfi = nand->nfi;
++      int sectors, value;
++      u8 param = 0;
++
++      sectors = div_round_up(count, nfi->sector_size);
++
++      nand->get_feature(nand, dev->feature.config.addr, &param, 1);
++      param |= BIT(dev->feature.config.otp_en_bit);
++      nand->set_feature(nand, dev->feature.config.addr, &param, 1);
++
++      param = 0;
++      nand->get_feature(nand, dev->feature.config.addr, &param, 1);
++      if (param & BIT(dev->feature.config.otp_en_bit)) {
++              value = 0;
++              nfi->nfi_ctrl(nfi, NFI_CTRL_ECC, &value);
++              nand->dev->col_cycle  = spi_replace_rx_col_cycle(spi->rx_mode);
++              nand->read_page(nand, 0x01);
++              nand->read_data(nand, 0x01, 0, sectors, data, NULL);
++      }
++
++      param &= ~BIT(dev->feature.config.otp_en_bit);
++      nand->set_feature(nand, dev->feature.config.addr, &param, 1);
++
++      return 0;
++}
++
++static int nand_spi_set_feature(struct nand_base *nand, u8 addr,
++                              u8 *param,
++                              int count)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand->write_enable(nand);
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      return parent->set_feature(nand, addr, param, count);
++}
++
++static int nand_spi_get_feature(struct nand_base *nand, u8 addr,
++                              u8 *param,
++                              int count)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      return parent->get_feature(nand, addr, param, count);
++}
++
++static int nand_spi_addressing(struct nand_base *nand, int *row,
++                             int *col)
++{
++      struct nand_device *dev = nand->dev;
++      int plane, block, block_pages;
++      int ret;
++
++      ret = nand_spi_die_select(nand, row);
++      if (ret)
++              return ret;
++
++      block_pages = nand_block_pages(dev);
++      block = div_down(*row, block_pages);
++
++      plane = block % dev->plane_num;
++      *col |= (plane << dev->addressing->plane_bit_start);
++
++      return 0;
++}
++
++static int nand_spi_read_page(struct nand_base *nand, int row)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      if (spi->op_mode == SNFI_AUTO_MODE)
++              nand_spi_set_op_mode(nand, SNFI_AUTO_MODE);
++      else
++              nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      parent->read_page(nand, row);
++
++      return nand_spi_wait_ready(nand, READY_TIMEOUT);
++}
++
++static int nand_spi_read_data(struct nand_base *nand, int row, int col,
++                            int sectors, u8 *data, u8 *oob)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++      int ret;
++
++      if ((spi->rx_mode == SNFI_RX_114 || spi->rx_mode == SNFI_RX_144) &&
++          dev->feature.config.need_qe)
++              nand_spi_set_config(nand, dev->feature.config.addr,
++                                  BIT(0), true);
++
++      nand->dev->col_cycle  = spi_replace_rx_col_cycle(spi->rx_mode);
++
++      nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE);
++
++      ret = parent->read_data(nand, row, col, sectors, data, oob);
++      if (ret)
++              return -ENANDREAD;
++
++      if (spi->ondie_ecc) {
++              ret = nand_spi_read_status(nand);
++              ret &= GENMASK(dev->feature.status.ecc_end_bit,
++                             dev->feature.status.ecc_start_bit);
++              ret >>= dev->feature.status.ecc_start_bit;
++              if (ret > nand->dev->endurance->ecc_req)
++                      return -ENANDREAD;
++              else if (ret > nand->dev->endurance->max_bitflips)
++                      return -ENANDFLIPS;
++      }
++
++      return 0;
++}
++
++static int nand_spi_write_enable(struct nand_base *nand)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nfi *nfi = nand->nfi;
++      int status;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->extend_cmds->write_enable);
++
++      nfi->trigger(nfi);
++
++      status = nand_spi_read_status(nand);
++      status &= nand->dev->status->write_protect;
++
++      return !status;
++}
++
++static int nand_spi_program_data(struct nand_base *nand, int row,
++                               int col,
++                               u8 *data, u8 *oob)
++{
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nand_spi *spi = base_to_spi(nand);
++
++      if (spi->tx_mode == SNFI_TX_114 && dev->feature.config.need_qe)
++              nand_spi_set_config(nand, dev->feature.config.addr,
++                                  BIT(0), true);
++
++      nand_spi_set_op_mode(nand, SNFI_CUSTOM_MODE);
++
++      nand->dev->col_cycle  = spi_replace_tx_col_cycle(spi->tx_mode);
++
++      return spi->parent->program_data(nand, row, col, data, oob);
++}
++
++static int nand_spi_program_page(struct nand_base *nand, int row)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_device *dev = nand->dev;
++      struct nfi *nfi = nand->nfi;
++
++      if (spi->op_mode == SNFI_AUTO_MODE)
++              nand_spi_set_op_mode(nand, SNFI_AUTO_MODE);
++      else
++              nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->program_2nd);
++      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
++      nfi->trigger(nfi);
++
++      return nand_spi_wait_ready(nand, READY_TIMEOUT);
++}
++
++static int nand_spi_erase_block(struct nand_base *nand, int row)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nand_base *parent = spi->parent;
++
++      nand_spi_set_op_mode(nand, SNFI_MAC_MODE);
++
++      parent->erase_block(nand, row);
++
++      return nand_spi_wait_ready(nand, READY_TIMEOUT);
++}
++
++static int nand_chip_spi_ctrl(struct nand_chip *chip, int cmd,
++                            void *args)
++{
++      struct nand_base *nand = chip->nand;
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nand_spi *spi = base_to_spi(nand);
++      struct nfi *nfi = nand->nfi;
++      int ret = 0, value = *(int *)args;
++
++      switch (cmd) {
++      case CHIP_CTRL_ONDIE_ECC:
++              spi->ondie_ecc = (bool)value;
++              ret = nand_spi_set_config(nand, dev->feature.config.addr,
++                                        BIT(dev->feature.config.ecc_en_bit),
++                                        spi->ondie_ecc);
++              break;
++
++      case SNFI_CTRL_TX_MODE:
++              if (value < 0 || value > SNFI_TX_114)
++                      return -EOPNOTSUPP;
++
++              if (dev->tx_mode_mask & BIT(value)) {
++                      spi->tx_mode = value;
++                      nand->dev->cmds->random_out_1st = spi_replace_tx_cmds(
++                                                                spi->tx_mode);
++                      ret = nfi->nfi_ctrl(nfi, cmd, args);
++              }
++
++              break;
++
++      case SNFI_CTRL_RX_MODE:
++              if (value < 0 || value > SNFI_RX_144)
++                      return -EOPNOTSUPP;
++
++              if (dev->rx_mode_mask & BIT(value)) {
++                      spi->rx_mode = value;
++                      nand->dev->cmds->program_1st = spi_replace_rx_cmds(
++                                                             spi->rx_mode);
++                      ret = nfi->nfi_ctrl(nfi, cmd, args);
++              }
++
++              break;
++
++      case CHIP_CTRL_OPS_CACHE:
++      case CHIP_CTRL_OPS_MULTI:
++      case CHIP_CTRL_PSLC_MODE:
++      case CHIP_CTRL_DDR_MODE:
++      case CHIP_CTRL_DRIVE_STRENGTH:
++      case CHIP_CTRL_TIMING_MODE:
++              ret = -EOPNOTSUPP;
++              break;
++
++      default:
++              ret = nfi->nfi_ctrl(nfi, cmd, args);
++              break;
++      }
++
++      return ret;
++}
++
++int nand_chip_spi_resume(struct nand_chip *chip)
++{
++      struct nand_base *nand = chip->nand;
++      struct nand_spi *spi = base_to_spi(nand);
++      struct device_spi *dev = device_to_spi(nand->dev);
++      struct nfi *nfi = nand->nfi;
++      struct nfi_format format;
++      u8 mask;
++
++      nand->reset(nand);
++
++      mask = GENMASK(dev->feature.protect.bp_end_bit,
++                     dev->feature.protect.bp_start_bit);
++      nand_spi_set_config(nand, dev->feature.config.addr, mask, false);
++      mask =  BIT(dev->feature.config.ecc_en_bit);
++      nand_spi_set_config(nand, dev->feature.config.addr, mask,
++                          spi->ondie_ecc);
++
++      format.page_size = nand->dev->page_size;
++      format.spare_size = nand->dev->spare_size;
++      format.ecc_req = nand->dev->endurance->ecc_req;
++
++      return nfi->set_format(nfi, &format);
++}
++
++static int nand_spi_set_format(struct nand_base *nand)
++{
++      struct nfi_format format = {
++              nand->dev->page_size,
++              nand->dev->spare_size,
++              nand->dev->endurance->ecc_req
++      };
++
++      return nand->nfi->set_format(nand->nfi, &format);
++}
++
++struct nand_base *nand_device_init(struct nand_chip *chip)
++{
++      struct nand_base *nand;
++      struct nand_spi *spi;
++      struct device_spi *dev;
++      int ret;
++      u8 mask;
++
++      spi = mem_alloc(1, sizeof(struct nand_spi));
++      if (!spi) {
++              pr_info("alloc nand_spi fail\n");
++              return NULL;
++      }
++
++      spi->ondie_ecc = false;
++      spi->op_mode = SNFI_CUSTOM_MODE;
++      spi->rx_mode = SNFI_RX_114;
++      spi->tx_mode = SNFI_TX_114;
++
++      spi->parent = chip->nand;
++      nand = &spi->base;
++      nand->dev = spi->parent->dev;
++      nand->nfi = spi->parent->nfi;
++
++      nand->select_device = nand_spi_select_device;
++      nand->reset = nand_spi_reset;
++      nand->read_id = nand_spi_read_id;
++      nand->read_param_page = nand_spi_read_param_page;
++      nand->set_feature = nand_spi_set_feature;
++      nand->get_feature = nand_spi_get_feature;
++      nand->read_status = nand_spi_read_status;
++      nand->addressing = nand_spi_addressing;
++      nand->read_page = nand_spi_read_page;
++      nand->read_data = nand_spi_read_data;
++      nand->write_enable = nand_spi_write_enable;
++      nand->program_data = nand_spi_program_data;
++      nand->program_page = nand_spi_program_page;
++      nand->erase_block = nand_spi_erase_block;
++
++      chip->chip_ctrl = nand_chip_spi_ctrl;
++      chip->nand_type = NAND_SPI;
++      chip->resume = nand_chip_spi_resume;
++
++      ret = nand_detect_device(nand);
++      if (ret)
++              goto err;
++
++      nand->select_device(nand, 0);
++
++      ret = nand_spi_set_format(nand);
++      if (ret)
++              goto err;
++
++      dev = (struct device_spi *)nand->dev;
++
++      nand->dev->cmds->random_out_1st =
++              spi_replace_rx_cmds(spi->rx_mode);
++      nand->dev->cmds->program_1st =
++              spi_replace_tx_cmds(spi->tx_mode);
++
++      mask = GENMASK(dev->feature.protect.bp_end_bit,
++                     dev->feature.protect.bp_start_bit);
++      ret = nand_spi_set_config(nand, dev->feature.protect.addr, mask, false);
++      if (ret)
++              goto err;
++
++      mask =  BIT(dev->feature.config.ecc_en_bit);
++      ret = nand_spi_set_config(nand, dev->feature.config.addr, mask,
++                                spi->ondie_ecc);
++      if (ret)
++              goto err;
++
++      return nand;
++
++err:
++      mem_free(spi);
++      return NULL;
++}
++
++void nand_exit(struct nand_base *nand)
++{
++      struct nand_spi *spi = base_to_spi(nand);
++
++      nand_base_exit(spi->parent);
++      mem_free(spi);
++}
+diff --git a/drivers/mtd/nandx/core/nand/nand_spi.h b/drivers/mtd/nandx/core/nand/nand_spi.h
+new file mode 100644
+index 0000000000..e55e4de6f7
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand/nand_spi.h
+@@ -0,0 +1,35 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NAND_SPI_H__
++#define __NAND_SPI_H__
++
++/*
++ * spi nand handler
++ * @base: spi nand base functions
++ * @parent: common parent nand base functions
++ * @tx_mode: spi bus width of transfer to device
++ * @rx_mode: spi bus width of transfer from device
++ * @op_mode: spi nand controller (NFI) operation mode
++ * @ondie_ecc: spi nand on-die ecc flag
++ */
++
++struct nand_spi {
++      struct nand_base base;
++      struct nand_base *parent;
++      u8 tx_mode;
++      u8 rx_mode;
++      u8 op_mode;
++      bool ondie_ecc;
++};
++
++static inline struct nand_spi *base_to_spi(struct nand_base *base)
++{
++      return container_of(base, struct nand_spi, base);
++}
++
++#endif /* __NAND_SPI_H__ */
+diff --git a/drivers/mtd/nandx/core/nand_base.c b/drivers/mtd/nandx/core/nand_base.c
+new file mode 100644
+index 0000000000..65998e5460
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_base.c
+@@ -0,0 +1,304 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "nand_chip.h"
++#include "nand_device.h"
++#include "nfi.h"
++#include "nand_base.h"
++
++static int nand_base_select_device(struct nand_base *nand, int cs)
++{
++      struct nfi *nfi = nand->nfi;
++
++      nfi->reset(nfi);
++
++      return nfi->select_chip(nfi, cs);
++}
++
++static int nand_base_reset(struct nand_base *nand)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->reset);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRST);
++}
++
++static int nand_base_read_id(struct nand_base *nand, u8 *id, int count)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_id);
++      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR);
++      nfi->send_addr(nfi, 0, 0, 1, 0);
++
++      return nfi->read_bytes(nfi, id, count);
++}
++
++static int nand_base_read_param_page(struct nand_base *nand, u8 *data,
++                                   int count)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_param_page);
++      nfi->send_addr(nfi, 0, 0, 1, 0);
++
++      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR);
++
++      return nfi->read_bytes(nfi, data, count);
++}
++
++static int nand_base_set_feature(struct nand_base *nand, u8 addr,
++                               u8 *param,
++                               int count)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->set_feature);
++      nfi->send_addr(nfi, addr, 0, 1, 0);
++
++      nfi->write_bytes(nfi, param, count);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tFEAT);
++}
++
++static int nand_base_get_feature(struct nand_base *nand, u8 addr,
++                               u8 *param,
++                               int count)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->get_feature);
++      nfi->send_addr(nfi, addr, 0, 1, 0);
++      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tFEAT);
++
++      return nfi->read_bytes(nfi, param, count);
++}
++
++static int nand_base_read_status(struct nand_base *nand)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++      u8 status = 0;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_status);
++      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tWHR);
++      nfi->read_bytes(nfi, &status, 1);
++
++      return status;
++}
++
++static int nand_base_addressing(struct nand_base *nand, int *row,
++                              int *col)
++{
++      struct nand_device *dev = nand->dev;
++      int lun, plane, block, page, cs = 0;
++      int block_pages, target_blocks, wl = 0;
++      int icol = *col;
++
++      if (dev->target_num > 1) {
++              block_pages = nand_block_pages(dev);
++              target_blocks = nand_target_blocks(dev);
++              cs = div_down(*row, block_pages * target_blocks);
++              *row -= cs * block_pages * target_blocks;
++      }
++
++      nand->select_device(nand, cs);
++
++      block_pages = nand_block_pages(dev);
++      block = div_down(*row, block_pages);
++      page = *row - block * block_pages;
++      plane = reminder(block, dev->plane_num);
++      lun = div_down(block, nand_lun_blocks(dev));
++
++      wl |= (page << dev->addressing->row_bit_start);
++      wl |= (block << dev->addressing->block_bit_start);
++      wl |= (plane << dev->addressing->plane_bit_start);
++      wl |= (lun << dev->addressing->lun_bit_start);
++
++      *row = wl;
++      *col = icol;
++
++      return 0;
++}
++
++static int nand_base_read_page(struct nand_base *nand, int row)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_1st);
++      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
++      nfi->send_cmd(nfi, dev->cmds->read_2nd);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tR);
++}
++
++static int nand_base_read_data(struct nand_base *nand, int row, int col,
++                             int sectors, u8 *data, u8 *oob)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->random_out_1st);
++      nfi->send_addr(nfi, col, row, dev->col_cycle, dev->row_cycle);
++      nfi->send_cmd(nfi, dev->cmds->random_out_2nd);
++      nfi->wait_ready(nfi, NAND_WAIT_POLLING, dev->array_timing->tRCBSY);
++
++      return nfi->read_sectors(nfi, data, oob, sectors);
++}
++
++static int nand_base_write_enable(struct nand_base *nand)
++{
++      struct nand_device *dev = nand->dev;
++      int status;
++
++      status = nand_base_read_status(nand);
++      if (status & dev->status->write_protect)
++              return 0;
++
++      return -ENANDWP;
++}
++
++static int nand_base_program_data(struct nand_base *nand, int row,
++                                int col,
++                                u8 *data, u8 *oob)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->program_1st);
++      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
++
++      return nfi->write_page(nfi, data, oob);
++}
++
++static int nand_base_program_page(struct nand_base *nand, int row)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->program_2nd);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tPROG);
++}
++
++static int nand_base_erase_block(struct nand_base *nand, int row)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->erase_1st);
++      nfi->send_addr(nfi, 0, row, 0, dev->row_cycle);
++      nfi->send_cmd(nfi, dev->cmds->erase_2nd);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tBERS);
++}
++
++static int nand_base_read_cache(struct nand_base *nand, int row)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_1st);
++      nfi->send_addr(nfi, 0, row, dev->col_cycle, dev->row_cycle);
++      nfi->send_cmd(nfi, dev->cmds->read_cache);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tRCBSY);
++}
++
++static int nand_base_read_last(struct nand_base *nand)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->read_cache_last);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tRCBSY);
++}
++
++static int nand_base_program_cache(struct nand_base *nand)
++{
++      struct nfi *nfi = nand->nfi;
++      struct nand_device *dev = nand->dev;
++
++      nfi->reset(nfi);
++      nfi->send_cmd(nfi, dev->cmds->program_cache);
++      nfi->trigger(nfi);
++
++      return nfi->wait_ready(nfi, NAND_WAIT_POLLING,
++                             dev->array_timing->tPCBSY);
++}
++
++struct nand_base *nand_base_init(struct nand_device *dev,
++                               struct nfi *nfi)
++{
++      struct nand_base *nand;
++
++      nand = mem_alloc(1, sizeof(struct nand_base));
++      if (!nand)
++              return NULL;
++
++      nand->dev = dev;
++      nand->nfi = nfi;
++      nand->select_device = nand_base_select_device;
++      nand->reset = nand_base_reset;
++      nand->read_id = nand_base_read_id;
++      nand->read_param_page = nand_base_read_param_page;
++      nand->set_feature = nand_base_set_feature;
++      nand->get_feature = nand_base_get_feature;
++      nand->read_status = nand_base_read_status;
++      nand->addressing = nand_base_addressing;
++      nand->read_page = nand_base_read_page;
++      nand->read_data = nand_base_read_data;
++      nand->read_cache = nand_base_read_cache;
++      nand->read_last = nand_base_read_last;
++      nand->write_enable = nand_base_write_enable;
++      nand->program_data = nand_base_program_data;
++      nand->program_page = nand_base_program_page;
++      nand->program_cache = nand_base_program_cache;
++      nand->erase_block = nand_base_erase_block;
++
++      return nand;
++}
++
++void nand_base_exit(struct nand_base *base)
++{
++      nfi_exit(base->nfi);
++      mem_free(base);
++}
+diff --git a/drivers/mtd/nandx/core/nand_base.h b/drivers/mtd/nandx/core/nand_base.h
+new file mode 100644
+index 0000000000..13217978e5
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_base.h
+@@ -0,0 +1,71 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NAND_BASE_H__
++#define __NAND_BASE_H__
++
++/*
++ * nand base functions
++ * @dev: nand device infomations
++ * @nfi: nand host controller
++ * @select_device: select one nand device of multi nand on chip
++ * @reset: reset current nand device
++ * @read_id: read current nand id
++ * @read_param_page: read current nand parameters page
++ * @set_feature: configurate the nand device feature
++ * @get_feature: get the nand device feature
++ * @read_status: read nand device status
++ * @addressing: addressing the address to nand device physical address
++ * @read_page: read page data to device cache register
++ * @read_data: read data from device cache register by bus protocol
++ * @read_cache: nand cache read operation for data output
++ * @read_last: nand cache read operation for last page output
++ * @write_enable: enable program/erase for nand, especially spi nand
++ * @program_data: program data to nand device cache register
++ * @program_page: program page data from nand device cache register to array
++ * @program_cache: nand cache program operation for data input
++ * @erase_block: erase nand block operation
++ */
++struct nand_base {
++      struct nand_device *dev;
++      struct nfi *nfi;
++      int (*select_device)(struct nand_base *nand, int cs);
++      int (*reset)(struct nand_base *nand);
++      int (*read_id)(struct nand_base *nand, u8 *id, int count);
++      int (*read_param_page)(struct nand_base *nand, u8 *data, int count);
++      int (*set_feature)(struct nand_base *nand, u8 addr, u8 *param,
++                         int count);
++      int (*get_feature)(struct nand_base *nand, u8 addr, u8 *param,
++                         int count);
++      int (*read_status)(struct nand_base *nand);
++      int (*addressing)(struct nand_base *nand, int *row, int *col);
++
++      int (*read_page)(struct nand_base *nand, int row);
++      int (*read_data)(struct nand_base *nand, int row, int col, int sectors,
++                       u8 *data, u8 *oob);
++      int (*read_cache)(struct nand_base *nand, int row);
++      int (*read_last)(struct nand_base *nand);
++
++      int (*write_enable)(struct nand_base *nand);
++      int (*program_data)(struct nand_base *nand, int row, int col, u8 *data,
++                          u8 *oob);
++      int (*program_page)(struct nand_base *nand, int row);
++      int (*program_cache)(struct nand_base *nand);
++
++      int (*erase_block)(struct nand_base *nand, int row);
++};
++
++struct nand_base *nand_base_init(struct nand_device *device,
++                               struct nfi *nfi);
++void nand_base_exit(struct nand_base *base);
++
++struct nand_base *nand_device_init(struct nand_chip *nand);
++void nand_exit(struct nand_base *nand);
++
++int nand_detect_device(struct nand_base *nand);
++
++#endif /* __NAND_BASE_H__ */
+diff --git a/drivers/mtd/nandx/core/nand_chip.c b/drivers/mtd/nandx/core/nand_chip.c
+new file mode 100644
+index 0000000000..02adc6f52e
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_chip.c
+@@ -0,0 +1,272 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "nand_chip.h"
++#include "nand_device.h"
++#include "nfi.h"
++#include "nand_base.h"
++
++static int nand_chip_read_page(struct nand_chip *chip,
++                             struct nand_ops *ops,
++                             int count)
++{
++      struct nand_base *nand = chip->nand;
++      struct nand_device *dev = nand->dev;
++      int i, ret = 0;
++      int row, col, sectors;
++      u8 *data, *oob;
++
++      for (i = 0; i < count; i++) {
++              row = ops[i].row;
++              col = ops[i].col;
++
++              nand->addressing(nand, &row, &col);
++              ops[i].status = nand->read_page(nand, row);
++              if (ops[i].status < 0) {
++                      ret = ops[i].status;
++                      continue;
++              }
++
++              data = ops[i].data;
++              oob = ops[i].oob;
++              sectors = ops[i].len / chip->sector_size;
++              ops[i].status = nand->read_data(nand, row, col,
++                                              sectors, data, oob);
++              if (ops[i].status > 0)
++                      ops[i].status = ops[i].status >=
++                                      dev->endurance->max_bitflips ?
++                                      -ENANDFLIPS : 0;
++
++              ret = min_t(int, ret, ops[i].status);
++      }
++
++      return ret;
++}
++
++static int nand_chip_write_page(struct nand_chip *chip,
++                              struct nand_ops *ops,
++                              int count)
++{
++      struct nand_base *nand = chip->nand;
++      struct nand_device *dev = nand->dev;
++      int i, ret = 0;
++      int row, col;
++      u8 *data, *oob;
++
++      for (i = 0; i < count; i++) {
++              row = ops[i].row;
++              col = ops[i].col;
++
++              nand->addressing(nand, &row, &col);
++
++              ops[i].status = nand->write_enable(nand);
++              if (ops[i].status) {
++                      pr_debug("Write Protect at %x!\n", row);
++                      ops[i].status = -ENANDWP;
++                      return -ENANDWP;
++              }
++
++              data = ops[i].data;
++              oob = ops[i].oob;
++              ops[i].status = nand->program_data(nand, row, col, data, oob);
++              if (ops[i].status < 0) {
++                      ret = ops[i].status;
++                      continue;
++              }
++
++              ops[i].status = nand->program_page(nand, row);
++              if (ops[i].status < 0) {
++                      ret = ops[i].status;
++                      continue;
++              }
++
++              ops[i].status = nand->read_status(nand);
++              if (ops[i].status & dev->status->program_fail)
++                      ops[i].status = -ENANDWRITE;
++
++              ret = min_t(int, ret, ops[i].status);
++      }
++
++      return ret;
++}
++
++static int nand_chip_erase_block(struct nand_chip *chip,
++                               struct nand_ops *ops,
++                               int count)
++{
++      struct nand_base *nand = chip->nand;
++      struct nand_device *dev = nand->dev;
++      int i, ret = 0;
++      int row, col;
++
++      for (i = 0; i < count; i++) {
++              row = ops[i].row;
++              col = ops[i].col;
++
++              nand->addressing(nand, &row, &col);
++
++              ops[i].status = nand->write_enable(nand);
++              if (ops[i].status) {
++                      pr_debug("Write Protect at %x!\n", row);
++                      ops[i].status = -ENANDWP;
++                      return -ENANDWP;
++              }
++
++              ops[i].status = nand->erase_block(nand, row);
++              if (ops[i].status < 0) {
++                      ret = ops[i].status;
++                      continue;
++              }
++
++              ops[i].status = nand->read_status(nand);
++              if (ops[i].status & dev->status->erase_fail)
++                      ops[i].status = -ENANDERASE;
++
++              ret = min_t(int, ret, ops[i].status);
++      }
++
++      return ret;
++}
++
++/* read first bad mark on spare */
++static int nand_chip_is_bad_block(struct nand_chip *chip,
++                                struct nand_ops *ops,
++                                int count)
++{
++      int i, ret, value;
++      int status = 0;
++      u8 *data, *tmp_buf;
++
++      tmp_buf = mem_alloc(1, chip->page_size);
++      if (!tmp_buf)
++              return -ENOMEM;
++
++      memset(tmp_buf, 0x00, chip->page_size);
++
++      /* Disable ECC */
++      value = 0;
++      ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value);
++      if (ret)
++              goto out;
++
++      ret = chip->read_page(chip, ops, count);
++      if (ret)
++              goto out;
++
++      for (i = 0; i < count; i++) {
++              data = ops[i].data;
++
++              /* temp solution for mt7622, because of no bad mark swap */
++              if (!memcmp(data, tmp_buf, chip->page_size)) {
++                      ops[i].status = -ENANDBAD;
++                      status = -ENANDBAD;
++                      
++              } else {
++                      ops[i].status = 0;
++              }
++      }
++
++      /* Enable ECC */
++      value = 1;
++      ret = chip->chip_ctrl(chip, NFI_CTRL_ECC, &value);
++      if (ret)
++              goto out;
++
++      mem_free(tmp_buf);
++      return status;
++
++out:
++      mem_free(tmp_buf);
++      return ret;
++}
++
++static int nand_chip_ctrl(struct nand_chip *chip, int cmd, void *args)
++{
++      return -EOPNOTSUPP;
++}
++
++static int nand_chip_suspend(struct nand_chip *chip)
++{
++      return 0;
++}
++
++static int nand_chip_resume(struct nand_chip *chip)
++{
++      return 0;
++}
++
++struct nand_chip *nand_chip_init(struct nfi_resource *res)
++{
++      struct nand_chip *chip;
++      struct nand_base *nand;
++      struct nfi *nfi;
++
++      chip = mem_alloc(1, sizeof(struct nand_chip));
++      if (!chip) {
++              pr_info("nand chip alloc fail!\n");
++              return NULL;
++      }
++
++      nfi = nfi_init(res);
++      if (!nfi) {
++              pr_info("nfi init fail!\n");
++              goto nfi_err;
++      }
++
++      nand = nand_base_init(NULL, nfi);
++      if (!nand) {
++              pr_info("nand base init fail!\n");
++              goto base_err;
++      }
++
++      chip->nand = (void *)nand;
++      chip->read_page = nand_chip_read_page;
++      chip->write_page = nand_chip_write_page;
++      chip->erase_block = nand_chip_erase_block;
++      chip->is_bad_block = nand_chip_is_bad_block;
++      chip->chip_ctrl = nand_chip_ctrl;
++      chip->suspend = nand_chip_suspend;
++      chip->resume = nand_chip_resume;
++
++      nand = nand_device_init(chip);
++      if (!nand)
++              goto nand_err;
++
++      chip->nand = (void *)nand;
++      chip->plane_num = nand->dev->plane_num;
++      chip->block_num = nand_total_blocks(nand->dev);
++      chip->block_size = nand->dev->block_size;
++      chip->block_pages = nand_block_pages(nand->dev);
++      chip->page_size = nand->dev->page_size;
++      chip->oob_size = nfi->fdm_size * div_down(chip->page_size,
++                                                nfi->sector_size);
++      chip->sector_size = nfi->sector_size;
++      chip->sector_spare_size = nfi->sector_spare_size;
++      chip->min_program_pages = nand->dev->min_program_pages;
++      chip->ecc_strength = nfi->ecc_strength;
++      chip->ecc_parity_size = nfi->ecc_parity_size;
++      chip->fdm_ecc_size = nfi->fdm_ecc_size;
++      chip->fdm_reg_size = nfi->fdm_size;
++
++      return chip;
++
++nand_err:
++      mem_free(nand);
++base_err:
++      nfi_exit(nfi);
++nfi_err:
++      mem_free(chip);
++      return NULL;
++}
++
++void nand_chip_exit(struct nand_chip *chip)
++{
++      nand_exit(chip->nand);
++      mem_free(chip);
++}
+diff --git a/drivers/mtd/nandx/core/nand_chip.h b/drivers/mtd/nandx/core/nand_chip.h
+new file mode 100644
+index 0000000000..3e9c8e6ca3
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_chip.h
+@@ -0,0 +1,103 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NAND_CHIP_H__
++#define __NAND_CHIP_H__
++
++enum nand_type {
++      NAND_SPI,
++      NAND_SLC,
++      NAND_MLC,
++      NAND_TLC
++};
++
++/*
++ * nand chip operation unit
++ *    one nand_ops indicates one row operation
++ * @row: nand chip row address, like as nand row
++ * @col: nand chip column address, like as nand column
++ * @len: operate data length, min is sector_size,
++ *    max is page_size and sector_size aligned
++ * @status: one operation result status
++ * @data: data buffer for operation
++ * @oob: oob buffer for operation, like as nand spare area
++ */
++struct nand_ops {
++      int row;
++      int col;
++      int len;
++      int status;
++      void *data;
++      void *oob;
++};
++
++/*
++ * nand chip descriptions
++ *    nand chip includes nand controller and the several same nand devices
++ * @nand_type: the nand type on this chip,
++ *    the chip maybe have several nand device and the type must be same
++ * @plane_num: the whole plane number on the chip
++ * @block_num: the whole block number on the chip
++ * @block_size: nand device block size
++ * @block_pages: nand device block has page number
++ * @page_size: nand device page size
++ * @oob_size: chip out of band size, like as nand spare szie,
++ *    but restricts this:
++ *    the size is provied by nand controller(NFI),
++ *    because NFI would use some nand spare size
++ * @min_program_pages: chip needs min pages per program operations
++ *    one page as one nand_ops
++ * @sector_size: chip min read size
++ * @sector_spare_size: spare size for sector, is spare_size/page_sectors
++ * @ecc_strength: ecc stregth per sector_size, it would be for calculated ecc
++ * @ecc_parity_size: ecc parity size for one  sector_size data
++ * @nand: pointer to inherited struct nand_base
++ * @read_page: read %count pages on chip
++ * @write_page: write %count pages on chip
++ * @erase_block: erase %count blocks on chip, one block is one nand_ops
++ *    it is better to set nand_ops.row to block start row
++ * @is_bad_block: judge the %count blocks on chip if they are bad
++ *    by vendor specification
++ * @chip_ctrl: control the chip features by nandx_ctrl_cmd
++ * @suspend: suspend nand chip
++ * @resume: resume nand chip
++ */
++struct nand_chip {
++      int nand_type;
++      int plane_num;
++      int block_num;
++      int block_size;
++      int block_pages;
++      int page_size;
++      int oob_size;
++
++      int min_program_pages;
++      int sector_size;
++      int sector_spare_size;
++      int ecc_strength;
++      int ecc_parity_size;
++      u32 fdm_ecc_size;
++      u32 fdm_reg_size;
++
++      void *nand;
++
++      int (*read_page)(struct nand_chip *chip, struct nand_ops *ops,
++                       int count);
++      int (*write_page)(struct nand_chip *chip, struct nand_ops *ops,
++                        int count);
++      int (*erase_block)(struct nand_chip *chip, struct nand_ops *ops,
++                         int count);
++      int (*is_bad_block)(struct nand_chip *chip, struct nand_ops *ops,
++                          int count);
++      int (*chip_ctrl)(struct nand_chip *chip, int cmd, void *args);
++      int (*suspend)(struct nand_chip *chip);
++      int (*resume)(struct nand_chip *chip);
++};
++
++struct nand_chip *nand_chip_init(struct nfi_resource *res);
++void nand_chip_exit(struct nand_chip *chip);
++#endif /* __NAND_CHIP_H__ */
+diff --git a/drivers/mtd/nandx/core/nand_device.c b/drivers/mtd/nandx/core/nand_device.c
+new file mode 100644
+index 0000000000..9f6764d1bc
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_device.c
+@@ -0,0 +1,285 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "nand_chip.h"
++#include "nand_device.h"
++#include "nand_base.h"
++
++#define MAX_CHIP_DEVICE 4
++#define PARAM_PAGE_LEN  2048
++#define ONFI_CRC_BASE   0x4f4e
++
++static u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len)
++{
++      int i;
++
++      while (len--) {
++              crc ^= *p++ << 8;
++
++              for (i = 0; i < 8; i++)
++                      crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
++      }
++
++      return crc;
++}
++
++static inline void decode_addr_cycle(u8 addr_cycle, u8 *row_cycle,
++                                   u8 *col_cycle)
++{
++      *row_cycle = addr_cycle & 0xf;
++      *col_cycle = (addr_cycle >> 4) & 0xf;
++}
++
++static int detect_onfi(struct nand_device *dev,
++                     struct nand_onfi_params *onfi)
++{
++      struct nand_endurance *endurance = dev->endurance;
++      u16 size, i, crc16;
++      u8 *id;
++
++      size = sizeof(struct nand_onfi_params) - sizeof(u16);
++
++      for (i = 0; i < 3; i++) {
++              crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&onfi[i], size);
++
++              if (onfi[i].signature[0] == 'O' &&
++                  onfi[i].signature[1] == 'N' &&
++                  onfi[i].signature[2] == 'F' &&
++                  onfi[i].signature[3] == 'I' &&
++                  onfi[i].crc16 == crc16)
++                      break;
++
++              /* in some spi nand, onfi signature maybe "NAND" */
++              if (onfi[i].signature[0] == 'N' &&
++                  onfi[i].signature[1] == 'A' &&
++                  onfi[i].signature[2] == 'N' &&
++                  onfi[i].signature[3] == 'D' &&
++                  onfi[i].crc16 == crc16)
++                      break;
++      }
++
++      if (i == 3)
++              return -ENODEV;
++
++      memcpy(dev->name, onfi[i].model, 20);
++      id = onfi[i].manufacturer;
++      dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6],
++                             id[7]);
++      dev->id_len = MAX_ID_NUM;
++      dev->io_width = (onfi[i].features & 1) ? NAND_IO16 : NAND_IO8;
++      decode_addr_cycle(onfi[i].addr_cycle, &dev->row_cycle,
++                        &dev->col_cycle);
++      dev->target_num = 1;
++      dev->lun_num = onfi[i].lun_num;
++      dev->plane_num = BIT(onfi[i].plane_address_bits);
++      dev->block_num = onfi[i].lun_blocks / dev->plane_num;
++      dev->block_size = onfi[i].block_pages * onfi[i].page_size;
++      dev->page_size = onfi[i].page_size;
++      dev->spare_size = onfi[i].spare_size;
++
++      endurance->ecc_req = onfi[i].ecc_req;
++      endurance->pe_cycle = onfi[i].valid_block_endurance;
++      endurance->max_bitflips = endurance->ecc_req >> 1;
++
++      return 0;
++}
++
++static int detect_jedec(struct nand_device *dev,
++                      struct nand_jedec_params *jedec)
++{
++      struct nand_endurance *endurance = dev->endurance;
++      u16 size, i, crc16;
++      u8 *id;
++
++      size = sizeof(struct nand_jedec_params) - sizeof(u16);
++
++      for (i = 0; i < 3; i++) {
++              crc16 = nand_onfi_crc16(ONFI_CRC_BASE, (u8 *)&jedec[i], size);
++
++              if (jedec[i].signature[0] == 'J' &&
++                  jedec[i].signature[1] == 'E' &&
++                  jedec[i].signature[2] == 'S' &&
++                  jedec[i].signature[3] == 'D' &&
++                  jedec[i].crc16 == crc16)
++                      break;
++      }
++
++      if (i == 3)
++              return -ENODEV;
++
++      memcpy(dev->name, jedec[i].model, 20);
++      id = jedec[i].manufacturer;
++      dev->id = NAND_PACK_ID(id[0], id[1], id[2], id[3], id[4], id[5], id[6],
++                             id[7]);
++      dev->id_len = MAX_ID_NUM;
++      dev->io_width = (jedec[i].features & 1) ? NAND_IO16 : NAND_IO8;
++      decode_addr_cycle(jedec[i].addr_cycle, &dev->row_cycle,
++                        &dev->col_cycle);
++      dev->target_num = 1;
++      dev->lun_num = jedec[i].lun_num;
++      dev->plane_num = BIT(jedec[i].plane_address_bits);
++      dev->block_num = jedec[i].lun_blocks / dev->plane_num;
++      dev->block_size = jedec[i].block_pages * jedec[i].page_size;
++      dev->page_size = jedec[i].page_size;
++      dev->spare_size = jedec[i].spare_size;
++
++      endurance->ecc_req = jedec[i].endurance_block0[0];
++      endurance->pe_cycle = jedec[i].valid_block_endurance;
++      endurance->max_bitflips = endurance->ecc_req >> 1;
++
++      return 0;
++}
++
++static struct nand_device *detect_parameters_page(struct nand_base
++                                                *nand)
++{
++      struct nand_device *dev = nand->dev;
++      void *params;
++      int ret;
++
++      params = mem_alloc(1, PARAM_PAGE_LEN);
++      if (!params)
++              return NULL;
++
++      memset(params, 0, PARAM_PAGE_LEN);
++      ret = nand->read_param_page(nand, params, PARAM_PAGE_LEN);
++      if (ret < 0) {
++              pr_info("read parameters page fail!\n");
++              goto error;
++      }
++
++      ret = detect_onfi(dev, params);
++      if (ret) {
++              pr_info("detect onfi device fail! try to detect jedec\n");
++              ret = detect_jedec(dev, params);
++              if (ret) {
++                      pr_info("detect jedec device fail!\n");
++                      goto error;
++              }
++      }
++
++      mem_free(params);
++      return dev;
++
++error:
++      mem_free(params);
++      return NULL;
++}
++
++static int read_device_id(struct nand_base *nand, int cs, u8 *id)
++{
++      int i;
++
++      nand->select_device(nand, cs);
++      nand->reset(nand);
++      nand->read_id(nand, id, MAX_ID_NUM);
++      pr_info("device %d ID: ", cs);
++
++      for (i = 0; i < MAX_ID_NUM; i++)
++              pr_info("%x ", id[i]);
++
++      pr_info("\n");
++
++      return 0;
++}
++
++static int detect_more_device(struct nand_base *nand, u8 *id)
++{
++      u8 id_ext[MAX_ID_NUM];
++      int i, j, target_num = 0;
++
++      for (i = 1; i < MAX_CHIP_DEVICE; i++) {
++              memset(id_ext, 0xff, MAX_ID_NUM);
++              read_device_id(nand, i, id_ext);
++
++              for (j = 0; j < MAX_ID_NUM; j++) {
++                      if (id_ext[j] != id[j])
++                              goto out;
++              }
++
++              target_num += 1;
++      }
++
++out:
++      return target_num;
++}
++
++static struct nand_device *scan_device_table(const u8 *id, int id_len)
++{
++      struct nand_device *dev;
++      int i = 0, j;
++      u8 ids[MAX_ID_NUM] = {0};
++
++      while (1) {
++              dev = nand_get_device(i);
++
++              if (!strcmp(dev->name, "NO-DEVICE"))
++                      break;
++
++              if (id_len < dev->id_len) {
++                      i += 1;
++                      continue;
++              }
++
++              NAND_UNPACK_ID(dev->id, ids, MAX_ID_NUM);
++              for (j = 0; j < dev->id_len; j++) {
++                      if (ids[j] != id[j])
++                              break;
++              }
++
++              if (j == dev->id_len)
++                      break;
++
++              i += 1;
++      }
++
++      return dev;
++}
++
++int nand_detect_device(struct nand_base *nand)
++{
++      struct nand_device *dev;
++      u8 id[MAX_ID_NUM] = { 0 };
++      int target_num = 0;
++
++      /* Get nand device default setting for reset/read_id */
++      nand->dev = scan_device_table(NULL, -1);
++
++      read_device_id(nand, 0, id);
++      dev = scan_device_table(id, MAX_ID_NUM);
++
++      if (!strcmp(dev->name, "NO-DEVICE")) {
++              pr_info("device scan fail\n");
++              return -ENODEV;
++      }
++
++      /* TobeFix: has null pointer issue in this funciton */
++      if (!strcmp(dev->name, "NO-DEVICE")) {
++              pr_info("device scan fail, detect parameters page\n");
++              dev = detect_parameters_page(nand);
++              if (!dev) {
++                      pr_info("detect parameters fail\n");
++                      return -ENODEV;
++              }
++      }
++
++      if (dev->target_num > 1)
++              target_num = detect_more_device(nand, id);
++
++      target_num += 1;
++      pr_debug("chip has target device num: %d\n", target_num);
++
++      if (dev->target_num != target_num)
++              dev->target_num = target_num;
++
++      nand->dev = dev;
++
++      return 0;
++}
++
+diff --git a/drivers/mtd/nandx/core/nand_device.h b/drivers/mtd/nandx/core/nand_device.h
+new file mode 100644
+index 0000000000..e142cf529d
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nand_device.h
+@@ -0,0 +1,608 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NAND_DEVICE_H__
++#define __NAND_DEVICE_H__
++
++/* onfi 3.2 */
++struct nand_onfi_params {
++      /* Revision information and features block. 0 */
++      /*
++       * Byte 0: 4Fh,
++       * Byte 1: 4Eh,
++       * Byte 2: 46h,
++       * Byte 3: 49h,
++       */
++      u8 signature[4];
++      /*
++       * 9-15 Reserved (0)
++       * 8 1 = supports ONFI version 3.2
++       * 7 1 = supports ONFI version 3.1
++       * 6 1 = supports ONFI version 3.0
++       * 5 1 = supports ONFI version 2.3
++       * 4 1 = supports ONFI version 2.2
++       * 3 1 = supports ONFI version 2.1
++       * 2 1 = supports ONFI version 2.0
++       * 1 1 = supports ONFI version 1.0
++       * 0 Reserved (0)
++       */
++      u16 revision;
++      /*
++       * 13-15 Reserved (0)
++       * 12 1 = supports external Vpp
++       * 11 1 = supports Volume addressing
++       * 10 1 = supports NV-DDR2
++       * 9 1 = supports EZ NAND
++       * 8 1 = supports program page register clear enhancement
++       * 7 1 = supports extended parameter page
++       * 6 1 = supports multi-plane read operations
++       * 5 1 = supports NV-DDR
++       * 4 1 = supports odd to even page Copyback
++       * 3 1 = supports multi-plane program and erase operations
++       * 2 1 = supports non-sequential page programming
++       * 1 1 = supports multiple LUN operations
++       * 0 1 = supports 16-bit data bus width
++       */
++      u16 features;
++      /*
++       * 13-15 Reserved (0)
++       * 12 1 = supports LUN Get and LUN Set Features
++       * 11 1 = supports ODT Configure
++       * 10 1 = supports Volume Select
++       * 9 1 = supports Reset LUN
++       * 8 1 = supports Small Data Move
++       * 7 1 = supports Change Row Address
++       * 6 1 = supports Change Read Column Enhanced
++       * 5 1 = supports Read Unique ID
++       * 4 1 = supports Copyback
++       * 3 1 = supports Read Status Enhanced
++       * 2 1 = supports Get Features and Set Features
++       * 1 1 = supports Read Cache commands
++       * 0 1 = supports Page Cache Program command
++       */
++      u16 opt_cmds;
++      /*
++       * 4-7 Reserved (0)
++       * 3 1 = supports Multi-plane Block Erase
++       * 2 1 = supports Multi-plane Copyback Program
++       * 1 1 = supports Multi-plane Page Program
++       * 0 1 = supports Random Data Out
++       */
++      u8 advance_cmds;
++      u8 reserved0[1];
++      u16 extend_param_len;
++      u8 param_page_num;
++      u8 reserved1[17];
++
++      /* Manufacturer information block. 32 */
++      u8 manufacturer[12];
++      u8 model[20];
++      u8 jedec_id;
++      u16 data_code;
++      u8 reserved2[13];
++
++      /* Memory organization block. 80 */
++      u32 page_size;
++      u16 spare_size;
++      u32 partial_page_size; /* obsolete */
++      u16 partial_spare_size; /* obsolete */
++      u32 block_pages;
++      u32 lun_blocks;
++      u8 lun_num;
++      /*
++       * 4-7 Column address cycles
++       * 0-3 Row address cycles
++       */
++      u8 addr_cycle;
++      u8 cell_bits;
++      u16 lun_max_bad_blocks;
++      u16 block_endurance;
++      u8 target_begin_valid_blocks;
++      u16 valid_block_endurance;
++      u8 page_program_num;
++      u8 partial_program_attr; /* obsolete */
++      u8 ecc_req;
++      /*
++       * 4-7 Reserved (0)
++       * 0-3 Number of plane address bits
++       */
++      u8 plane_address_bits;
++      /*
++       * 6-7 Reserved (0)
++       * 5 1 = lower bit XNOR block address restriction
++       * 4 1 = read cache supported
++       * 3 Address restrictions for cache operations
++       * 2 1 = program cache supported
++       * 1 1 = no block address restrictions
++       * 0 Overlapped / concurrent multi-plane support
++       */
++      u8 multi_plane_attr;
++      u8 ez_nand_support;
++      u8 reserved3[12];
++
++      /* Electrical parameters block. 128 */
++      u8 io_pin_max_capacitance;
++      /*
++       * 6-15 Reserved (0)
++       * 5 1 = supports timing mode 5
++       * 4 1 = supports timing mode 4
++       * 3 1 = supports timing mode 3
++       * 2 1 = supports timing mode 2
++       * 1 1 = supports timing mode 1
++       * 0 1 = supports timing mode 0, shall be 1
++       */
++      u16 sdr_timing_mode;
++      u16 sdr_program_cache_timing_mode; /* obsolete */
++      u16 tPROG;
++      u16 tBERS;
++      u16 tR;
++      u16 tCCS;
++      /*
++       * 7 Reserved (0)
++       * 6 1 = supports NV-DDR2 timing mode 8
++       * 5 1 = supports NV-DDR timing mode 5
++       * 4 1 = supports NV-DDR timing mode 4
++       * 3 1 = supports NV-DDR timing mode 3
++       * 2 1 = supports NV-DDR timing mode 2
++       * 1 1 = supports NV-DDR timing mode 1
++       * 0 1 = supports NV-DDR timing mode 0
++       */
++      u8 nvddr_timing_mode;
++      /*
++       * 7 1 = supports timing mode 7
++       * 6 1 = supports timing mode 6
++       * 5 1 = supports timing mode 5
++       * 4 1 = supports timing mode 4
++       * 3 1 = supports timing mode 3
++       * 2 1 = supports timing mode 2
++       * 1 1 = supports timing mode 1
++       * 0 1 = supports timing mode 0
++       */
++      u8 nvddr2_timing_mode;
++      /*
++       * 4-7 Reserved (0)
++       * 3 1 = device requires Vpp enablement sequence
++       * 2 1 = device supports CLK stopped for data input
++       * 1 1 = typical capacitance
++       * 0 tCAD value to use
++       */
++      u8 nvddr_fetures;
++      u16 clk_pin_capacitance;
++      u16 io_pin_capacitance;
++      u16 input_pin_capacitance;
++      u8 input_pin_max_capacitance;
++      /*
++       * 3-7 Reserved (0)
++       * 2 1 = supports 18 Ohm drive strength
++       * 1 1 = supports 25 Ohm drive strength
++       * 0 1 = supports driver strength settings
++       */
++      u8 drive_strength;
++      u16 tR_multi_plane;
++      u16 tADL;
++      u16 tR_ez_nand;
++      /*
++       * 6-7 Reserved (0)
++       * 5 1 = external VREFQ required for >= 200 MT/s
++       * 4 1 = supports differential signaling for DQS
++       * 3 1 = supports differential signaling for RE_n
++       * 2 1 = supports ODT value of 30 Ohms
++       * 1 1 = supports matrix termination ODT
++       * 0 1 = supports self-termination ODT
++       */
++      u8 nvddr2_features;
++      u8 nvddr2_warmup_cycles;
++      u8 reserved4[4];
++
++      /* vendor block. 164 */
++      u16 vendor_revision;
++      u8      vendor_spec[88];
++
++      /* CRC for Parameter Page. 254 */
++      u16 crc16;
++} __packed;
++
++/* JESD230-B */
++struct nand_jedec_params {
++      /* Revision information and features block. 0 */
++      /*
++       * Byte 0:4Ah
++       * Byte 1:45h
++       * Byte 2:53h
++       * Byte 3:44h
++       */
++      u8 signature[4];
++      /*
++       * 3-15: Reserved (0)
++       * 2: 1 = supports parameter page revision 1.0 and standard revision 1.0
++       * 1: 1 = supports vendor specific parameter page
++       * 0: Reserved (0)
++       */
++      u16 revision;
++      /*
++       * 9-15 Reserved (0)
++       * 8: 1 = supports program page register clear enhancement
++       * 7: 1 = supports external Vpp
++       * 6: 1 = supports Toggle Mode DDR
++       * 5: 1 = supports Synchronous DDR
++       * 4: 1 = supports multi-plane read operations
++       * 3: 1 = supports multi-plane program and erase operations
++       * 2: 1 = supports non-sequential page programming
++       * 1: 1 = supports multiple LUN operations
++       * 0: 1 = supports 16-bit data bus width
++       */
++      u16 features;
++      /*
++       * 11-23: Reserved (0)
++       * 10: 1 = supports Synchronous Reset
++       * 9: 1 = supports Reset LUN (Primary)
++       * 8: 1 = supports Small Data Move
++       * 7: 1 = supports Multi-plane Copyback Program (Primary)
++       * 6: 1 = supports Random Data Out (Primary)
++       * 5: 1 = supports Read Unique ID
++       * 4: 1 = supports Copyback
++       * 3: 1 = supports Read Status Enhanced (Primary)
++       * 2: 1 = supports Get Features and Set Features
++       * 1: 1 = supports Read Cache commands
++       * 0: 1 = supports Page Cache Program command
++       */
++      u8 opt_cmds[3];
++      /*
++       * 8-15: Reserved (0)
++       * 7: 1 = supports secondary Read Status Enhanced
++       * 6: 1 = supports secondary Multi-plane Block Erase
++       * 5: 1 = supports secondary Multi-plane Copyback Program
++       * 4: 1 = supports secondary Multi-plane Program
++       * 3: 1 = supports secondary Random Data Out
++       * 2: 1 = supports secondary Multi-plane Copyback Read
++       * 1: 1 = supports secondary Multi-plane Read Cache Random
++       * 0: 1 = supports secondary Multi-plane Read
++       */
++      u16 secondary_cmds;
++      u8 param_page_num;
++      u8 reserved0[18];
++
++      /* Manufacturer information block. 32*/
++      u8 manufacturer[12];
++      u8 model[20];
++      u8 jedec_id[6];
++      u8 reserved1[10];
++
++      /* Memory organization block. 80 */
++      u32 page_size;
++      u16 spare_size;
++      u8 reserved2[6];
++      u32 block_pages;
++      u32 lun_blocks;
++      u8 lun_num;
++      /*
++       * 4-7 Column address cycles
++       * 0-3 Row address cycles
++       */
++      u8 addr_cycle;
++      u8 cell_bits;
++      u8 page_program_num;
++      /*
++       * 4-7 Reserved (0)
++       * 0-3 Number of plane address bits
++       */
++      u8 plane_address_bits;
++      /*
++       * 3-7: Reserved (0)
++       * 2: 1= read cache supported
++       * 1: 1 = program cache supported
++       * 0: 1= No multi-plane block address restrictions
++       */
++      u8 multi_plane_attr;
++      u8 reserved3[38];
++
++      /* Electrical parameters block. 144 */
++      /*
++       * 6-15: Reserved (0)
++       * 5: 1 = supports 20 ns speed grade (50 MHz)
++       * 4: 1 = supports 25 ns speed grade (40 MHz)
++       * 3: 1 = supports 30 ns speed grade (~33 MHz)
++       * 2: 1 = supports 35 ns speed grade (~28 MHz)
++       * 1: 1 = supports 50 ns speed grade (20 MHz)
++       * 0: 1 = supports 100 ns speed grade (10 MHz)
++       */
++      u16 sdr_speed;
++      /*
++       * 8-15: Reserved (0)
++       * 7: 1 = supports 5 ns speed grade (200 MHz)
++       * 6: 1 = supports 6 ns speed grade (~166 MHz)
++       * 5: 1 = supports 7.5 ns speed grade (~133 MHz)
++       * 4: 1 = supports 10 ns speed grade (100 MHz)
++       * 3: 1 = supports 12 ns speed grade (~83 MHz)
++       * 2: 1 = supports 15 ns speed grade (~66 MHz)
++       * 1: 1 = supports 25 ns speed grade (40 MHz)
++       * 0: 1 = supports 30 ns speed grade (~33 MHz)
++       */
++      u16 toggle_ddr_speed;
++      /*
++       * 6-15: Reserved (0)
++       * 5: 1 = supports 10 ns speed grade (100 MHz)
++       * 4: 1 = supports 12 ns speed grade (~83 MHz)
++       * 3: 1 = supports 15 ns speed grade (~66 MHz)
++       * 2: 1 = supports 20 ns speed grade (50 MHz)
++       * 1: 1 = supports 30 ns speed grade (~33 MHz)
++       * 0: 1 = supports 50 ns speed grade (20 MHz)
++       */
++      u16 sync_ddr_speed;
++      u8 sdr_features;
++      u8 toggle_ddr_features;
++      /*
++       * 2-7: Reserved (0)
++       * 1: Device supports CK stopped for data input
++       * 0: tCAD value to use
++       */
++      u8 sync_ddr_features;
++      u16 tPROG;
++      u16 tBERS;
++      u16 tR;
++      u16 tR_multi_plane;
++      u16 tCCS;
++      u16 io_pin_capacitance;
++      u16 input_pin_capacitance;
++      u16 ck_pin_capacitance;
++      /*
++       * 3-7: Reserved (0)
++       * 2: 1 = supports 18 ohm drive strength
++       * 1: 1 = supports 25 ohm drive strength
++       * 0: 1 = supports 35ohm/50ohm drive strength
++       */
++      u8 drive_strength;
++      u16 tADL;
++      u8 reserved4[36];
++
++      /* ECC and endurance block. 208 */
++      u8 target_begin_valid_blocks;
++      u16 valid_block_endurance;
++      /*
++       * Byte 0: Number of bits ECC correctability
++       * Byte 1: Codeword size
++       * Byte 2-3: Bad blocks maximum per LUN
++       * Byte 4-5: Block endurance
++       * Byte 6-7: Reserved (0)
++       */
++      u8 endurance_block0[8];
++      u8 endurance_block1[8];
++      u8 endurance_block2[8];
++      u8 endurance_block3[8];
++      u8 reserved5[29];
++
++      /* Reserved. 272 */
++      u8 reserved6[148];
++
++      /* Vendor specific block. 420  */
++      u16 vendor_revision;
++      u8      vendor_spec[88];
++
++      /* CRC for Parameter Page. 510 */
++      u16 crc16;
++} __packed;
++
++/* parallel nand io width */
++enum nand_io_width {
++      NAND_IO8,
++      NAND_IO16
++};
++
++/* all supported nand timming type */
++enum nand_timing_type {
++      NAND_TIMING_SDR,
++      NAND_TIMING_SYNC_DDR,
++      NAND_TIMING_TOGGLE_DDR,
++      NAND_TIMING_NVDDR2
++};
++
++/* nand basic commands */
++struct nand_cmds {
++      short reset;
++      short read_id;
++      short read_status;
++      short read_param_page;
++      short set_feature;
++      short get_feature;
++      short read_1st;
++      short read_2nd;
++      short random_out_1st;
++      short random_out_2nd;
++      short program_1st;
++      short program_2nd;
++      short erase_1st;
++      short erase_2nd;
++      short read_cache;
++      short read_cache_last;
++      short program_cache;
++};
++
++/*
++ * addressing for nand physical address
++ * @row_bit_start: row address start bit
++ * @block_bit_start: block address start bit
++ * @plane_bit_start: plane address start bit
++ * @lun_bit_start: lun address start bit
++ */
++struct nand_addressing {
++      u8 row_bit_start;
++      u8 block_bit_start;
++      u8 plane_bit_start;
++      u8 lun_bit_start;
++};
++
++/*
++ * nand operations status
++ * @array_busy: indicates device array operation busy
++ * @write_protect: indicates the device cannot be wrote or erased
++ * @erase_fail: indicates erase operation fail
++ * @program_fail: indicates program operation fail
++ */
++struct nand_status {
++      u8 array_busy;
++      u8 write_protect;
++      u8 erase_fail;
++      u8 program_fail;
++};
++
++/*
++ * nand endurance information
++ * @pe_cycle: max program/erase cycle for nand stored data stability
++ * @ecc_req: ecc strength required for the nand, measured per 1KB
++ * @max_bitflips: bitflips is ecc corrected bits,
++ *    max_bitflips is the threshold for nand stored data stability
++ *    if corrected bits is over max_bitflips, stored data must be moved
++ *    to another good block
++ */
++struct nand_endurance {
++      int pe_cycle;
++      int ecc_req;
++      int max_bitflips;
++};
++
++/* wait for nand busy type */
++enum nand_wait_type {
++      NAND_WAIT_IRQ,
++      NAND_WAIT_POLLING,
++      NAND_WAIT_TWHR2,
++};
++
++/* each nand array operations time */
++struct nand_array_timing {
++      u16 tRST;
++      u16 tWHR;
++      u16 tR;
++      u16 tRCBSY;
++      u16 tFEAT;
++      u16 tPROG;
++      u16 tPCBSY;
++      u16 tBERS;
++      u16 tDBSY;
++};
++
++/* nand sdr interface timing required */
++struct nand_sdr_timing {
++      u16 tREA;
++      u16 tREH;
++      u16 tCR;
++      u16 tRP;
++      u16 tWP;
++      u16 tWH;
++      u16 tWHR;
++      u16 tCLS;
++      u16 tALS;
++      u16 tCLH;
++      u16 tALH;
++      u16 tWC;
++      u16 tRC;
++};
++
++/* nand onfi ddr (nvddr) interface timing required */
++struct nand_onfi_timing {
++      u16 tCAD;
++      u16 tWPRE;
++      u16 tWPST;
++      u16 tWRCK;
++      u16 tDQSCK;
++      u16 tWHR;
++};
++
++/* nand toggle ddr (toggle 1.0) interface timing required */
++struct nand_toggle_timing {
++      u16 tCS;
++      u16 tCH;
++      u16 tCAS;
++      u16 tCAH;
++      u16 tCALS;
++      u16 tCALH;
++      u16 tWP;
++      u16 tWPRE;
++      u16 tWPST;
++      u16 tWPSTH;
++      u16 tCR;
++      u16 tRPRE;
++      u16 tRPST;
++      u16 tRPSTH;
++      u16 tCDQSS;
++      u16 tWHR;
++};
++
++/* nand basic device information */
++struct nand_device {
++      u8 *name;
++      u64 id;
++      u8 id_len;
++      u8 io_width;
++      u8 row_cycle;
++      u8 col_cycle;
++      u8 target_num;
++      u8 lun_num;
++      u8 plane_num;
++      int block_num;
++      int block_size;
++      int page_size;
++      int spare_size;
++      int min_program_pages;
++      struct nand_cmds *cmds;
++      struct nand_addressing *addressing;
++      struct nand_status *status;
++      struct nand_endurance *endurance;
++      struct nand_array_timing *array_timing;
++};
++
++#define NAND_DEVICE(_name, _id, _id_len, _io_width, _row_cycle, \
++                  _col_cycle, _target_num, _lun_num, _plane_num, \
++                  _block_num, _block_size, _page_size, _spare_size, \
++                  _min_program_pages, _cmds, _addressing, _status, \
++                  _endurance, _array_timing) \
++{ \
++      _name, _id, _id_len, _io_width, _row_cycle, \
++      _col_cycle, _target_num, _lun_num, _plane_num, \
++      _block_num, _block_size, _page_size, _spare_size, \
++      _min_program_pages, _cmds, _addressing, _status, \
++      _endurance, _array_timing \
++}
++
++#define MAX_ID_NUM      sizeof(u64)
++
++#define NAND_PACK_ID(id0, id1, id2, id3, id4, id5, id6, id7) \
++      ( \
++        id0 | id1 << 8 | id2 << 16 | id3 << 24 | \
++        (u64)id4 << 32 | (u64)id5 << 40 | \
++        (u64)id6 << 48 | (u64)id7 << 56 \
++      )
++
++#define NAND_UNPACK_ID(id, ids, len) \
++      do { \
++              int _i; \
++              for (_i = 0; _i < len; _i++) \
++                      ids[_i] = id >> (_i << 3) & 0xff; \
++      } while (0)
++
++static inline int nand_block_pages(struct nand_device *device)
++{
++      return div_down(device->block_size, device->page_size);
++}
++
++static inline int nand_lun_blocks(struct nand_device *device)
++{
++      return device->plane_num * device->block_num;
++}
++
++static inline int nand_target_blocks(struct nand_device *device)
++{
++      return device->lun_num * device->plane_num * device->block_num;
++}
++
++static inline int nand_total_blocks(struct nand_device *device)
++{
++      return device->target_num * device->lun_num * device->plane_num *
++             device->block_num;
++}
++
++struct nand_device *nand_get_device(int index);
++#endif /* __NAND_DEVICE_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi.h b/drivers/mtd/nandx/core/nfi.h
+new file mode 100644
+index 0000000000..ba84e73ccc
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi.h
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFI_H__
++#define __NFI_H__
++
++struct nfi_format {
++      int page_size;
++      int spare_size;
++      int ecc_req;
++};
++
++struct nfi {
++      int sector_size;
++      int sector_spare_size;
++      int fdm_size; /*for sector*/
++      int fdm_ecc_size;
++      int ecc_strength;
++      int ecc_parity_size; /*for sector*/
++
++      int (*select_chip)(struct nfi *nfi, int cs);
++      int (*set_format)(struct nfi *nfi, struct nfi_format *format);
++      int (*set_timing)(struct nfi *nfi, void *timing, int type);
++      int (*nfi_ctrl)(struct nfi *nfi, int cmd, void *args);
++
++      int (*reset)(struct nfi *nfi);
++      int (*send_cmd)(struct nfi *nfi, short cmd);
++      int (*send_addr)(struct nfi *nfi, int col, int row,
++                       int col_cycle, int row_cycle);
++      int (*trigger)(struct nfi *nfi);
++
++      int (*write_page)(struct nfi *nfi, u8 *data, u8 *fdm);
++      int (*write_bytes)(struct nfi *nfi, u8 *data, int count);
++      int (*read_sectors)(struct nfi *nfi, u8 *data, u8 *fdm,
++                          int sectors);
++      int (*read_bytes)(struct nfi *nfi, u8 *data, int count);
++
++      int (*wait_ready)(struct nfi *nfi, int type, u32 timeout);
++
++      int (*enable_randomizer)(struct nfi *nfi, u32 row, bool encode);
++      int (*disable_randomizer)(struct nfi *nfi);
++};
++
++struct nfi *nfi_init(struct nfi_resource *res);
++void nfi_exit(struct nfi *nfi);
++
++#endif /* __NFI_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_base.c b/drivers/mtd/nandx/core/nfi/nfi_base.c
+new file mode 100644
+index 0000000000..d8679d7aa3
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_base.c
+@@ -0,0 +1,1357 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++/**
++ * nfi_base.c - the base logic for nfi to access nand flash
++ *
++ * slc/mlc/tlc could use same code to access nand
++ * of cause, there still some work need to do
++ * even for spi nand, there should be a chance to integrate code together
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "../nfi.h"
++#include "../nand_device.h"
++#include "nfi_regs.h"
++#include "nfiecc.h"
++#include "nfi_base.h"
++
++static const int spare_size_mt7622[] = {
++      16, 26, 27, 28
++};
++
++#define RAND_SEED_SHIFT(op) \
++      ((op) == RAND_ENCODE ? ENCODE_SEED_SHIFT : DECODE_SEED_SHIFT)
++#define RAND_EN(op) \
++      ((op) == RAND_ENCODE ? RAN_ENCODE_EN : RAN_DECODE_EN)
++
++#define SS_SEED_NUM     128
++static u16 ss_randomizer_seed[SS_SEED_NUM] = {
++      0x576A, 0x05E8, 0x629D, 0x45A3, 0x649C, 0x4BF0, 0x2342, 0x272E,
++      0x7358, 0x4FF3, 0x73EC, 0x5F70, 0x7A60, 0x1AD8, 0x3472, 0x3612,
++      0x224F, 0x0454, 0x030E, 0x70A5, 0x7809, 0x2521, 0x484F, 0x5A2D,
++      0x492A, 0x043D, 0x7F61, 0x3969, 0x517A, 0x3B42, 0x769D, 0x0647,
++      0x7E2A, 0x1383, 0x49D9, 0x07B8, 0x2578, 0x4EEC, 0x4423, 0x352F,
++      0x5B22, 0x72B9, 0x367B, 0x24B6, 0x7E8E, 0x2318, 0x6BD0, 0x5519,
++      0x1783, 0x18A7, 0x7B6E, 0x7602, 0x4B7F, 0x3648, 0x2C53, 0x6B99,
++      0x0C23, 0x67CF, 0x7E0E, 0x4D8C, 0x5079, 0x209D, 0x244A, 0x747B,
++      0x350B, 0x0E4D, 0x7004, 0x6AC3, 0x7F3E, 0x21F5, 0x7A15, 0x2379,
++      0x1517, 0x1ABA, 0x4E77, 0x15A1, 0x04FA, 0x2D61, 0x253A, 0x1302,
++      0x1F63, 0x5AB3, 0x049A, 0x5AE8, 0x1CD7, 0x4A00, 0x30C8, 0x3247,
++      0x729C, 0x5034, 0x2B0E, 0x57F2, 0x00E4, 0x575B, 0x6192, 0x38F8,
++      0x2F6A, 0x0C14, 0x45FC, 0x41DF, 0x38DA, 0x7AE1, 0x7322, 0x62DF,
++      0x5E39, 0x0E64, 0x6D85, 0x5951, 0x5937, 0x6281, 0x33A1, 0x6A32,
++      0x3A5A, 0x2BAC, 0x743A, 0x5E74, 0x3B2E, 0x7EC7, 0x4FD2, 0x5D28,
++      0x751F, 0x3EF8, 0x39B1, 0x4E49, 0x746B, 0x6EF6, 0x44BE, 0x6DB7
++};
++
++#if 0
++static void dump_register(void *regs)
++{
++      int i;
++
++      pr_info("registers:\n");
++      for (i = 0; i < 0x600; i += 0x10) {
++              pr_info("    address 0x%X : %X %X %X %X\n",
++                      (u32)((unsigned long)regs + i),
++                      (u32)readl(regs + i),
++                      (u32)readl(regs + i + 0x4),
++                      (u32)readl(regs + i + 0x8),
++                      (u32)readl(regs + i + 0xC));
++      }
++}
++#endif
++
++static int nfi_enable_randomizer(struct nfi *nfi, u32 row, bool encode)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      enum randomizer_op op = RAND_ENCODE;
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      if (!encode)
++              op = RAND_DECODE;
++
++      /* randomizer type and reseed type setup */
++      val = readl(regs + NFI_CNFG);
++      val |= CNFG_RAND_SEL | CNFG_RESEED_SEC_EN;
++      writel(val, regs + NFI_CNFG);
++
++      /* randomizer seed and type setup */
++      val = ss_randomizer_seed[row % SS_SEED_NUM] & RAN_SEED_MASK;
++      val <<= RAND_SEED_SHIFT(op);
++      val |= RAND_EN(op);
++      writel(val, regs + NFI_RANDOM_CNFG);
++
++      return 0;
++}
++
++static int nfi_disable_randomizer(struct nfi *nfi)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++
++      writel(0, nb->res.nfi_regs + NFI_RANDOM_CNFG);
++
++      return 0;
++}
++
++static int nfi_irq_handler(int irq, void *data)
++{
++      struct nfi_base *nb = (struct nfi_base *) data;
++      void *regs = nb->res.nfi_regs;
++      u16 status, en;
++
++      status = readw(regs + NFI_INTR_STA);
++      en = readw(regs + NFI_INTR_EN);
++
++      if (!(status & en))
++              return NAND_IRQ_NONE;
++
++      writew(~status & en, regs + NFI_INTR_EN);
++
++      nandx_event_complete(nb->done);
++
++      return NAND_IRQ_HANDLED;
++}
++
++static int nfi_select_chip(struct nfi *nfi, int cs)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++
++      writel(cs, nb->res.nfi_regs + NFI_CSEL);
++
++      return 0;
++}
++
++static inline void set_op_mode(void *regs, u32 mode)
++{
++      u32 val = readl(regs + NFI_CNFG);
++
++      val &= ~CNFG_OP_MODE_MASK;
++      val |= mode;
++
++      writel(val, regs + NFI_CNFG);
++}
++
++static int nfi_reset(struct nfi *nfi)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      int ret, val;
++
++      /* The NFI reset to reset all registers and force the NFI
++       * master be early terminated
++       */
++      writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON);
++
++      /* check state of NFI internal FSM and NAND interface FSM */
++      ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA, val,
++                                      !(val & MASTER_BUS_BUSY),
++                                      10, NFI_TIMEOUT);
++      if (ret)
++              pr_info("nfi reset timeout...\n");
++
++      writel(CON_FIFO_FLUSH | CON_NFI_RST, regs + NFI_CON);
++      writew(STAR_DE, regs + NFI_STRDATA);
++
++      return ret;
++}
++
++static void bad_mark_swap(struct nfi *nfi, u8 *buf, u8 *fdm)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      u32 start_sector = div_down(nb->col, nfi->sector_size);
++      u32 data_mark_pos;
++      u8 temp;
++
++      /* raw access, no need to do swap. */
++      if (!nb->ecc_en)
++              return;
++
++      if (!buf || !fdm)
++              return;
++
++      if (nb->bad_mark_ctrl.sector < start_sector ||
++          nb->bad_mark_ctrl.sector > start_sector + nb->rw_sectors)
++              return;
++
++      data_mark_pos = nb->bad_mark_ctrl.position +
++                      (nb->bad_mark_ctrl.sector - start_sector) *
++                      nfi->sector_size;
++
++      temp = *fdm;
++      *fdm = *(buf + data_mark_pos);
++      *(buf + data_mark_pos) = temp;
++}
++
++static u8 *fdm_shift(struct nfi *nfi, u8 *fdm, int sector)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      u8 *pos;
++
++      if (!fdm)
++              return NULL;
++
++      /* map the sector's FDM data to free oob:
++       * the beginning of the oob area stores the FDM data of bad mark sectors
++       */
++      if (sector < nb->bad_mark_ctrl.sector)
++              pos = fdm + (sector + 1) * nfi->fdm_size;
++      else if (sector == nb->bad_mark_ctrl.sector)
++              pos = fdm;
++      else
++              pos = fdm + sector * nfi->fdm_size;
++
++      return pos;
++
++}
++
++static void set_bad_mark_ctrl(struct nfi_base *nb)
++{
++      int temp, page_size = nb->format.page_size;
++
++      nb->bad_mark_ctrl.bad_mark_swap = bad_mark_swap;
++      nb->bad_mark_ctrl.fdm_shift = fdm_shift;
++
++      temp = nb->nfi.sector_size + nb->nfi.sector_spare_size;
++      nb->bad_mark_ctrl.sector = div_down(page_size, temp);
++      nb->bad_mark_ctrl.position = reminder(page_size, temp);
++}
++
++/* NOTE: check if page_size valid future */
++static int setup_format(struct nfi_base *nb, int spare_idx)
++{
++      struct nfi *nfi = &nb->nfi;
++      u32 page_size = nb->format.page_size;
++      u32 val;
++
++      switch (page_size) {
++      case 512:
++              val = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
++              break;
++
++      case KB(2):
++              if (nfi->sector_size == 512)
++                      val = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
++              else
++                      val = PAGEFMT_512_2K;
++
++              break;
++
++      case KB(4):
++              if (nfi->sector_size == 512)
++                      val = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
++              else
++                      val = PAGEFMT_2K_4K;
++
++              break;
++
++      case KB(8):
++              if (nfi->sector_size == 512)
++                      val = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
++              else
++                      val = PAGEFMT_4K_8K;
++
++              break;
++
++      case KB(16):
++              val = PAGEFMT_8K_16K;
++              break;
++
++      default:
++              pr_info("invalid page len: %d\n", page_size);
++              return -EINVAL;
++      }
++
++      val |= spare_idx << PAGEFMT_SPARE_SHIFT;
++      val |= nfi->fdm_size << PAGEFMT_FDM_SHIFT;
++      val |= nfi->fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT;
++      writel(val, nb->res.nfi_regs + NFI_PAGEFMT);
++
++      if (nb->custom_sector_en) {
++              val = nfi->sector_spare_size + nfi->sector_size;
++              val |= SECCUS_SIZE_EN;
++              writel(val, nb->res.nfi_regs + NFI_SECCUS_SIZE);
++      }
++
++      return 0;
++}
++
++static int adjust_spare(struct nfi_base *nb, int *spare)
++{
++      int multi = nb->nfi.sector_size == 512 ? 1 : 2;
++      int i, count = nb->caps->spare_size_num;
++
++      if (*spare >= nb->caps->spare_size[count - 1] * multi) {
++              *spare = nb->caps->spare_size[count - 1] * multi;
++              return count - 1;
++      }
++
++      if (*spare < nb->caps->spare_size[0] * multi)
++              return -EINVAL;
++
++      for (i = 1; i < count; i++) {
++              if (*spare < nb->caps->spare_size[i] * multi) {
++                      *spare = nb->caps->spare_size[i - 1] * multi;
++                      return i - 1;
++              }
++      }
++
++      return -EINVAL;
++}
++
++static int nfi_set_format(struct nfi *nfi, struct nfi_format *format)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfiecc *ecc = nb->ecc;
++      int ecc_strength = format->ecc_req;
++      int min_fdm, min_ecc, max_ecc;
++      u32 temp, page_sectors;
++      int spare_idx = 0;
++
++      if (!nb->buf) {
++#if NANDX_BULK_IO_USE_DRAM
++              nb->buf = NANDX_NFI_BUF_ADDR;
++#else
++              nb->buf = mem_alloc(1, format->page_size + format->spare_size);
++#endif
++              if (!nb->buf)
++                      return -ENOMEM;
++      }
++
++      nb->format = *format;
++
++      /* ToBeFixed: for spi nand, now sector size is 512,
++       * it should be same with slc.
++       */
++      nfi->sector_size = 512;
++      /* format->ecc_req is the requirement per 1KB */
++      ecc_strength >>= 1;
++
++      page_sectors = div_down(format->page_size, nfi->sector_size);
++      nfi->sector_spare_size = div_down(format->spare_size, page_sectors);
++
++      if (!nb->custom_sector_en) {
++              spare_idx = adjust_spare(nb, &nfi->sector_spare_size);
++              if (spare_idx < 0)
++                      return -EINVAL;
++      }
++
++      /* calculate ecc strength and fdm size */
++      temp = (nfi->sector_spare_size - nb->caps->max_fdm_size) * 8;
++      min_ecc = div_down(temp, nb->caps->ecc_parity_bits);
++      min_ecc = ecc->adjust_strength(ecc, min_ecc);
++      if (min_ecc < 0)
++              return -EINVAL;
++
++      temp = div_up(nb->res.min_oob_req, page_sectors);
++      temp = (nfi->sector_spare_size - temp) * 8;
++      max_ecc = div_down(temp, nb->caps->ecc_parity_bits);
++      max_ecc = ecc->adjust_strength(ecc, max_ecc);
++      if (max_ecc < 0)
++              return -EINVAL;
++
++      temp = div_up(temp * nb->caps->ecc_parity_bits, 8);
++      temp = nfi->sector_spare_size - temp;
++      min_fdm = min_t(u32, temp, (u32)nb->caps->max_fdm_size);
++
++      if (ecc_strength > max_ecc) {
++              pr_info("required ecc strength %d, max supported %d\n",
++                      ecc_strength, max_ecc);
++              nfi->ecc_strength = max_ecc;
++              nfi->fdm_size = min_fdm;
++      } else if (format->ecc_req < min_ecc) {
++              nfi->ecc_strength = min_ecc;
++              nfi->fdm_size = nb->caps->max_fdm_size;
++      } else {
++              ecc_strength = ecc->adjust_strength(ecc, ecc_strength);
++              if (ecc_strength < 0)
++                      return -EINVAL;
++
++              nfi->ecc_strength = ecc_strength;
++              temp = div_up(ecc_strength * nb->caps->ecc_parity_bits, 8);
++              nfi->fdm_size = nfi->sector_spare_size - temp;
++      }
++
++      nb->page_sectors = div_down(format->page_size, nfi->sector_size);
++
++      /* some IC has fixed fdm_ecc_size, if not assigend, set to fdm_size */
++      nfi->fdm_ecc_size = nb->caps->fdm_ecc_size ? : nfi->fdm_size;
++
++      nfi->ecc_parity_size = div_up(nfi->ecc_strength *
++                                    nb->caps->ecc_parity_bits,
++                                    8);
++      set_bad_mark_ctrl(nb);
++
++      pr_debug("sector_size: %d\n", nfi->sector_size);
++      pr_debug("sector_spare_size: %d\n", nfi->sector_spare_size);
++      pr_debug("fdm_size: %d\n", nfi->fdm_size);
++      pr_debug("fdm_ecc_size: %d\n", nfi->fdm_ecc_size);
++      pr_debug("ecc_strength: %d\n", nfi->ecc_strength);
++      pr_debug("ecc_parity_size: %d\n", nfi->ecc_parity_size);
++
++      return setup_format(nb, spare_idx);
++}
++
++static int nfi_ctrl(struct nfi *nfi, int cmd, void *args)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      int ret = 0;
++
++      switch (cmd) {
++      case NFI_CTRL_DMA:
++              nb->dma_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_AUTOFORMAT:
++              nb->auto_format = *(bool *)args;
++              break;
++
++      case NFI_CTRL_NFI_IRQ:
++              nb->nfi_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_PAGE_IRQ:
++              nb->page_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_BAD_MARK_SWAP:
++              nb->bad_mark_swap_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC:
++              nb->ecc_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_MODE:
++              nb->ecc_mode = *(enum nfiecc_mode *)args;
++              break;
++
++      case NFI_CTRL_ECC_CLOCK:
++              /* NOTE: it seems that there's nothing need to do
++               * if new IC need, just add tht logic
++               */
++              nb->ecc_clk_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_IRQ:
++              nb->ecc_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_DECODE_MODE:
++              nb->ecc_deccon = *(enum nfiecc_deccon *)args;
++              break;
++
++      default:
++              pr_info("invalid arguments.\n");
++              ret = -EOPNOTSUPP;
++              break;
++      }
++
++      pr_debug("%s: set cmd(%d) to %d\n", __func__, cmd, *(int *)args);
++      return ret;
++}
++
++static int nfi_send_cmd(struct nfi *nfi, short cmd)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      int ret;
++      u32 val;
++
++      pr_debug("%s: cmd 0x%x\n", __func__, cmd);
++
++      if (cmd < 0)
++              return -EINVAL;
++
++      set_op_mode(regs, nb->op_mode);
++
++      writel(cmd, regs + NFI_CMD);
++
++      ret = readl_poll_timeout_atomic(regs + NFI_STA,
++                                      val, !(val & STA_CMD),
++                                      5, NFI_TIMEOUT);
++      if (ret)
++              pr_info("send cmd 0x%x timeout\n", cmd);
++
++      return ret;
++}
++
++static int nfi_send_addr(struct nfi *nfi, int col, int row,
++                       int col_cycle, int row_cycle)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      int ret;
++      u32 val;
++
++      pr_debug("%s: col 0x%x, row 0x%x, col_cycle 0x%x, row_cycle 0x%x\n",
++               __func__, col, row, col_cycle, row_cycle);
++
++      nb->col = col;
++      nb->row = row;
++
++      writel(col, regs + NFI_COLADDR);
++      writel(row, regs + NFI_ROWADDR);
++      writel(col_cycle | (row_cycle << ROW_SHIFT), regs + NFI_ADDRNOB);
++
++      ret = readl_poll_timeout_atomic(regs + NFI_STA,
++                                      val, !(val & STA_ADDR),
++                                      5, NFI_TIMEOUT);
++      if (ret)
++              pr_info("send address timeout\n");
++
++      return ret;
++}
++
++static int nfi_trigger(struct nfi *nfi)
++{
++      /* Nothing need to do. */
++      return 0;
++}
++
++static inline int wait_io_ready(void *regs)
++{
++      u32 val;
++      int ret;
++
++      ret = readl_poll_timeout_atomic(regs + NFI_PIO_DIRDY,
++                                      val, val & PIO_DI_RDY,
++                                      2, NFI_TIMEOUT);
++      if (ret)
++              pr_info("wait io ready timeout\n");
++
++      return ret;
++}
++
++static int wait_ready_irq(struct nfi_base *nb, u32 timeout)
++{
++      void *regs = nb->res.nfi_regs;
++      int ret;
++      u32 val;
++
++      writel(0xf1, regs + NFI_CNRNB);
++      nandx_event_init(nb->done);
++
++      writel(INTR_BUSY_RETURN_EN, (void *)(regs + NFI_INTR_EN));
++
++      /**
++       * check if nand already bean ready,
++       * avoid issue that casued by missing irq-event.
++       */
++      val = readl(regs + NFI_STA);
++      if (val & STA_BUSY2READY) {
++              readl(regs + NFI_INTR_STA);
++              writel(0, (void *)(regs + NFI_INTR_EN));
++              return 0;
++      }
++
++      ret = nandx_event_wait_complete(nb->done, timeout);
++
++      writew(0, regs + NFI_CNRNB);
++      return ret;
++}
++
++static void wait_ready_twhr2(struct nfi_base *nb, u32 timeout)
++{
++      /* NOTE: this for tlc */
++}
++
++static int wait_ready_poll(struct nfi_base *nb, u32 timeout)
++{
++      void *regs = nb->res.nfi_regs;
++      int ret;
++      u32 val;
++
++      writel(0x21, regs + NFI_CNRNB);
++      ret = readl_poll_timeout_atomic(regs + NFI_STA, val,
++                                      val & STA_BUSY2READY,
++                                      2, timeout);
++      writew(0, regs + NFI_CNRNB);
++
++      return ret;
++}
++
++static int nfi_wait_ready(struct nfi *nfi, int type, u32 timeout)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      int ret;
++
++      switch (type) {
++      case NAND_WAIT_IRQ:
++              if (nb->nfi_irq_en)
++                      ret = wait_ready_irq(nb, timeout);
++              else
++                      ret = -EINVAL;
++
++              break;
++
++      case NAND_WAIT_POLLING:
++              ret = wait_ready_poll(nb, timeout);
++              break;
++
++      case NAND_WAIT_TWHR2:
++              wait_ready_twhr2(nb, timeout);
++              ret = 0;
++              break;
++
++      default:
++              ret = -EINVAL;
++              break;
++      }
++
++      if (ret)
++              pr_info("%s: type 0x%x, timeout 0x%x\n",
++                     __func__, type, timeout);
++
++      return ret;
++}
++
++static int enable_ecc_decode(struct nfi_base *nb, int sectors)
++{
++      struct nfi *nfi = &nb->nfi;
++      struct nfiecc *ecc = nb->ecc;
++
++      ecc->config.op = ECC_DECODE;
++      ecc->config.mode = nb->ecc_mode;
++      ecc->config.deccon = nb->ecc_deccon;
++      ecc->config.sectors = sectors;
++      ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size;
++      ecc->config.strength = nfi->ecc_strength;
++
++      return ecc->enable(ecc);
++}
++
++static int enable_ecc_encode(struct nfi_base *nb)
++{
++      struct nfiecc *ecc = nb->ecc;
++      struct nfi *nfi = &nb->nfi;
++
++      ecc->config.op = ECC_ENCODE;
++      ecc->config.mode = nb->ecc_mode;
++      ecc->config.len = nfi->sector_size + nfi->fdm_ecc_size;
++      ecc->config.strength = nfi->ecc_strength;
++
++      return ecc->enable(ecc);
++}
++
++static void read_fdm(struct nfi_base *nb, u8 *fdm, int start_sector,
++                   int sectors)
++{
++      void *regs = nb->res.nfi_regs;
++      int j, i = start_sector;
++      u32 vall, valm;
++      u8 *buf = fdm;
++
++      for (; i < start_sector + sectors; i++) {
++              if (nb->bad_mark_swap_en)
++                      buf = nb->bad_mark_ctrl.fdm_shift(&nb->nfi, fdm, i);
++
++              vall = readl(regs + NFI_FDML(i));
++              valm = readl(regs + NFI_FDMM(i));
++
++              for (j = 0; j < nb->nfi.fdm_size; j++)
++                      *buf++ = (j >= 4 ? valm : vall) >> ((j & 3) << 3);
++      }
++}
++
++static void write_fdm(struct nfi_base *nb, u8 *fdm)
++{
++      struct nfi *nfi = &nb->nfi;
++      void *regs = nb->res.nfi_regs;
++      u32 vall, valm;
++      int i, j;
++      u8 *buf = fdm;
++
++      for (i = 0; i < nb->page_sectors; i++) {
++              if (nb->bad_mark_swap_en)
++                      buf = nb->bad_mark_ctrl.fdm_shift(nfi, fdm, i);
++
++              vall = 0;
++              for (j = 0; j < 4; j++)
++                      vall |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8);
++              writel(vall, regs + NFI_FDML(i));
++
++              valm = 0;
++              for (j = 0; j < 4; j++)
++                      valm |= (j < nfi->fdm_size ? *buf++ : 0xff) << (j * 8);
++              writel(valm, regs + NFI_FDMM(i));
++      }
++}
++
++/* NOTE: pio not use auto format */
++static int pio_rx_data(struct nfi_base *nb, u8 *data, u8 *fdm,
++                     int sectors)
++{
++      struct nfiecc_status ecc_status;
++      struct nfi *nfi = &nb->nfi;
++      void *regs = nb->res.nfi_regs;
++      u32 val, bitflips = 0;
++      int len, ret, i;
++      u8 *buf;
++
++      val = readl(regs + NFI_CNFG) | CNFG_BYTE_RW;
++      writel(val, regs + NFI_CNFG);
++
++      len = nfi->sector_size + nfi->sector_spare_size;
++      len *= sectors;
++
++      for (i = 0; i < len; i++) {
++              ret = wait_io_ready(regs);
++              if (ret)
++                      return ret;
++
++              nb->buf[i] = readb(regs + NFI_DATAR);
++      }
++
++      /* TODO: do error handle for autoformat setting of pio */
++      if (nb->ecc_en) {
++              for (i = 0; i < sectors; i++) {
++                      buf = nb->buf + i * (nfi->sector_size +
++                                           nfi->sector_spare_size);
++                      ret = nb->ecc->correct_data(nb->ecc, &ecc_status,
++                                                  buf, i);
++                      if (data)
++                              memcpy(data + i * nfi->sector_size,
++                                     buf, nfi->sector_size);
++                      if (fdm)
++                              memcpy(fdm + i * nfi->fdm_size,
++                                     buf + nfi->sector_size, nfi->fdm_size);
++                      if (ret) {
++                              ret = nb->ecc->decode_status(nb->ecc, i, 1);
++                              if (ret < 0)
++                                      return ret;
++
++                              bitflips = max_t(int, (int)bitflips, ret);
++                      }
++              }
++
++              return bitflips;
++      }
++
++      /* raw read, only data not null, and its length should be $len */
++      if (data)
++              memcpy(data, nb->buf, len);
++
++      return 0;
++}
++
++static int pio_tx_data(struct nfi_base *nb, u8 *data, u8 *fdm,
++                     int sectors)
++{
++      struct nfi *nfi = &nb->nfi;
++      void *regs = nb->res.nfi_regs;
++      u32 i, val;
++      int len, ret;
++
++      val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW;
++      writew(val, regs + NFI_CNFG);
++
++      len = nb->ecc_en ? nfi->sector_size :
++            nfi->sector_size + nfi->sector_spare_size;
++      len *= sectors;
++
++      /* data shouldn't null,
++       * and if ecc enable ,fdm been written in prepare process
++       */
++      for (i = 0; i < len; i++) {
++              ret = wait_io_ready(regs);
++              if (ret)
++                      return ret;
++              writeb(data[i], regs + NFI_DATAW);
++      }
++
++      return 0;
++}
++
++static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm,
++                        int sectors)
++{
++      u32 empty = readl(nb->res.nfi_regs + NFI_STA) & STA_EMP_PAGE;
++
++      if (empty) {
++              pr_info("empty page!\n");
++              return true;
++      }
++
++      return false;
++}
++
++static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data,
++                    u8 *fdm, bool read)
++{
++      void *regs = nb->res.nfi_regs;
++      u32 len = nb->nfi.sector_size * sectors;
++      bool irq_en = nb->dma_en && nb->nfi_irq_en;
++      void *dma_addr;
++      u32 val;
++      int ret;
++
++      nb->rw_sectors = sectors;
++
++      if (irq_en) {
++              nandx_event_init(nb->done);
++              writel(INTR_AHB_DONE_EN, regs + NFI_INTR_EN);
++      }
++
++      val = readw(regs + NFI_CNFG);
++      if (read)
++              val |= CNFG_READ_EN;
++      else
++              val &= ~CNFG_READ_EN;
++
++      /* as design, now, auto format enabled when ecc enabled */
++      if (nb->ecc_en) {
++              val |= CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN;
++
++              if (read)
++                      ret = enable_ecc_decode(nb, sectors);
++              else
++                      ret = enable_ecc_encode(nb);
++
++              if (ret) {
++                      pr_info("%s: ecc enable %s fail!\n", __func__,
++                             read ? "decode" : "encode");
++                      return ret;
++              }
++      } else {
++              val &= ~(CNFG_HW_ECC_EN | CNFG_AUTO_FMT_EN);
++      }
++
++      if (!read && nb->bad_mark_swap_en)
++              nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm);
++
++      if (!nb->ecc_en && read)
++              len += sectors * nb->nfi.sector_spare_size;
++
++      if (nb->dma_en) {
++              val |= CNFG_DMA_BURST_EN | CNFG_AHB;
++
++              if (read) {
++                      dma_addr = (void *)(unsigned long)nandx_dma_map(
++                                              nb->res.dev, nb->buf,
++                                              (u64)len, NDMA_FROM_DEV);
++              } else {
++                      memcpy(nb->buf, data, len);
++                      dma_addr = (void *)(unsigned long)nandx_dma_map(
++                                              nb->res.dev, nb->buf,
++                                              (u64)len, NDMA_TO_DEV);
++              }
++
++              writel((unsigned long)dma_addr, (void *)regs + NFI_STRADDR);
++
++              nb->access_len = len;
++              nb->dma_addr = dma_addr;
++      }
++
++      if (nb->ecc_en && !read && fdm)
++              write_fdm(nb, fdm);
++
++      writew(val, regs + NFI_CNFG);
++      /* setup R/W sector number */
++      writel(sectors << CON_SEC_SHIFT, regs + NFI_CON);
++
++      return 0;
++}
++
++static void rw_trigger(struct nfi_base *nb, bool read)
++{
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      val = read ? CON_BRD : CON_BWR;
++      val |= readl(regs + NFI_CON);
++      writel(val, regs + NFI_CON);
++
++      writel(STAR_EN, regs + NFI_STRDATA);
++}
++
++static int rw_wait_done(struct nfi_base *nb, int sectors, bool read)
++{
++      void *regs = nb->res.nfi_regs;
++      bool irq_en = nb->dma_en && nb->nfi_irq_en;
++      int ret;
++      u32 val;
++
++      if (irq_en) {
++              ret = nandx_event_wait_complete(nb->done, NFI_TIMEOUT);
++              if (!ret) {
++                      writew(0, regs + NFI_INTR_EN);
++                      return ret;
++              }
++      }
++
++      if (read) {
++              ret = readl_poll_timeout_atomic(regs + NFI_BYTELEN, val,
++                                              ADDRCNTR_SEC(val) >=
++                                              (u32)sectors,
++                                              2, NFI_TIMEOUT);
++              /* HW issue: if not wait ahb done, need polling bus busy */
++              if (!ret && !irq_en)
++                      ret = readl_poll_timeout_atomic(regs + NFI_MASTER_STA,
++                                                      val,
++                                                      !(val &
++                                                        MASTER_BUS_BUSY),
++                                                      2, NFI_TIMEOUT);
++      } else {
++              ret = readl_poll_timeout_atomic(regs + NFI_ADDRCNTR, val,
++                                              ADDRCNTR_SEC(val) >=
++                                              (u32)sectors,
++                                              2, NFI_TIMEOUT);
++      }
++
++      if (ret) {
++              pr_info("do page %s timeout\n", read ? "read" : "write");
++              return ret;
++      }
++
++      if (read && nb->ecc_en) {
++              ret = nb->ecc->wait_done(nb->ecc);
++              if (ret)
++                      return ret;
++
++              return nb->ecc->decode_status(nb->ecc, 0, sectors);
++      }
++
++      return 0;
++}
++
++static int rw_data(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors,
++                 bool read)
++{
++      if (read && nb->dma_en && nb->ecc_en && fdm)
++              read_fdm(nb, fdm, 0, sectors);
++
++      if (!nb->dma_en) {
++              if (read)
++                      return pio_rx_data(nb, data, fdm, sectors);
++
++              return pio_tx_data(nb, data, fdm, sectors);
++      }
++
++      return 0;
++}
++
++static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm,
++                      bool read)
++{
++      int data_len = 0;
++      bool is_empty;
++
++      if (nb->dma_en) {
++              if (read) {
++                      nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr,
++                                      (u64)nb->access_len, NDMA_FROM_DEV);
++
++                      if (data) {
++                              data_len = nb->rw_sectors * nb->nfi.sector_size;
++                              memcpy(data, nb->buf, data_len);
++                      }
++
++                      if (fdm)
++                              memcpy(fdm, nb->buf + data_len,
++                                     nb->access_len - data_len);
++
++                      if (nb->read_status == -ENANDREAD) {
++                              is_empty = nb->is_page_empty(nb, data, fdm,
++                                                      nb->rw_sectors);
++                              if (is_empty)
++                                      nb->read_status = 0;
++                      }
++              } else {
++                      nandx_dma_unmap(nb->res.dev, nb->buf, nb->dma_addr,
++                                      (u64)nb->access_len, NDMA_TO_DEV);
++              }
++      }
++
++      /* whether it's reading or writing, we all check if nee swap
++       * for write, we need to restore data
++       */
++      if (nb->bad_mark_swap_en)
++              nb->bad_mark_ctrl.bad_mark_swap(&nb->nfi, data, fdm);
++
++      if (nb->ecc_en)
++              nb->ecc->disable(nb->ecc);
++
++      writel(0, nb->res.nfi_regs + NFI_CNFG);
++      writel(0, nb->res.nfi_regs + NFI_CON);
++}
++
++static int nfi_read_sectors(struct nfi *nfi, u8 *data, u8 *fdm,
++                          int sectors)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      int bitflips = 0, ret;
++
++      pr_debug("%s: read page#%d\n", __func__, nb->row);
++      pr_debug("%s: data address 0x%x, fdm address 0x%x, sectors 0x%x\n",
++               __func__, (u32)((unsigned long)data),
++               (u32)((unsigned long)fdm), sectors);
++
++      nb->read_status = 0;
++
++      ret = nb->rw_prepare(nb, sectors, data, fdm, true);
++      if (ret)
++              return ret;
++
++      nb->rw_trigger(nb, true);
++
++      if (nb->dma_en) {
++              ret = nb->rw_wait_done(nb, sectors, true);
++              if (ret > 0)
++                      bitflips = ret;
++              else if (ret == -ENANDREAD)
++                      nb->read_status = -ENANDREAD;
++              else if (ret < 0)
++                      goto complete;
++
++      }
++
++      ret = nb->rw_data(nb, data, fdm, sectors, true);
++      if (ret > 0)
++              ret = max_t(int, ret, bitflips);
++
++complete:
++      nb->rw_complete(nb, data, fdm, true);
++
++      if (nb->read_status == -ENANDREAD)
++              return -ENANDREAD;
++
++      return ret;
++}
++
++int nfi_write_page(struct nfi *nfi, u8 *data, u8 *fdm)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      u32 sectors = div_down(nb->format.page_size, nfi->sector_size);
++      int ret;
++
++      pr_debug("%s: data address 0x%x, fdm address 0x%x\n",
++               __func__, (int)((unsigned long)data),
++               (int)((unsigned long)fdm));
++
++      ret = nb->rw_prepare(nb, sectors, data, fdm, false);
++      if (ret)
++              return ret;
++
++      nb->rw_trigger(nb, false);
++
++      ret = nb->rw_data(nb, data, fdm, sectors, false);
++      if (ret)
++              return ret;
++
++      ret = nb->rw_wait_done(nb, sectors, false);
++
++      nb->rw_complete(nb, data, fdm, false);
++
++      return ret;
++}
++
++static int nfi_rw_bytes(struct nfi *nfi, u8 *data, int count, bool read)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      int i, ret;
++      u32 val;
++
++      for (i = 0; i < count; i++) {
++              val = readl(regs + NFI_STA) & NFI_FSM_MASK;
++              if (val != NFI_FSM_CUSTDATA) {
++                      val = readw(regs + NFI_CNFG) | CNFG_BYTE_RW;
++                      if (read)
++                              val |= CNFG_READ_EN;
++                      writew(val, regs + NFI_CNFG);
++
++                      val = div_up(count, nfi->sector_size);
++                      val = (val << CON_SEC_SHIFT) | CON_BRD | CON_BWR;
++                      writel(val, regs + NFI_CON);
++
++                      writew(STAR_EN, regs + NFI_STRDATA);
++              }
++
++              ret = wait_io_ready(regs);
++              if (ret)
++                      return ret;
++
++              if (read)
++                      data[i] = readb(regs + NFI_DATAR);
++              else
++                      writeb(data[i], regs + NFI_DATAW);
++      }
++
++      writel(0, nb->res.nfi_regs + NFI_CNFG);
++
++      return 0;
++}
++
++static int nfi_read_bytes(struct nfi *nfi, u8 *data, int count)
++{
++      return nfi_rw_bytes(nfi, data, count, true);
++}
++
++static int nfi_write_bytes(struct nfi *nfi, u8 *data, int count)
++{
++      return nfi_rw_bytes(nfi, data, count, false);
++}
++
++/* As register map says, only when flash macro is idle,
++ * sw reset or nand interface change can be issued
++ */
++static inline int wait_flash_macro_idle(void *regs)
++{
++      u32 val;
++
++      return readl_poll_timeout_atomic(regs + NFI_STA, val,
++                                       val & FLASH_MACRO_IDLE, 2,
++                                       NFI_TIMEOUT);
++}
++
++#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
++      ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \
++       (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt))
++
++static int nfi_set_sdr_timing(struct nfi *nfi, void *timing, u8 type)
++{
++      struct nand_sdr_timing *sdr = (struct nand_sdr_timing *) timing;
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      u32 tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt, tstrobe;
++      u32 rate, val;
++      int ret;
++
++      ret = wait_flash_macro_idle(regs);
++      if (ret)
++              return ret;
++
++      /* turn clock rate into KHZ */
++      rate = nb->res.clock_1x / 1000;
++
++      tpoecs = max_t(u16, sdr->tALH, sdr->tCLH);
++      tpoecs = div_up(tpoecs * rate, 1000000);
++      tpoecs &= 0xf;
++
++      tprecs = max_t(u16, sdr->tCLS, sdr->tALS);
++      tprecs = div_up(tprecs * rate, 1000000);
++      tprecs &= 0x3f;
++
++      /* tc2r is in unit of 2T */
++      tc2r = div_up(sdr->tCR * rate, 1000000);
++      tc2r = div_down(tc2r, 2);
++      tc2r &= 0x3f;
++
++      tw2r = div_up(sdr->tWHR * rate, 1000000);
++      tw2r = div_down(tw2r, 2);
++      tw2r &= 0xf;
++
++      twh = max_t(u16, sdr->tREH, sdr->tWH);
++      twh = div_up(twh * rate, 1000000) - 1;
++      twh &= 0xf;
++
++      twst = div_up(sdr->tWP * rate, 1000000) - 1;
++      twst &= 0xf;
++
++      trlt = div_up(sdr->tRP * rate, 1000000) - 1;
++      trlt &= 0xf;
++
++      /* If tREA is bigger than tRP, setup strobe sel here */
++      if ((trlt + 1) * 1000000 / rate < sdr->tREA) {
++              tstrobe = sdr->tREA - (trlt + 1) * 1000000 / rate;
++              tstrobe = div_up(tstrobe * rate, 1000000);
++              val = readl(regs + NFI_DEBUG_CON1);
++              val &= ~STROBE_MASK;
++              val |= tstrobe << STROBE_SHIFT;
++              writel(val, regs + NFI_DEBUG_CON1);
++      }
++
++      /*
++       * ACCON: access timing control register
++       * -------------------------------------
++       * 31:28: tpoecs, minimum required time for CS post pulling down after
++       *        accessing the device
++       * 27:22: tprecs, minimum required time for CS pre pulling down before
++       *        accessing the device
++       * 21:16: tc2r, minimum required time from NCEB low to NREB low
++       * 15:12: tw2r, minimum required time from NWEB high to NREB low.
++       * 11:08: twh, write enable hold time
++       * 07:04: twst, write wait states
++       * 03:00: trlt, read wait states
++       */
++      val = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt);
++      pr_info("acctiming: 0x%x\n", val);
++      writel(val, regs + NFI_ACCCON);
++
++      /* set NAND type */
++      writel(NAND_TYPE_ASYNC, regs + NFI_NAND_TYPE_CNFG);
++
++      return ret;
++}
++
++static int nfi_set_timing(struct nfi *nfi, void *timing, int type)
++{
++      switch (type) {
++      case NAND_TIMING_SDR:
++              return nfi_set_sdr_timing(nfi, timing, type);
++
++      /* NOTE: for mlc/tlc */
++      case NAND_TIMING_SYNC_DDR:
++      case NAND_TIMING_TOGGLE_DDR:
++      case NAND_TIMING_NVDDR2:
++      default:
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static void set_nfi_funcs(struct nfi *nfi)
++{
++      nfi->select_chip = nfi_select_chip;
++      nfi->set_format = nfi_set_format;
++      nfi->nfi_ctrl = nfi_ctrl;
++      nfi->set_timing = nfi_set_timing;
++
++      nfi->reset = nfi_reset;
++      nfi->send_cmd = nfi_send_cmd;
++      nfi->send_addr = nfi_send_addr;
++      nfi->trigger = nfi_trigger;
++
++      nfi->write_page = nfi_write_page;
++      nfi->write_bytes = nfi_write_bytes;
++      nfi->read_sectors = nfi_read_sectors;
++      nfi->read_bytes = nfi_read_bytes;
++
++      nfi->wait_ready = nfi_wait_ready;
++
++      nfi->enable_randomizer = nfi_enable_randomizer;
++      nfi->disable_randomizer = nfi_disable_randomizer;
++}
++
++static struct nfi_caps nfi_caps_mt7622 = {
++      .max_fdm_size = 8,
++      .fdm_ecc_size = 1,
++      .ecc_parity_bits = 13,
++      .spare_size = spare_size_mt7622,
++      .spare_size_num = 4,
++};
++
++static struct nfi_caps *nfi_get_match_data(enum mtk_ic_version ic)
++{
++      /* NOTE: add other IC's data */
++      return &nfi_caps_mt7622;
++}
++
++static void set_nfi_base_params(struct nfi_base *nb)
++{
++      nb->ecc_en = false;
++      nb->dma_en = false;
++      nb->nfi_irq_en = false;
++      nb->ecc_irq_en = false;
++      nb->page_irq_en = false;
++      nb->ecc_clk_en = false;
++      nb->randomize_en = false;
++      nb->custom_sector_en = false;
++      nb->bad_mark_swap_en = false;
++
++      nb->op_mode = CNFG_CUSTOM_MODE;
++      nb->ecc_deccon = ECC_DEC_CORRECT;
++      nb->ecc_mode = ECC_NFI_MODE;
++
++      nb->done = nandx_event_create();
++      nb->caps = nfi_get_match_data(nb->res.ic_ver);
++
++      nb->set_op_mode = set_op_mode;
++      nb->is_page_empty = is_page_empty;
++
++      nb->rw_prepare = rw_prepare;
++      nb->rw_trigger = rw_trigger;
++      nb->rw_wait_done = rw_wait_done;
++      nb->rw_data = rw_data;
++      nb->rw_complete = rw_complete;
++}
++
++struct nfi *__weak nfi_extend_init(struct nfi_base *nb)
++{
++      return &nb->nfi;
++}
++
++void __weak nfi_extend_exit(struct nfi_base *nb)
++{
++      mem_free(nb);
++}
++
++struct nfi *nfi_init(struct nfi_resource *res)
++{
++      struct nfiecc_resource ecc_res;
++      struct nfi_base *nb;
++      struct nfiecc *ecc;
++      struct nfi *nfi;
++      int ret;
++
++      nb = mem_alloc(1, sizeof(struct nfi_base));
++      if (!nb) {
++              pr_info("nfi alloc memory fail @%s.\n", __func__);
++              return NULL;
++      }
++
++      nb->res = *res;
++
++      ret = nandx_irq_register(res->dev, res->nfi_irq_id, nfi_irq_handler,
++                               "mtk_nand", nb);
++      if (ret) {
++              pr_info("nfi irq register failed!\n");
++              goto error;
++      }
++
++      /* fill ecc paras and init ecc */
++      ecc_res.ic_ver = nb->res.ic_ver;
++      ecc_res.dev = nb->res.dev;
++      ecc_res.irq_id = nb->res.ecc_irq_id;
++      ecc_res.regs = nb->res.ecc_regs;
++      ecc = nfiecc_init(&ecc_res);
++      if (!ecc) {
++              pr_info("nfiecc init fail.\n");
++              return NULL;
++      }
++
++      nb->ecc = ecc;
++
++      set_nfi_base_params(nb);
++      set_nfi_funcs(&nb->nfi);
++
++      /* Assign a temp sector size for reading ID & para page.
++       * We may assign new value later.
++       */
++      nb->nfi.sector_size = 512;
++
++      /* give a default timing, and as discuss
++       * this is the only thing what we need do for nfi init
++       * if need do more, then we can add a function
++       */
++      writel(0x30C77FFF, nb->res.nfi_regs + NFI_ACCCON);
++
++      nfi = nfi_extend_init(nb);
++      if (nfi)
++              return nfi;
++
++error:
++      mem_free(nb);
++      return NULL;
++}
++
++void nfi_exit(struct nfi *nfi)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++
++      nandx_event_destroy(nb->done);
++      nfiecc_exit(nb->ecc);
++#if !NANDX_BULK_IO_USE_DRAM
++      mem_free(nb->buf);
++#endif
++      nfi_extend_exit(nb);
++}
++
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_base.h b/drivers/mtd/nandx/core/nfi/nfi_base.h
+new file mode 100644
+index 0000000000..ae894eaa31
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_base.h
+@@ -0,0 +1,95 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFI_BASE_H__
++#define __NFI_BASE_H__
++
++#define NFI_TIMEOUT             1000000
++
++enum randomizer_op {
++      RAND_ENCODE,
++      RAND_DECODE
++};
++
++struct bad_mark_ctrl {
++      void (*bad_mark_swap)(struct nfi *nfi, u8 *buf, u8 *fdm);
++      u8 *(*fdm_shift)(struct nfi *nfi, u8 *fdm, int sector);
++      u32 sector;
++      u32 position;
++};
++
++struct nfi_caps {
++      u8 max_fdm_size;
++      u8 fdm_ecc_size;
++      u8 ecc_parity_bits;
++      const int *spare_size;
++      u32 spare_size_num;
++};
++
++struct nfi_base {
++      struct nfi nfi;
++      struct nfi_resource res;
++      struct nfiecc *ecc;
++      struct nfi_format format;
++      struct nfi_caps *caps;
++      struct bad_mark_ctrl bad_mark_ctrl;
++
++      /* page_size + spare_size */
++      u8 *buf;
++
++      /* used for spi nand */
++      u8 cmd_mode;
++      u32 op_mode;
++
++      int page_sectors;
++
++      void *done;
++
++      /* for read/write */
++      int col;
++      int row;
++      int access_len;
++      int rw_sectors;
++      void *dma_addr;
++      int read_status;
++
++      bool dma_en;
++      bool nfi_irq_en;
++      bool page_irq_en;
++      bool auto_format;
++      bool ecc_en;
++      bool ecc_irq_en;
++      bool ecc_clk_en;
++      bool randomize_en;
++      bool custom_sector_en;
++      bool bad_mark_swap_en;
++
++      enum nfiecc_deccon ecc_deccon;
++      enum nfiecc_mode ecc_mode;
++
++      void (*set_op_mode)(void *regs, u32 mode);
++      bool (*is_page_empty)(struct nfi_base *nb, u8 *data, u8 *fdm,
++                            int sectors);
++
++      int (*rw_prepare)(struct nfi_base *nb, int sectors, u8 *data, u8 *fdm,
++                        bool read);
++      void (*rw_trigger)(struct nfi_base *nb, bool read);
++      int (*rw_wait_done)(struct nfi_base *nb, int sectors, bool read);
++      int (*rw_data)(struct nfi_base *nb, u8 *data, u8 *fdm, int sectors,
++                     bool read);
++      void (*rw_complete)(struct nfi_base *nb, u8 *data, u8 *fdm, bool read);
++};
++
++static inline struct nfi_base *nfi_to_base(struct nfi *nfi)
++{
++      return container_of(nfi, struct nfi_base, nfi);
++}
++
++struct nfi *nfi_extend_init(struct nfi_base *nb);
++void nfi_extend_exit(struct nfi_base *nb);
++
++#endif /* __NFI_BASE_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_regs.h b/drivers/mtd/nandx/core/nfi/nfi_regs.h
+new file mode 100644
+index 0000000000..ba4868acc8
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_regs.h
+@@ -0,0 +1,114 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFI_REGS_H__
++#define __NFI_REGS_H__
++
++#define NFI_CNFG                0x000
++#define         CNFG_AHB                BIT(0)
++#define         CNFG_READ_EN            BIT(1)
++#define         CNFG_DMA_BURST_EN       BIT(2)
++#define         CNFG_RESEED_SEC_EN      BIT(4)
++#define         CNFG_RAND_SEL           BIT(5)
++#define         CNFG_BYTE_RW            BIT(6)
++#define         CNFG_HW_ECC_EN          BIT(8)
++#define         CNFG_AUTO_FMT_EN        BIT(9)
++#define         CNFG_RAND_MASK          GENMASK(5, 4)
++#define         CNFG_OP_MODE_MASK       GENMASK(14, 12)
++#define         CNFG_IDLE_MOD           0
++#define         CNFG_READ_MODE          (1 << 12)
++#define         CNFG_SINGLE_READ_MODE   (2 << 12)
++#define         CNFG_PROGRAM_MODE       (3 << 12)
++#define         CNFG_ERASE_MODE         (4 << 12)
++#define         CNFG_RESET_MODE         (5 << 12)
++#define         CNFG_CUSTOM_MODE        (6 << 12)
++#define NFI_PAGEFMT             0x004
++#define         PAGEFMT_SPARE_SHIFT     4
++#define         PAGEFMT_FDM_ECC_SHIFT   12
++#define         PAGEFMT_FDM_SHIFT       8
++#define         PAGEFMT_SEC_SEL_512     BIT(2)
++#define         PAGEFMT_512_2K          0
++#define         PAGEFMT_2K_4K           1
++#define         PAGEFMT_4K_8K           2
++#define         PAGEFMT_8K_16K          3
++#define NFI_CON                 0x008
++#define         CON_FIFO_FLUSH          BIT(0)
++#define         CON_NFI_RST             BIT(1)
++#define         CON_BRD                 BIT(8)
++#define         CON_BWR                 BIT(9)
++#define         CON_SEC_SHIFT           12
++#define NFI_ACCCON              0x00c
++#define NFI_INTR_EN             0x010
++#define         INTR_BUSY_RETURN_EN     BIT(4)
++#define         INTR_AHB_DONE_EN        BIT(6)
++#define NFI_INTR_STA            0x014
++#define NFI_CMD                 0x020
++#define NFI_ADDRNOB             0x030
++#define         ROW_SHIFT               4
++#define NFI_COLADDR             0x034
++#define NFI_ROWADDR             0x038
++#define NFI_STRDATA             0x040
++#define         STAR_EN                 1
++#define         STAR_DE                 0
++#define NFI_CNRNB               0x044
++#define NFI_DATAW               0x050
++#define NFI_DATAR               0x054
++#define NFI_PIO_DIRDY           0x058
++#define         PIO_DI_RDY              1
++#define NFI_STA                 0x060
++#define         STA_CMD                 BIT(0)
++#define         STA_ADDR                BIT(1)
++#define         FLASH_MACRO_IDLE        BIT(5)
++#define         STA_BUSY                BIT(8)
++#define         STA_BUSY2READY          BIT(9)
++#define         STA_EMP_PAGE            BIT(12)
++#define         NFI_FSM_CUSTDATA        (0xe << 16)
++#define         NFI_FSM_MASK            GENMASK(19, 16)
++#define         NAND_FSM_MASK           GENMASK(29, 23)
++#define NFI_ADDRCNTR            0x070
++#define         CNTR_VALID_MASK         GENMASK(16, 0)
++#define         CNTR_MASK               GENMASK(15, 12)
++#define         ADDRCNTR_SEC_SHIFT      12
++#define         ADDRCNTR_SEC(val) \
++      (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT)
++#define NFI_STRADDR             0x080
++#define NFI_BYTELEN             0x084
++#define NFI_CSEL                0x090
++#define NFI_FDML(x)             (0x0a0 + (x) * 8)
++#define NFI_FDMM(x)             (0x0a4 + (x) * 8)
++#define NFI_DEBUG_CON1          0x220
++#define         STROBE_MASK             GENMASK(4, 3)
++#define         STROBE_SHIFT            3
++#define         ECC_CLK_EN              BIT(11)
++#define         AUTOC_SRAM_MODE         BIT(12)
++#define         BYPASS_MASTER_EN        BIT(15)
++#define NFI_MASTER_STA          0x224
++#define         MASTER_BUS_BUSY         0x3
++#define NFI_SECCUS_SIZE         0x22c
++#define         SECCUS_SIZE_EN          BIT(17)
++#define NFI_RANDOM_CNFG         0x238
++#define         RAN_ENCODE_EN           BIT(0)
++#define         ENCODE_SEED_SHIFT       1
++#define         RAN_DECODE_EN           BIT(16)
++#define         DECODE_SEED_SHIFT       17
++#define         RAN_SEED_MASK           0x7fff
++#define NFI_EMPTY_THRESH        0x23c
++#define NFI_NAND_TYPE_CNFG      0x240
++#define         NAND_TYPE_ASYNC         0
++#define         NAND_TYPE_TOGGLE        1
++#define         NAND_TYPE_SYNC          2
++#define NFI_ACCCON1             0x244
++#define NFI_DELAY_CTRL          0x248
++#define NFI_TLC_RD_WHR2         0x300
++#define         TLC_RD_WHR2_EN          BIT(12)
++#define         TLC_RD_WHR2_MASK        GENMASK(11, 0)
++#define SNF_SNF_CNFG            0x55c
++#define         SPI_MODE_EN             1
++#define         SPI_MODE_DIS            0
++
++#endif /* __NFI_REGS_H__ */
++
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi.c b/drivers/mtd/nandx/core/nfi/nfi_spi.c
+new file mode 100644
+index 0000000000..67cd0aaad9
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_spi.c
+@@ -0,0 +1,689 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "../nfi.h"
++#include "nfiecc.h"
++#include "nfi_regs.h"
++#include "nfi_base.h"
++#include "nfi_spi_regs.h"
++#include "nfi_spi.h"
++
++#define NFI_CMD_DUMMY_RD 0x00
++#define NFI_CMD_DUMMY_WR 0x80
++
++static struct nfi_spi_delay spi_delay[SPI_NAND_MAX_DELAY] = {
++      /*
++       * tCLK_SAM_DLY, tCLK_OUT_DLY, tCS_DLY, tWR_EN_DLY,
++       * tIO_IN_DLY[4], tIO_OUT_DLY[4], tREAD_LATCH_LATENCY
++       */
++      {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
++      {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
++      {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 0},
++      {0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1},
++      {21, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1},
++      {63, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0}, 1}
++};
++
++static inline struct nfi_spi *base_to_snfi(struct nfi_base *nb)
++{
++      return container_of(nb, struct nfi_spi, base);
++}
++
++static void snfi_mac_enable(struct nfi_base *nb)
++{
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      val = readl(regs + SNF_MAC_CTL);
++      val &= ~MAC_XIO_SEL;
++      val |= SF_MAC_EN;
++
++      writel(val, regs + SNF_MAC_CTL);
++}
++
++static void snfi_mac_disable(struct nfi_base *nb)
++{
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      val = readl(regs + SNF_MAC_CTL);
++      val &= ~(SF_TRIG | SF_MAC_EN);
++      writel(val, regs + SNF_MAC_CTL);
++}
++
++static int snfi_mac_trigger(struct nfi_base *nb)
++{
++      void *regs = nb->res.nfi_regs;
++      int ret;
++      u32 val;
++
++      val = readl(regs + SNF_MAC_CTL);
++      val |= SF_TRIG;
++      writel(val, regs + SNF_MAC_CTL);
++
++      ret = readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val,
++                                      val & WIP_READY, 10,
++                                      NFI_TIMEOUT);
++      if (ret) {
++              pr_info("polling wip ready for read timeout\n");
++              return ret;
++      }
++
++      return readl_poll_timeout_atomic(regs + SNF_MAC_CTL, val,
++                                       !(val & WIP), 10,
++                                       NFI_TIMEOUT);
++}
++
++static int snfi_mac_op(struct nfi_base *nb)
++{
++      int ret;
++
++      snfi_mac_enable(nb);
++      ret = snfi_mac_trigger(nb);
++      snfi_mac_disable(nb);
++
++      return ret;
++}
++
++static void snfi_write_mac(struct nfi_spi *nfi_spi, u8 *data, int count)
++{
++      struct nandx_split32 split = {0};
++      u32 reg_offset = round_down(nfi_spi->tx_count, 4);
++      void *regs = nfi_spi->base.res.nfi_regs;
++      u32 data_offset = 0, i, val;
++      u8 *p_val = (u8 *)(&val);
++
++      nandx_split(&split, nfi_spi->tx_count, count, val, 4);
++
++      if (split.head_len) {
++              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
++
++              for (i = 0; i < split.head_len; i++)
++                      p_val[split.head + i] = data[i];
++
++              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
++      }
++
++      if (split.body_len) {
++              reg_offset = split.body;
++              data_offset = split.head_len;
++
++              for (i = 0; i < split.body_len; i++) {
++                      p_val[i & 3] = data[data_offset + i];
++
++                      if ((i & 3) == 3) {
++                              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
++                              reg_offset += 4;
++                      }
++              }
++      }
++
++      if (split.tail_len) {
++              reg_offset = split.tail;
++              data_offset += split.body_len;
++
++              for (i = 0; i < split.tail_len; i++) {
++                      p_val[i] = data[data_offset + i];
++
++                      if (i == split.tail_len - 1)
++                              writel(val, regs + SPI_GPRAM_ADDR + reg_offset);
++              }
++      }
++}
++
++static void snfi_read_mac(struct nfi_spi *nfi_spi, u8 *data, int count)
++{
++      void *regs = nfi_spi->base.res.nfi_regs;
++      u32 reg_offset = round_down(nfi_spi->tx_count, 4);
++      struct nandx_split32 split = {0};
++      u32 data_offset = 0, i, val;
++      u8 *p_val = (u8 *)&val;
++
++      nandx_split(&split, nfi_spi->tx_count, count, val, 4);
++
++      if (split.head_len) {
++              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
++
++              for (i = 0; i < split.head_len; i++)
++                      data[data_offset + i] = p_val[split.head + i];
++      }
++
++      if (split.body_len) {
++              reg_offset = split.body;
++              data_offset = split.head_len;
++
++              for (i = 0; i < split.body_len; i++) {
++                      if ((i & 3) == 0) {
++                              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
++                              reg_offset += 4;
++                      }
++
++                      data[data_offset + i] = p_val[i % 4];
++              }
++      }
++
++      if (split.tail_len) {
++              reg_offset = split.tail;
++              data_offset += split.body_len;
++              val = readl(regs + SPI_GPRAM_ADDR + reg_offset);
++
++              for (i = 0; i < split.tail_len; i++)
++                      data[data_offset + i] = p_val[i];
++      }
++}
++
++static int snfi_send_command(struct nfi *nfi, short cmd)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++
++      if (cmd == -1)
++              return 0;
++
++      if (nfi_spi->snfi_mode == SNFI_MAC_MODE) {
++              snfi_write_mac(nfi_spi, (u8 *)&cmd, 1);
++              nfi_spi->tx_count++;
++              return 0;
++      }
++
++      nfi_spi->cmd[nfi_spi->cur_cmd_idx++] = cmd;
++      return 0;
++}
++
++static int snfi_send_address(struct nfi *nfi, int col, int row,
++                           int col_cycle,
++                           int row_cycle)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      u32 addr, cycle, temp;
++
++      nb->col = col;
++      nb->row = row;
++
++      if (nfi_spi->snfi_mode == SNFI_MAC_MODE) {
++              addr = row;
++              cycle = row_cycle;
++
++              if (!row_cycle) {
++                      addr = col;
++                      cycle = col_cycle;
++              }
++
++              temp = nandx_cpu_to_be32(addr) >> ((4 - cycle) << 3);
++              snfi_write_mac(nfi_spi, (u8 *)&temp, cycle);
++              nfi_spi->tx_count += cycle;
++      }  else {
++              nfi_spi->row_addr[nfi_spi->cur_addr_idx++] = row;
++              nfi_spi->col_addr[nfi_spi->cur_addr_idx++] = col;
++      }
++
++      return 0;
++}
++
++static int snfi_trigger(struct nfi *nfi)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++
++      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
++      writel(0, regs + SNF_MAC_INL);
++
++      nfi_spi->tx_count = 0;
++      nfi_spi->cur_cmd_idx = 0;
++      nfi_spi->cur_addr_idx = 0;
++
++      return snfi_mac_op(nb);
++}
++
++static int snfi_select_chip(struct nfi *nfi, int cs)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      val = readl(regs + SNF_MISC_CTL);
++
++      if (cs == 0) {
++              val &= ~SF2CS_SEL;
++              val &= ~SF2CS_EN;
++      } else if (cs == 1) {
++              val |= SF2CS_SEL;
++              val |= SF2CS_EN;
++      } else {
++              return -EIO;
++      }
++
++      writel(val, regs + SNF_MISC_CTL);
++
++      return 0;
++}
++
++static int snfi_set_delay(struct nfi_base *nb, u8 delay_mode)
++{
++      void *regs = nb->res.nfi_regs;
++      struct nfi_spi_delay *delay;
++      u32 val;
++
++      if (delay_mode < 0 || delay_mode > SPI_NAND_MAX_DELAY)
++              return -EINVAL;
++
++      delay = &spi_delay[delay_mode];
++
++      val = delay->tIO_OUT_DLY[0] | delay->tIO_OUT_DLY[1] << 8 |
++            delay->tIO_OUT_DLY[2] << 16 |
++            delay->tIO_OUT_DLY[3] << 24;
++      writel(val, regs + SNF_DLY_CTL1);
++
++      val = delay->tIO_IN_DLY[0] | (delay->tIO_IN_DLY[1] << 8) |
++            delay->tIO_IN_DLY[2] << 16 |
++            delay->tIO_IN_DLY[3] << 24;
++      writel(val, regs + SNF_DLY_CTL2);
++
++      val = delay->tCLK_SAM_DLY | delay->tCLK_OUT_DLY << 8 |
++            delay->tCS_DLY << 16 |
++            delay->tWR_EN_DLY << 24;
++      writel(val, regs + SNF_DLY_CTL3);
++
++      writel(delay->tCS_DLY, regs + SNF_DLY_CTL4);
++
++      val = readl(regs + SNF_MISC_CTL);
++      val |= (delay->tREAD_LATCH_LATENCY) <<
++             LATCH_LAT_SHIFT;
++      writel(val, regs + SNF_MISC_CTL);
++
++      return 0;
++}
++
++static int snfi_set_timing(struct nfi *nfi, void *timing, int type)
++{
++      /* Nothing need to do. */
++      return 0;
++}
++
++static int snfi_wait_ready(struct nfi *nfi, int type, u32 timeout)
++{
++      /* Nothing need to do. */
++      return 0;
++}
++
++static int snfi_ctrl(struct nfi *nfi, int cmd, void *args)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      int ret = 0;
++
++      if (!args)
++              return -EINVAL;
++
++      switch (cmd) {
++      case NFI_CTRL_DMA:
++              nb->dma_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_NFI_IRQ:
++              nb->nfi_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_IRQ:
++              nb->ecc_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_PAGE_IRQ:
++              nb->page_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC:
++              nb->ecc_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_BAD_MARK_SWAP:
++              nb->bad_mark_swap_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_CLOCK:
++              nb->ecc_clk_en = *(bool *)args;
++              break;
++
++      case SNFI_CTRL_OP_MODE:
++              nfi_spi->snfi_mode = *(u8 *)args;
++              break;
++
++      case SNFI_CTRL_RX_MODE:
++              nfi_spi->read_cache_mode = *(u8 *)args;
++              break;
++
++      case SNFI_CTRL_TX_MODE:
++              nfi_spi->write_cache_mode = *(u8 *)args;
++              break;
++
++      case SNFI_CTRL_DELAY_MODE:
++              ret = snfi_set_delay(nb, *(u8 *)args);
++              break;
++
++      default:
++              pr_info("operation not support.\n");
++              ret = -EOPNOTSUPP;
++              break;
++      }
++
++      return ret;
++}
++
++static int snfi_read_bytes(struct nfi *nfi, u8 *data, int count)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++      int ret;
++
++      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
++      writel(count, regs + SNF_MAC_INL);
++
++      ret = snfi_mac_op(nb);
++      if (ret)
++              return ret;
++
++      snfi_read_mac(nfi_spi, data, count);
++
++      nfi_spi->tx_count = 0;
++
++      return 0;
++}
++
++static int snfi_write_bytes(struct nfi *nfi, u8 *data, int count)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++
++      snfi_write_mac(nfi_spi, data, count);
++      nfi_spi->tx_count += count;
++
++      writel(0, regs + SNF_MAC_INL);
++      writel(nfi_spi->tx_count, regs + SNF_MAC_OUTL);
++
++      nfi_spi->tx_count = 0;
++
++      return snfi_mac_op(nb);
++}
++
++static int snfi_reset(struct nfi *nfi)
++{
++      struct nfi_base *nb = nfi_to_base(nfi);
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++      int ret;
++
++      ret = nfi_spi->parent->nfi.reset(nfi);
++      if (ret)
++              return ret;
++
++      val = readl(regs + SNF_MISC_CTL);
++      val |= SW_RST;
++      writel(val, regs + SNF_MISC_CTL);
++
++      ret = readx_poll_timeout_atomic(readw, regs + SNF_STA_CTL1, val,
++                                      !(val & SPI_STATE), 50,
++                                      NFI_TIMEOUT);
++      if (ret) {
++              pr_info("spi state active in reset [0x%x] = 0x%x\n",
++                      SNF_STA_CTL1, val);
++              return ret;
++      }
++
++      val = readl(regs + SNF_MISC_CTL);
++      val &= ~SW_RST;
++      writel(val, regs + SNF_MISC_CTL);
++
++      return 0;
++}
++
++static int snfi_config_for_write(struct nfi_base *nb, int count)
++{
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      nb->set_op_mode(regs, CNFG_CUSTOM_MODE);
++
++      val = readl(regs + SNF_MISC_CTL);
++
++      if (nfi_spi->write_cache_mode == SNFI_TX_114)
++              val |= PG_LOAD_X4_EN;
++
++      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
++              val |= PG_LOAD_CUSTOM_EN;
++
++      writel(val, regs + SNF_MISC_CTL);
++
++      val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size);
++      writel(val << PG_LOAD_SHIFT, regs + SNF_MISC_CTL2);
++
++      val = readl(regs + SNF_PG_CTL1);
++
++      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
++              val |= nfi_spi->cmd[0] << PG_LOAD_CMD_SHIFT;
++      else {
++              val |= nfi_spi->cmd[0] | nfi_spi->cmd[1] << PG_LOAD_CMD_SHIFT |
++                     nfi_spi->cmd[2] << PG_EXE_CMD_SHIFT;
++
++              writel(nfi_spi->row_addr[1], regs + SNF_PG_CTL3);
++              writel(nfi_spi->cmd[3] << GF_CMD_SHIFT | nfi_spi->col_addr[2] <<
++                     GF_ADDR_SHIFT, regs + SNF_GF_CTL1);
++      }
++
++      writel(val, regs + SNF_PG_CTL1);
++      writel(nfi_spi->col_addr[1], regs + SNF_PG_CTL2);
++
++      writel(NFI_CMD_DUMMY_WR, regs + NFI_CMD);
++
++      return 0;
++}
++
++static int snfi_config_for_read(struct nfi_base *nb, int count)
++{
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++      int ret = 0;
++
++      nb->set_op_mode(regs, CNFG_CUSTOM_MODE);
++
++      val = readl(regs + SNF_MISC_CTL);
++      val &= ~DARA_READ_MODE_MASK;
++
++      switch (nfi_spi->read_cache_mode) {
++
++      case SNFI_RX_111:
++              break;
++
++      case SNFI_RX_112:
++              val |= X2_DATA_MODE << READ_MODE_SHIFT;
++              break;
++
++      case SNFI_RX_114:
++              val |= X4_DATA_MODE << READ_MODE_SHIFT;
++              break;
++
++      case SNFI_RX_122:
++              val |= DUAL_IO_MODE << READ_MODE_SHIFT;
++              break;
++
++      case SNFI_RX_144:
++              val |= QUAD_IO_MODE << READ_MODE_SHIFT;
++              break;
++
++      default:
++              pr_info("Not support this read operarion: %d!\n",
++                     nfi_spi->read_cache_mode);
++              ret = -EINVAL;
++              break;
++      }
++
++      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE)
++              val |= DATARD_CUSTOM_EN;
++
++      writel(val, regs + SNF_MISC_CTL);
++
++      val = count * (nb->nfi.sector_size + nb->nfi.sector_spare_size);
++      writel(val, regs + SNF_MISC_CTL2);
++
++      val = readl(regs + SNF_RD_CTL2);
++
++      if (nfi_spi->snfi_mode == SNFI_CUSTOM_MODE) {
++              val |= nfi_spi->cmd[0];
++              writel(nfi_spi->col_addr[1], regs + SNF_RD_CTL3);
++      } else {
++              val |= nfi_spi->cmd[2];
++              writel(nfi_spi->cmd[0] << PAGE_READ_CMD_SHIFT |
++                     nfi_spi->row_addr[0], regs + SNF_RD_CTL1);
++              writel(nfi_spi->cmd[1] << GF_CMD_SHIFT |
++                     nfi_spi->col_addr[1] << GF_ADDR_SHIFT,
++                     regs + SNF_GF_CTL1);
++              writel(nfi_spi->col_addr[2], regs + SNF_RD_CTL3);
++      }
++
++      writel(val, regs + SNF_RD_CTL2);
++
++      writel(NFI_CMD_DUMMY_RD, regs + NFI_CMD);
++
++      return ret;
++}
++
++static bool is_page_empty(struct nfi_base *nb, u8 *data, u8 *fdm,
++                        int sectors)
++{
++      u32 *data32 = (u32 *)data;
++      u32 *fdm32 = (u32 *)fdm;
++      u32 i, count = 0;
++
++      for (i = 0; i < nb->format.page_size >> 2; i++) {
++              if (data32[i] != 0xffff) {
++                      count += zero_popcount(data32[i]);
++                      if (count > 10) {
++                              pr_info("%s %d %d count:%d\n",
++                                      __func__, __LINE__, i, count);
++                              return false;
++                      }
++              }
++      }
++
++      if (fdm) {
++              for (i = 0; i < (nb->nfi.fdm_size * sectors >> 2); i++)
++              if (fdm32[i] != 0xffff) {
++                      count += zero_popcount(fdm32[i]);
++                      if (count > 10) {
++                              pr_info("%s %d %d count:%d\n",
++                                      __func__, __LINE__, i, count);
++                              return false;
++                      }
++              }
++      }
++
++      return true;
++}
++
++static int rw_prepare(struct nfi_base *nb, int sectors, u8 *data,
++                    u8 *fdm,
++                    bool read)
++{
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      int ret;
++
++      ret = nfi_spi->parent->rw_prepare(nb, sectors, data, fdm, read);
++      if (ret)
++              return ret;
++
++      if (read)
++              ret = snfi_config_for_read(nb, sectors);
++      else
++              ret = snfi_config_for_write(nb, sectors);
++
++      return ret;
++}
++
++static void rw_complete(struct nfi_base *nb, u8 *data, u8 *fdm,
++                      bool read)
++{
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++      void *regs = nb->res.nfi_regs;
++      u32 val;
++
++      nfi_spi->parent->rw_complete(nb, data, fdm, read);
++
++      val = readl(regs + SNF_MISC_CTL);
++
++      if (read)
++              val &= ~DATARD_CUSTOM_EN;
++      else
++              val &= ~PG_LOAD_CUSTOM_EN;
++
++      writel(val, regs + SNF_MISC_CTL);
++
++      nfi_spi->tx_count = 0;
++      nfi_spi->cur_cmd_idx = 0;
++      nfi_spi->cur_addr_idx = 0;
++}
++
++static void set_nfi_base_funcs(struct nfi_base *nb)
++{
++      nb->nfi.reset = snfi_reset;
++      nb->nfi.set_timing = snfi_set_timing;
++      nb->nfi.wait_ready = snfi_wait_ready;
++
++      nb->nfi.send_cmd = snfi_send_command;
++      nb->nfi.send_addr = snfi_send_address;
++      nb->nfi.trigger = snfi_trigger;
++      nb->nfi.nfi_ctrl = snfi_ctrl;
++      nb->nfi.select_chip = snfi_select_chip;
++
++      nb->nfi.read_bytes = snfi_read_bytes;
++      nb->nfi.write_bytes = snfi_write_bytes;
++
++      nb->rw_prepare = rw_prepare;
++      nb->rw_complete = rw_complete;
++      nb->is_page_empty = is_page_empty;
++
++}
++
++struct nfi *nfi_extend_init(struct nfi_base *nb)
++{
++      struct nfi_spi *nfi_spi;
++
++      nfi_spi = mem_alloc(1, sizeof(struct nfi_spi));
++      if (!nfi_spi) {
++              pr_info("snfi alloc memory fail @%s.\n", __func__);
++              return NULL;
++      }
++
++      memcpy(&nfi_spi->base, nb, sizeof(struct nfi_base));
++      nfi_spi->parent = nb;
++
++      nfi_spi->read_cache_mode = SNFI_RX_114;
++      nfi_spi->write_cache_mode = SNFI_TX_114;
++
++      set_nfi_base_funcs(&nfi_spi->base);
++
++      /* Change nfi to spi mode */
++      writel(SPI_MODE, nb->res.nfi_regs + SNF_SNF_CNFG);
++
++      return &(nfi_spi->base.nfi);
++}
++
++void nfi_extend_exit(struct nfi_base *nb)
++{
++      struct nfi_spi *nfi_spi = base_to_snfi(nb);
++
++      mem_free(nfi_spi->parent);
++      mem_free(nfi_spi);
++}
++
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi.h b/drivers/mtd/nandx/core/nfi/nfi_spi.h
+new file mode 100644
+index 0000000000..a52255663a
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_spi.h
+@@ -0,0 +1,44 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFI_SPI_H__
++#define __NFI_SPI_H__
++
++#define SPI_NAND_MAX_DELAY      6
++#define SPI_NAND_MAX_OP         4
++
++/*TODO - add comments */
++struct nfi_spi_delay {
++      u8 tCLK_SAM_DLY;
++      u8 tCLK_OUT_DLY;
++      u8 tCS_DLY;
++      u8 tWR_EN_DLY;
++      u8 tIO_IN_DLY[4];
++      u8 tIO_OUT_DLY[4];
++      u8 tREAD_LATCH_LATENCY;
++};
++
++/* SPI Nand structure */
++struct nfi_spi {
++      struct nfi_base base;
++      struct nfi_base *parent;
++
++      u8 snfi_mode;
++      u8 tx_count;
++
++      u8 cmd[SPI_NAND_MAX_OP];
++      u8 cur_cmd_idx;
++
++      u32 row_addr[SPI_NAND_MAX_OP];
++      u32 col_addr[SPI_NAND_MAX_OP];
++      u8 cur_addr_idx;
++
++      u8 read_cache_mode;
++      u8 write_cache_mode;
++};
++
++#endif /* __NFI_SPI_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
+new file mode 100644
+index 0000000000..77adf46782
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
+@@ -0,0 +1,64 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFI_SPI_REGS_H__
++#define __NFI_SPI_REGS_H__
++
++#define SNF_MAC_CTL             0x500
++#define         WIP                     BIT(0)
++#define         WIP_READY               BIT(1)
++#define         SF_TRIG                 BIT(2)
++#define         SF_MAC_EN               BIT(3)
++#define         MAC_XIO_SEL             BIT(4)
++#define SNF_MAC_OUTL            0x504
++#define SNF_MAC_INL             0x508
++#define SNF_RD_CTL1             0x50c
++#define         PAGE_READ_CMD_SHIFT     24
++#define SNF_RD_CTL2             0x510
++#define SNF_RD_CTL3             0x514
++#define SNF_GF_CTL1             0x518
++#define         GF_ADDR_SHIFT           16
++#define         GF_CMD_SHIFT            24
++#define SNF_GF_CTL3             0x520
++#define SNF_PG_CTL1             0x524
++#define         PG_EXE_CMD_SHIFT        16
++#define         PG_LOAD_CMD_SHIFT       8
++#define SNF_PG_CTL2             0x528
++#define SNF_PG_CTL3             0x52c
++#define SNF_ER_CTL              0x530
++#define SNF_ER_CTL2             0x534
++#define SNF_MISC_CTL            0x538
++#define         SW_RST                  BIT(28)
++#define         PG_LOAD_X4_EN           BIT(20)
++#define         X2_DATA_MODE            1
++#define         X4_DATA_MODE            2
++#define         DUAL_IO_MODE            5
++#define         QUAD_IO_MODE            6
++#define         READ_MODE_SHIFT         16
++#define         LATCH_LAT_SHIFT         8
++#define         LATCH_LAT_MASK          GENMASK(9, 8)
++#define         DARA_READ_MODE_MASK     GENMASK(18, 16)
++#define         SF2CS_SEL               BIT(13)
++#define         SF2CS_EN                BIT(12)
++#define         PG_LOAD_CUSTOM_EN       BIT(7)
++#define         DATARD_CUSTOM_EN        BIT(6)
++#define SNF_MISC_CTL2           0x53c
++#define         PG_LOAD_SHIFT           16
++#define SNF_DLY_CTL1            0x540
++#define SNF_DLY_CTL2            0x544
++#define SNF_DLY_CTL3            0x548
++#define SNF_DLY_CTL4            0x54c
++#define SNF_STA_CTL1            0x550
++#define         SPI_STATE               GENMASK(3, 0)
++#define SNF_STA_CTL2            0x554
++#define SNF_STA_CTL3            0x558
++#define SNF_SNF_CNFG            0x55c
++#define         SPI_MODE                BIT(0)
++#define SNF_DEBUG_SEL           0x560
++#define SPI_GPRAM_ADDR          0x800
++
++#endif /* __NFI_SPI_REGS_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi/nfiecc.c b/drivers/mtd/nandx/core/nfi/nfiecc.c
+new file mode 100644
+index 0000000000..14246fbc3e
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfiecc.c
+@@ -0,0 +1,510 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "nfiecc_regs.h"
++#include "nfiecc.h"
++
++#define NFIECC_IDLE_REG(op) \
++      ((op) == ECC_ENCODE ? NFIECC_ENCIDLE : NFIECC_DECIDLE)
++#define         IDLE_MASK       1
++#define NFIECC_CTL_REG(op) \
++      ((op) == ECC_ENCODE ? NFIECC_ENCCON : NFIECC_DECCON)
++#define NFIECC_IRQ_REG(op) \
++      ((op) == ECC_ENCODE ? NFIECC_ENCIRQEN : NFIECC_DECIRQEN)
++#define NFIECC_ADDR(op) \
++      ((op) == ECC_ENCODE ? NFIECC_ENCDIADDR : NFIECC_DECDIADDR)
++
++#define ECC_TIMEOUT     500000
++
++/* ecc strength that each IP supports */
++static const int ecc_strength_mt7622[] = {
++      4, 6, 8, 10, 12, 14, 16
++};
++
++static int nfiecc_irq_handler(void *data)
++{
++      struct nfiecc *ecc = data;
++      void *regs = ecc->res.regs;
++      u32 status;
++
++      status = readl(regs + NFIECC_DECIRQSTA) & DEC_IRQSTA_GEN;
++      if (status) {
++              status = readl(regs + NFIECC_DECDONE);
++              if (!(status & ecc->config.sectors))
++                      return NAND_IRQ_NONE;
++
++              /*
++               * Clear decode IRQ status once again to ensure that
++               * there will be no extra IRQ.
++               */
++              readl(regs + NFIECC_DECIRQSTA);
++              ecc->config.sectors = 0;
++              nandx_event_complete(ecc->done);
++      } else {
++              status = readl(regs + NFIECC_ENCIRQSTA) & ENC_IRQSTA_GEN;
++              if (!status)
++                      return NAND_IRQ_NONE;
++
++              nandx_event_complete(ecc->done);
++      }
++
++      return NAND_IRQ_HANDLED;
++}
++
++static inline int nfiecc_wait_idle(struct nfiecc *ecc)
++{
++      int op = ecc->config.op;
++      int ret, val;
++
++      ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_IDLE_REG(op),
++                                      val, val & IDLE_MASK,
++                                      10, ECC_TIMEOUT);
++      if (ret)
++              pr_info("%s not idle\n",
++                      op == ECC_ENCODE ? "encoder" : "decoder");
++
++      return ret;
++}
++
++static int nfiecc_wait_encode_done(struct nfiecc *ecc)
++{
++      int ret, val;
++
++      if (ecc->ecc_irq_en) {
++              /* poll one time to avoid missing irq event */
++              ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA,
++                                              val, val & ENC_FSM_IDLE, 1, 1);
++              if (!ret)
++                      return 0;
++
++              /* irq done, if not, we can go on to poll status for a while */
++              ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT);
++              if (ret)
++                      return 0;
++      }
++
++      ret = readl_poll_timeout_atomic(ecc->res.regs + NFIECC_ENCSTA,
++                                      val, val & ENC_FSM_IDLE,
++                                      10, ECC_TIMEOUT);
++      if (ret)
++              pr_info("encode timeout\n");
++
++      return ret;
++
++}
++
++static int nfiecc_wait_decode_done(struct nfiecc *ecc)
++{
++      u32 secbit = BIT(ecc->config.sectors - 1);
++      void *regs = ecc->res.regs;
++      int ret, val;
++
++      if (ecc->ecc_irq_en) {
++              ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE,
++                                              val, val & secbit, 1, 1);
++              if (!ret)
++                      return 0;
++
++              ret = nandx_event_wait_complete(ecc->done, ECC_TIMEOUT);
++              if (ret)
++                      return 0;
++      }
++
++      ret = readl_poll_timeout_atomic(regs + NFIECC_DECDONE,
++                                      val, val & secbit,
++                                      10, ECC_TIMEOUT);
++      if (ret) {
++              pr_info("decode timeout\n");
++              return ret;
++      }
++
++      /* decode done does not stands for ecc all work done.
++       * we need check syn, bma, chien, autoc all idle.
++       * just check it when ECC_DECCNFG[13:12] is 3,
++       * which means auto correct.
++       */
++      ret = readl_poll_timeout_atomic(regs + NFIECC_DECFSM,
++                                      val, (val & FSM_MASK) == FSM_IDLE,
++                                      10, ECC_TIMEOUT);
++      if (ret)
++              pr_info("decode fsm(0x%x) is not idle\n",
++                     readl(regs + NFIECC_DECFSM));
++
++      return ret;
++}
++
++static int nfiecc_wait_done(struct nfiecc *ecc)
++{
++      if (ecc->config.op == ECC_ENCODE)
++              return nfiecc_wait_encode_done(ecc);
++
++      return nfiecc_wait_decode_done(ecc);
++}
++
++static void nfiecc_encode_config(struct nfiecc *ecc, u32 ecc_idx)
++{
++      struct nfiecc_config *config = &ecc->config;
++      u32 val;
++
++      val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift);
++
++      if (config->mode == ECC_DMA_MODE)
++              val |= ENC_BURST_EN;
++
++      val |= (config->len << 3) << ENCCNFG_MS_SHIFT;
++      writel(val, ecc->res.regs + NFIECC_ENCCNFG);
++}
++
++static void nfiecc_decode_config(struct nfiecc *ecc, u32 ecc_idx)
++{
++      struct nfiecc_config *config = &ecc->config;
++      u32 dec_sz = (config->len << 3) +
++                   config->strength * ecc->caps->parity_bits;
++      u32 val;
++
++      val = ecc_idx | (config->mode << ecc->caps->ecc_mode_shift);
++
++      if (config->mode == ECC_DMA_MODE)
++              val |= DEC_BURST_EN;
++
++      val |= (dec_sz << DECCNFG_MS_SHIFT) |
++             (config->deccon << DEC_CON_SHIFT);
++      val |= DEC_EMPTY_EN;
++      writel(val, ecc->res.regs + NFIECC_DECCNFG);
++}
++
++static void nfiecc_config(struct nfiecc *ecc)
++{
++      u32 idx;
++
++      for (idx = 0; idx < ecc->caps->ecc_strength_num; idx++) {
++              if (ecc->config.strength == ecc->caps->ecc_strength[idx])
++                      break;
++      }
++
++      if (ecc->config.op == ECC_ENCODE)
++              nfiecc_encode_config(ecc, idx);
++      else
++              nfiecc_decode_config(ecc, idx);
++}
++
++static int nfiecc_enable(struct nfiecc *ecc)
++{
++      enum nfiecc_operation op = ecc->config.op;
++      void *regs = ecc->res.regs;
++
++      nfiecc_config(ecc);
++
++      writel(ECC_OP_EN, regs + NFIECC_CTL_REG(op));
++
++      if (ecc->ecc_irq_en) {
++              writel(ECC_IRQEN, regs + NFIECC_IRQ_REG(op));
++
++              if (ecc->page_irq_en)
++                      writel(ECC_IRQEN | ECC_PG_IRQ_SEL,
++                             regs + NFIECC_IRQ_REG(op));
++
++              nandx_event_init(ecc->done);
++      }
++
++      return 0;
++}
++
++static int nfiecc_disable(struct nfiecc *ecc)
++{
++      enum nfiecc_operation op = ecc->config.op;
++      void *regs = ecc->res.regs;
++
++      nfiecc_wait_idle(ecc);
++
++      writel(0, regs + NFIECC_IRQ_REG(op));
++      writel(~ECC_OP_EN, regs + NFIECC_CTL_REG(op));
++
++      return 0;
++}
++
++static int nfiecc_correct_data(struct nfiecc *ecc,
++                             struct nfiecc_status *status,
++                             u8 *data, u32 sector)
++{
++      u32 err, offset, i;
++      u32 loc, byteloc, bitloc;
++
++      status->corrected = 0;
++      status->failed = 0;
++
++      offset = (sector >> 2);
++      err = readl(ecc->res.regs + NFIECC_DECENUM(offset));
++      err >>= (sector % 4) * 8;
++      err &= ecc->caps->err_mask;
++
++      if (err == ecc->caps->err_mask) {
++              status->failed++;
++              return -ENANDREAD;
++      }
++
++      status->corrected += err;
++      status->bitflips = max_t(u32, status->bitflips, err);
++
++      for (i = 0; i < err; i++) {
++              loc = readl(ecc->res.regs + NFIECC_DECEL(i >> 1));
++              loc >>= ((i & 0x1) << 4);
++              byteloc = loc >> 3;
++              bitloc = loc & 0x7;
++              data[byteloc] ^= (1 << bitloc);
++      }
++
++      return 0;
++}
++
++static int nfiecc_fill_data(struct nfiecc *ecc, u8 *data)
++{
++      struct nfiecc_config *config = &ecc->config;
++      void *regs = ecc->res.regs;
++      int size, ret, i;
++      u32 val;
++
++      if (config->mode == ECC_DMA_MODE) {
++              if ((unsigned long)config->dma_addr & 0x3)
++                      pr_info("encode address is not 4B aligned: 0x%x\n",
++                             (u32)(unsigned long)config->dma_addr);
++
++              writel((unsigned long)config->dma_addr,
++                     regs + NFIECC_ADDR(config->op));
++      } else if (config->mode == ECC_PIO_MODE) {
++              if (config->op == ECC_ENCODE) {
++                      size = (config->len + 3) >> 2;
++              } else {
++                      size = config->strength * ecc->caps->parity_bits;
++                      size = (size + 7) >> 3;
++                      size += config->len;
++                      size >>= 2;
++              }
++
++              for (i = 0; i < size; i++) {
++                      ret = readl_poll_timeout_atomic(regs + NFIECC_PIO_DIRDY,
++                                                      val, val & PIO_DI_RDY,
++                                                      10, ECC_TIMEOUT);
++                      if (ret)
++                              return ret;
++
++                      writel(*((u32 *)data + i), regs + NFIECC_PIO_DI);
++              }
++      }
++
++      return 0;
++}
++
++static int nfiecc_encode(struct nfiecc *ecc, u8 *data)
++{
++      struct nfiecc_config *config = &ecc->config;
++      u32 len, i, val = 0;
++      u8 *p;
++      int ret;
++
++      /* Under NFI mode, nothing need to do */
++      if (config->mode == ECC_NFI_MODE)
++              return 0;
++
++      ret = nfiecc_fill_data(ecc, data);
++      if (ret)
++              return ret;
++
++      ret = nfiecc_wait_encode_done(ecc);
++      if (ret)
++              return ret;
++
++      ret = nfiecc_wait_idle(ecc);
++      if (ret)
++              return ret;
++
++      /* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
++      len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
++      p = data + config->len;
++
++      /* Write the parity bytes generated by the ECC back to the OOB region */
++      for (i = 0; i < len; i++) {
++              if ((i % 4) == 0)
++                      val = readl(ecc->res.regs + NFIECC_ENCPAR(i / 4));
++
++              p[i] = (val >> ((i % 4) * 8)) & 0xff;
++      }
++
++      return 0;
++}
++
++static int nfiecc_decode(struct nfiecc *ecc, u8 *data)
++{
++      int ret;
++
++      /* Under NFI mode, nothing need to do */
++      if (ecc->config.mode == ECC_NFI_MODE)
++              return 0;
++
++      ret = nfiecc_fill_data(ecc, data);
++      if (ret)
++              return ret;
++
++      return nfiecc_wait_decode_done(ecc);
++}
++
++static int nfiecc_decode_status(struct nfiecc *ecc, u32 start_sector,
++                              u32 sectors)
++{
++      void *regs = ecc->res.regs;
++      u32 i, val = 0, err;
++      u32 bitflips = 0;
++
++      for (i = start_sector; i < start_sector + sectors; i++) {
++              if ((i % 4) == 0)
++                      val = readl(regs + NFIECC_DECENUM(i / 4));
++
++              err = val >> ((i % 4) * 5);
++              err &= ecc->caps->err_mask;
++
++              if (err == ecc->caps->err_mask)
++                      pr_err("sector %d is uncorrect\n", i);
++
++              bitflips = max_t(u32, bitflips, err);
++      }
++
++      if (bitflips == ecc->caps->err_mask)
++              return -ENANDREAD;
++
++      if (bitflips)
++              pr_info("bitflips %d is corrected\n", bitflips);
++
++      return bitflips;
++}
++
++static int nfiecc_adjust_strength(struct nfiecc *ecc, int strength)
++{
++      struct nfiecc_caps *caps = ecc->caps;
++      int i, count = caps->ecc_strength_num;
++
++      if (strength >= caps->ecc_strength[count - 1])
++              return caps->ecc_strength[count - 1];
++
++      if (strength < caps->ecc_strength[0])
++              return -EINVAL;
++
++      for (i = 1; i < count; i++) {
++              if (strength < caps->ecc_strength[i])
++                      return caps->ecc_strength[i - 1];
++      }
++
++      return -EINVAL;
++}
++
++static int nfiecc_ctrl(struct nfiecc *ecc, int cmd, void *args)
++{
++      int ret = 0;
++
++      switch (cmd) {
++      case NFI_CTRL_ECC_IRQ:
++              ecc->ecc_irq_en = *(bool *)args;
++              break;
++
++      case NFI_CTRL_ECC_PAGE_IRQ:
++              ecc->page_irq_en = *(bool *)args;
++              break;
++
++      default:
++              pr_info("invalid arguments.\n");
++              ret = -EINVAL;
++              break;
++      }
++
++      return ret;
++}
++
++static int nfiecc_hw_init(struct nfiecc *ecc)
++{
++      int ret;
++
++      ret = nfiecc_wait_idle(ecc);
++      if (ret)
++              return ret;
++
++      writel(~ECC_OP_EN, ecc->res.regs + NFIECC_ENCCON);
++
++      ret = nfiecc_wait_idle(ecc);
++      if (ret)
++              return ret;
++
++      writel(~ECC_OP_EN, ecc->res.regs + NFIECC_DECCON);
++
++      return 0;
++}
++
++static struct nfiecc_caps nfiecc_caps_mt7622 = {
++      .err_mask = 0x1f,
++      .ecc_mode_shift = 4,
++      .parity_bits = 13,
++      .ecc_strength = ecc_strength_mt7622,
++      .ecc_strength_num = 7,
++};
++
++static struct nfiecc_caps *nfiecc_get_match_data(enum mtk_ic_version ic)
++{
++      /* NOTE: add other IC's data */
++      return &nfiecc_caps_mt7622;
++}
++
++struct nfiecc *nfiecc_init(struct nfiecc_resource *res)
++{
++      struct nfiecc *ecc;
++      int ret;
++
++      ecc = mem_alloc(1, sizeof(struct nfiecc));
++      if (!ecc)
++              return NULL;
++
++      ecc->res = *res;
++
++      ret = nandx_irq_register(res->dev, res->irq_id, nfiecc_irq_handler,
++                               "mtk-ecc", ecc);
++      if (ret) {
++              pr_info("ecc irq register failed!\n");
++              goto error;
++      }
++
++      ecc->ecc_irq_en = false;
++      ecc->page_irq_en = false;
++      ecc->done = nandx_event_create();
++      ecc->caps = nfiecc_get_match_data(res->ic_ver);
++
++      ecc->adjust_strength = nfiecc_adjust_strength;
++      ecc->enable = nfiecc_enable;
++      ecc->disable = nfiecc_disable;
++      ecc->decode = nfiecc_decode;
++      ecc->encode = nfiecc_encode;
++      ecc->wait_done = nfiecc_wait_done;
++      ecc->decode_status = nfiecc_decode_status;
++      ecc->correct_data = nfiecc_correct_data;
++      ecc->nfiecc_ctrl = nfiecc_ctrl;
++
++      ret = nfiecc_hw_init(ecc);
++      if (ret)
++              return NULL;
++
++      return ecc;
++
++error:
++      mem_free(ecc);
++
++      return NULL;
++}
++
++void nfiecc_exit(struct nfiecc *ecc)
++{
++      nandx_event_destroy(ecc->done);
++      mem_free(ecc);
++}
++
+diff --git a/drivers/mtd/nandx/core/nfi/nfiecc.h b/drivers/mtd/nandx/core/nfi/nfiecc.h
+new file mode 100644
+index 0000000000..b02a5c3534
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfiecc.h
+@@ -0,0 +1,90 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFIECC_H__
++#define __NFIECC_H__
++
++enum nfiecc_mode {
++      ECC_DMA_MODE,
++      ECC_NFI_MODE,
++      ECC_PIO_MODE
++};
++
++enum nfiecc_operation {
++      ECC_ENCODE,
++      ECC_DECODE
++};
++
++enum nfiecc_deccon {
++      ECC_DEC_FER = 1,
++      ECC_DEC_LOCATE = 2,
++      ECC_DEC_CORRECT = 3
++};
++
++struct nfiecc_resource {
++      int ic_ver;
++      void *dev;
++      void *regs;
++      int irq_id;
++
++};
++
++struct nfiecc_status {
++      u32 corrected;
++      u32 failed;
++      u32 bitflips;
++};
++
++struct nfiecc_caps {
++      u32 err_mask;
++      u32 ecc_mode_shift;
++      u32 parity_bits;
++      const int *ecc_strength;
++      u32 ecc_strength_num;
++};
++
++struct nfiecc_config {
++      enum nfiecc_operation op;
++      enum nfiecc_mode mode;
++      enum nfiecc_deccon deccon;
++
++      void *dma_addr; /* DMA use only */
++      u32 strength;
++      u32 sectors;
++      u32 len;
++};
++
++struct nfiecc {
++      struct nfiecc_resource res;
++      struct nfiecc_config config;
++      struct nfiecc_caps *caps;
++
++      bool ecc_irq_en;
++      bool page_irq_en;
++
++      void *done;
++
++      int (*adjust_strength)(struct nfiecc *ecc, int strength);
++      int (*enable)(struct nfiecc *ecc);
++      int (*disable)(struct nfiecc *ecc);
++
++      int (*decode)(struct nfiecc *ecc, u8 *data);
++      int (*encode)(struct nfiecc *ecc, u8 *data);
++
++      int (*decode_status)(struct nfiecc *ecc, u32 start_sector, u32 sectors);
++      int (*correct_data)(struct nfiecc *ecc,
++                          struct nfiecc_status *status,
++                          u8 *data, u32 sector);
++      int (*wait_done)(struct nfiecc *ecc);
++
++      int (*nfiecc_ctrl)(struct nfiecc *ecc, int cmd, void *args);
++};
++
++struct nfiecc *nfiecc_init(struct nfiecc_resource *res);
++void nfiecc_exit(struct nfiecc *ecc);
++
++#endif /* __NFIECC_H__ */
+diff --git a/drivers/mtd/nandx/core/nfi/nfiecc_regs.h b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h
+new file mode 100644
+index 0000000000..96564cf872
+--- /dev/null
++++ b/drivers/mtd/nandx/core/nfi/nfiecc_regs.h
+@@ -0,0 +1,51 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NFIECC_REGS_H__
++#define __NFIECC_REGS_H__
++
++#define NFIECC_ENCCON           0x000
++/* NFIECC_DECCON has same bit define */
++#define         ECC_OP_EN               BIT(0)
++#define NFIECC_ENCCNFG          0x004
++#define         ENCCNFG_MS_SHIFT        16
++#define         ENC_BURST_EN            BIT(8)
++#define NFIECC_ENCDIADDR        0x008
++#define NFIECC_ENCIDLE          0x00c
++#define NFIECC_ENCSTA           0x02c
++#define         ENC_FSM_IDLE            1
++#define NFIECC_ENCIRQEN         0x030
++/* NFIECC_DECIRQEN has same bit define */
++#define         ECC_IRQEN               BIT(0)
++#define         ECC_PG_IRQ_SEL          BIT(1)
++#define NFIECC_ENCIRQSTA        0x034
++#define         ENC_IRQSTA_GEN          BIT(0)
++#define NFIECC_PIO_DIRDY        0x080
++#define         PIO_DI_RDY              BIT(0)
++#define NFIECC_PIO_DI           0x084
++#define NFIECC_DECCON           0x100
++#define NFIECC_DECCNFG          0x104
++#define         DEC_BURST_EN            BIT(8)
++#define         DEC_EMPTY_EN            BIT(31)
++#define         DEC_CON_SHIFT           12
++#define         DECCNFG_MS_SHIFT        16
++#define NFIECC_DECDIADDR        0x108
++#define NFIECC_DECIDLE          0x10c
++#define NFIECC_DECENUM(x)       (0x114 + (x) * 4)
++#define NFIECC_DECDONE          0x11c
++#define NFIECC_DECIRQEN         0x140
++#define NFIECC_DECIRQSTA        0x144
++#define         DEC_IRQSTA_GEN          BIT(0)
++#define NFIECC_DECFSM           0x14c
++#define         FSM_MASK                0x7f0f0f0f
++#define         FSM_IDLE                0x01010101
++#define NFIECC_BYPASS           0x20c
++#define         NFIECC_BYPASS_EN        BIT(0)
++#define NFIECC_ENCPAR(x)        (0x010 + (x) * 4)
++#define NFIECC_DECEL(x)         (0x120 + (x) * 4)
++
++#endif /* __NFIECC_REGS_H__ */
+diff --git a/drivers/mtd/nandx/driver/Nandx.mk b/drivers/mtd/nandx/driver/Nandx.mk
+new file mode 100644
+index 0000000000..3fb93d37c5
+--- /dev/null
++++ b/drivers/mtd/nandx/driver/Nandx.mk
+@@ -0,0 +1,18 @@
++#
++# Copyright (C) 2017 MediaTek Inc.
++# Licensed under either
++#     BSD Licence, (see NOTICE for more details)
++#     GNU General Public License, version 2.0, (see NOTICE for more details)
++#
++
++nandx-$(NANDX_SIMULATOR_SUPPORT) += simulator/driver.c
++
++nandx-$(NANDX_CTP_SUPPORT) += ctp/ts_nand.c
++nandx-$(NANDX_CTP_SUPPORT) += ctp/nand_test.c
++nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nand_test.h
++
++nandx-$(NANDX_BBT_SUPPORT) += bbt/bbt.c
++nandx-$(NANDX_BROM_SUPPORT) += brom/driver.c
++nandx-$(NANDX_KERNEL_SUPPORT) += kernel/driver.c
++nandx-$(NANDX_LK_SUPPORT) += lk/driver.c
++nandx-$(NANDX_UBOOT_SUPPORT) += uboot/driver.c
+diff --git a/drivers/mtd/nandx/driver/bbt/bbt.c b/drivers/mtd/nandx/driver/bbt/bbt.c
+new file mode 100644
+index 0000000000..c9d4823e09
+--- /dev/null
++++ b/drivers/mtd/nandx/driver/bbt/bbt.c
+@@ -0,0 +1,408 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include "nandx_util.h"
++#include "nandx_core.h"
++#include "bbt.h"
++
++/* Not support: multi-chip */
++static u8 main_bbt_pattern[] = {'B', 'b', 't', '0' };
++static u8 mirror_bbt_pattern[] = {'1', 't', 'b', 'B' };
++
++static struct bbt_manager g_bbt_manager = {
++      {       {{main_bbt_pattern, 4}, 0, BBT_INVALID_ADDR},
++              {{mirror_bbt_pattern, 4}, 0, BBT_INVALID_ADDR}
++      },
++      NAND_BBT_SCAN_MAXBLOCKS, NULL
++};
++
++static inline void set_bbt_mark(u8 *bbt, int block, u8 mark)
++{
++      int index, offset;
++
++      index = GET_ENTRY(block);
++      offset = GET_POSITION(block);
++
++      bbt[index] &= ~(BBT_ENTRY_MASK << offset);
++      bbt[index] |= (mark & BBT_ENTRY_MASK) << offset;
++      pr_info("%s %d block:%d, bbt[%d]:0x%x, offset:%d, mark:%d\n",
++              __func__, __LINE__, block, index, bbt[index], offset, mark);
++}
++
++static inline u8 get_bbt_mark(u8 *bbt, int block)
++{
++      int offset = GET_POSITION(block);
++      int index = GET_ENTRY(block);
++      u8 value = bbt[index];
++
++      return (value >> offset) & BBT_ENTRY_MASK;
++}
++
++static void mark_nand_bad(struct nandx_info *nand, int block)
++{
++      u8 *buf;
++
++      buf = mem_alloc(1, nand->page_size + nand->oob_size);
++      if (!buf) {
++              pr_info("%s, %d, memory alloc fail, pagesize:%d, oobsize:%d\n",
++                     __func__, __LINE__, nand->page_size, nand->oob_size);
++              return;
++      }
++      memset(buf, 0, nand->page_size + nand->oob_size);
++      nandx_erase(block * nand->block_size, nand->block_size);
++      nandx_write(buf, buf + nand->page_size, block * nand->block_size,
++                  nand->page_size);
++      mem_free(buf);
++}
++
++static inline bool is_bbt_data(u8 *buf, struct bbt_pattern *pattern)
++{
++      int i;
++
++      for (i = 0; i < pattern->len; i++) {
++              if (buf[i] != pattern->data[i])
++                      return false;
++      }
++
++      return true;
++}
++
++static u64 get_bbt_address(struct nandx_info *nand, u8 *bbt,
++                         u64 mirror_addr,
++                         int max_blocks)
++{
++      u64 addr, end_addr;
++      u8 mark;
++
++      addr = nand->total_size;
++      end_addr = nand->total_size - nand->block_size * max_blocks;
++
++      while (addr > end_addr) {
++              addr -= nand->block_size;
++              mark = get_bbt_mark(bbt, div_down(addr, nand->block_size));
++
++              if (mark == BBT_BLOCK_WORN || mark == BBT_BLOCK_FACTORY_BAD)
++                      continue;
++              if (addr != mirror_addr)
++                      return addr;
++      }
++
++      return BBT_INVALID_ADDR;
++}
++
++static int read_bbt(struct bbt_desc *desc, u8 *bbt, u32 len)
++{
++      int ret;
++
++      ret = nandx_read(bbt, NULL, desc->bbt_addr + desc->pattern.len + 1,
++                       len);
++      if (ret < 0)
++              pr_info("nand_bbt: error reading BBT page, ret:-%x\n", ret);
++
++      return ret;
++}
++
++static void create_bbt(struct nandx_info *nand, u8 *bbt)
++{
++      u32 offset = 0, block = 0;
++
++      do {
++              if (nandx_is_bad_block(offset)) {
++                      pr_info("Create bbt at bad block:%d\n", block);
++                      set_bbt_mark(bbt, block, BBT_BLOCK_FACTORY_BAD);
++              }
++              block++;
++              offset += nand->block_size;
++      } while (offset < nand->total_size);
++}
++
++static int search_bbt(struct nandx_info *nand, struct bbt_desc *desc,
++                    int max_blocks)
++{
++      u64 addr, end_addr;
++      u8 *buf;
++      int ret;
++
++      buf = mem_alloc(1, nand->page_size);
++      if (!buf) {
++              pr_info("%s, %d, mem alloc fail!!! len:%d\n",
++                     __func__, __LINE__, nand->page_size);
++              return -ENOMEM;
++      }
++
++      addr = nand->total_size;
++      end_addr = nand->total_size - max_blocks * nand->block_size;
++      while (addr > end_addr) {
++              addr -= nand->block_size;
++
++              nandx_read(buf, NULL, addr, nand->page_size);
++
++              if (is_bbt_data(buf, &desc->pattern)) {
++                      desc->bbt_addr = addr;
++                      desc->version = buf[desc->pattern.len];
++                      pr_info("BBT is found at addr 0x%llx, version %d\n",
++                              desc->bbt_addr, desc->version);
++                      ret = 0;
++                      break;
++              }
++              ret = -EFAULT;
++      }
++
++      mem_free(buf);
++      return ret;
++}
++
++static int save_bbt(struct nandx_info *nand, struct bbt_desc *desc,
++                  u8 *bbt)
++{
++      u32 page_size_mask, total_block;
++      int write_len;
++      u8 *buf;
++      int ret;
++
++      ret = nandx_erase(desc->bbt_addr, nand->block_size);
++      if (ret) {
++              pr_info("erase addr 0x%llx fail !!!, ret %d\n",
++                      desc->bbt_addr, ret);
++              return ret;
++      }
++
++      total_block = div_down(nand->total_size, nand->block_size);
++      write_len = GET_BBT_LENGTH(total_block) + desc->pattern.len + 1;
++      page_size_mask = nand->page_size - 1;
++      write_len = (write_len + page_size_mask) & (~page_size_mask);
++
++      buf = (u8 *)mem_alloc(1, write_len);
++      if (!buf) {
++              pr_info("%s, %d, mem alloc fail!!! len:%d\n",
++                     __func__, __LINE__, write_len);
++              return -ENOMEM;
++      }
++      memset(buf, 0xFF, write_len);
++
++      memcpy(buf, desc->pattern.data, desc->pattern.len);
++      buf[desc->pattern.len] = desc->version;
++
++      memcpy(buf + desc->pattern.len + 1, bbt, GET_BBT_LENGTH(total_block));
++
++      ret = nandx_write(buf, NULL, desc->bbt_addr, write_len);
++
++      if (ret)
++              pr_info("nandx_write fail(%d), offset:0x%llx, len(%d)\n",
++                     ret, desc->bbt_addr, write_len);
++      mem_free(buf);
++
++      return ret;
++}
++
++static int write_bbt(struct nandx_info *nand, struct bbt_desc *main,
++                   struct bbt_desc *mirror, u8 *bbt, int max_blocks)
++{
++      int block;
++      int ret;
++
++      do {
++              if (main->bbt_addr == BBT_INVALID_ADDR) {
++                      main->bbt_addr = get_bbt_address(nand, bbt,
++                                       mirror->bbt_addr, max_blocks);
++                      if (main->bbt_addr == BBT_INVALID_ADDR)
++                              return -ENOSPC;
++              }
++
++              ret = save_bbt(nand, main, bbt);
++              if (!ret)
++                      break;
++
++              block = div_down(main->bbt_addr, nand->block_size);
++              set_bbt_mark(bbt, block, BBT_BLOCK_WORN);
++              main->version++;
++              mark_nand_bad(nand, block);
++              main->bbt_addr = BBT_INVALID_ADDR;
++      } while (1);
++
++      return 0;
++}
++
++static void mark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks)
++{
++      int total_block;
++      int block;
++      u8 mark;
++
++      total_block = div_down(nand->total_size, nand->block_size);
++      block = total_block - bbt_blocks;
++
++      while (bbt_blocks) {
++              mark = get_bbt_mark(bbt, block);
++              if (mark == BBT_BLOCK_GOOD)
++                      set_bbt_mark(bbt, block, BBT_BLOCK_RESERVED);
++              block++;
++              bbt_blocks--;
++      }
++}
++
++static void unmark_bbt_region(struct nandx_info *nand, u8 *bbt, int bbt_blocks)
++{
++      int total_block;
++      int block;
++      u8 mark;
++
++      total_block = div_down(nand->total_size, nand->block_size);
++      block = total_block - bbt_blocks;
++
++      while (bbt_blocks) {
++              mark = get_bbt_mark(bbt, block);
++              if (mark == BBT_BLOCK_RESERVED)
++                      set_bbt_mark(bbt, block, BBT_BLOCK_GOOD);
++              block++;
++              bbt_blocks--;
++      }
++}
++
++static int update_bbt(struct nandx_info *nand, struct bbt_desc *desc,
++                    u8 *bbt,
++                    int max_blocks)
++{
++      int ret = 0, i;
++
++      /* The reserved info is not stored in NAND*/
++      unmark_bbt_region(nand, bbt, max_blocks);
++
++      desc[0].version++;
++      for (i = 0; i < 2; i++) {
++              if (i > 0)
++                      desc[i].version = desc[i - 1].version;
++
++              ret = write_bbt(nand, &desc[i], &desc[1 - i], bbt, max_blocks);
++              if (ret)
++                      break;
++      }
++      mark_bbt_region(nand, bbt, max_blocks);
++
++      return ret;
++}
++
++int scan_bbt(struct nandx_info *nand)
++{
++      struct bbt_manager *manager = &g_bbt_manager;
++      struct bbt_desc *pdesc;
++      int total_block, len, i;
++      int valid_desc = 0;
++      int ret = 0;
++      u8 *bbt;
++
++      total_block = div_down(nand->total_size, nand->block_size);
++      len = GET_BBT_LENGTH(total_block);
++
++      if (!manager->bbt) {
++              manager->bbt = (u8 *)mem_alloc(1, len);
++              if (!manager->bbt) {
++                      pr_info("%s, %d, mem alloc fail!!! len:%d\n",
++                             __func__, __LINE__, len);
++                      return -ENOMEM;
++              }
++      }
++      bbt = manager->bbt;
++      memset(bbt, 0xFF, len);
++
++      /* scan bbt */
++      for (i = 0; i < 2; i++) {
++              pdesc = &manager->desc[i];
++              pdesc->bbt_addr = BBT_INVALID_ADDR;
++              pdesc->version = 0;
++              ret = search_bbt(nand, pdesc, manager->max_blocks);
++              if (!ret && (pdesc->bbt_addr != BBT_INVALID_ADDR))
++                      valid_desc += 1 << i;
++      }
++
++      pdesc = &manager->desc[0];
++      if ((valid_desc == 0x3) && (pdesc[0].version != pdesc[1].version))
++              valid_desc = (pdesc[0].version > pdesc[1].version) ? 1 : 2;
++
++      /* read bbt */
++      for (i = 0; i < 2; i++) {
++              if (!(valid_desc & (1 << i)))
++                      continue;
++              ret = read_bbt(&pdesc[i], bbt, len);
++              if (ret) {
++                      pdesc->bbt_addr = BBT_INVALID_ADDR;
++                      pdesc->version = 0;
++                      valid_desc &= ~(1 << i);
++              }
++              /* If two BBT version is same, only need to read the first bbt*/
++              if ((valid_desc == 0x3) &&
++                  (pdesc[0].version == pdesc[1].version))
++                      break;
++      }
++
++      if (!valid_desc) {
++              create_bbt(nand, bbt);
++              pdesc[0].version = 1;
++              pdesc[1].version = 1;
++      }
++
++      pdesc[0].version = max_t(u8, pdesc[0].version, pdesc[1].version);
++      pdesc[1].version = pdesc[0].version;
++
++      for (i = 0; i < 2; i++) {
++              if (valid_desc & (1 << i))
++                      continue;
++
++              ret = write_bbt(nand, &pdesc[i], &pdesc[1 - i], bbt,
++                              manager->max_blocks);
++              if (ret) {
++                      pr_info("write bbt(%d) fail, ret:%d\n", i, ret);
++                      manager->bbt = NULL;
++                      return ret;
++              }
++      }
++
++      /* Prevent the bbt regions from erasing / writing */
++      mark_bbt_region(nand, manager->bbt, manager->max_blocks);
++
++      for (i = 0; i < total_block; i++) {
++              if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_WORN)
++                      pr_info("Checked WORN bad blk: %d\n", i);
++              else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_FACTORY_BAD)
++                      pr_info("Checked Factory bad blk: %d\n", i);
++              else if (get_bbt_mark(manager->bbt, i) == BBT_BLOCK_RESERVED)
++                      pr_info("Checked Reserved blk: %d\n", i);
++              else if (get_bbt_mark(manager->bbt, i) != BBT_BLOCK_GOOD)
++                      pr_info("Checked unknown blk: %d\n", i);
++      }
++
++      return 0;
++}
++
++int bbt_mark_bad(struct nandx_info *nand, off_t offset)
++{
++      struct bbt_manager *manager = &g_bbt_manager;
++      int block = div_down(offset, nand->block_size);
++      int ret = 0;
++
++      mark_nand_bad(nand, block);
++
++#if 0
++      set_bbt_mark(manager->bbt, block, BBT_BLOCK_WORN);
++
++      /* Update flash-based bad block table */
++      ret = update_bbt(nand, manager->desc, manager->bbt,
++                       manager->max_blocks);
++#endif
++      pr_info("block %d, update result %d.\n", block, ret);
++
++      return ret;
++}
++
++int bbt_is_bad(struct nandx_info *nand, off_t offset)
++{
++      int block;
++
++      block = div_down(offset, nand->block_size);
++
++      return get_bbt_mark(g_bbt_manager.bbt, block) != BBT_BLOCK_GOOD;
++}
+diff --git a/drivers/mtd/nandx/driver/uboot/driver.c b/drivers/mtd/nandx/driver/uboot/driver.c
+new file mode 100644
+index 0000000000..7bd3342452
+--- /dev/null
++++ b/drivers/mtd/nandx/driver/uboot/driver.c
+@@ -0,0 +1,574 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#include <common.h>
++#include <linux/io.h>
++#include <dm.h>
++#include <clk.h>
++#include <nand.h>
++#include <linux/iopoll.h>
++#include <linux/delay.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++#include "nandx_core.h"
++#include "nandx_util.h"
++#include "bbt.h"
++
++typedef int (*func_nandx_operation)(u8 *, u8 *, u64, size_t);
++
++struct nandx_clk {
++      struct clk *nfi_clk;
++      struct clk *ecc_clk;
++      struct clk *snfi_clk;
++      struct clk *snfi_clk_sel;
++      struct clk *snfi_parent_50m;
++};
++
++struct nandx_nfc {
++      struct nandx_info info;
++      struct nandx_clk clk;
++      struct nfi_resource *res;
++
++      struct nand_chip *nand;
++      spinlock_t lock;
++};
++
++/* Default flash layout for MTK nand controller
++ * 64Bytes oob format.
++ */
++static struct nand_ecclayout eccoob = {
++      .eccbytes = 42,
++      .eccpos = {
++              17, 18, 19, 20, 21, 22, 23, 24, 25,
++              26, 27, 28, 29, 30, 31, 32, 33, 34,
++              35, 36, 37, 38, 39, 40, 41
++      },
++      .oobavail = 16,
++      .oobfree = {
++                      {
++                      .offset = 0,
++                      .length = 16,
++                      },
++      }
++};
++
++static struct nandx_nfc *mtd_to_nfc(struct mtd_info *mtd)
++{
++      struct nand_chip *nand = mtd_to_nand(mtd);
++
++      return (struct nandx_nfc *)nand_get_controller_data(nand);
++}
++
++static int nandx_enable_clk(struct nandx_clk *clk)
++{
++      int ret;
++
++      ret = clk_enable(clk->nfi_clk);
++      if (ret) {
++              pr_info("failed to enable nfi clk\n");
++              return ret;
++      }
++
++      ret = clk_enable(clk->ecc_clk);
++      if (ret) {
++              pr_info("failed to enable ecc clk\n");
++              goto disable_nfi_clk;
++      }
++
++      ret = clk_enable(clk->snfi_clk);
++      if (ret) {
++              pr_info("failed to enable snfi clk\n");
++              goto disable_ecc_clk;
++      }
++
++      ret = clk_enable(clk->snfi_clk_sel);
++      if (ret) {
++              pr_info("failed to enable snfi clk sel\n");
++              goto disable_snfi_clk;
++      }
++
++      ret = clk_set_parent(clk->snfi_clk_sel, clk->snfi_parent_50m);
++      if (ret) {
++              pr_info("failed to set snfi parent 50MHz\n");
++              goto disable_snfi_clk;
++      }
++
++      return 0;
++
++disable_snfi_clk:
++      clk_disable(clk->snfi_clk);
++disable_ecc_clk:
++      clk_disable(clk->ecc_clk);
++disable_nfi_clk:
++      clk_disable(clk->nfi_clk);
++
++      return ret;
++}
++
++static void nandx_disable_clk(struct nandx_clk *clk)
++{
++      clk_disable(clk->ecc_clk);
++      clk_disable(clk->nfi_clk);
++      clk_disable(clk->snfi_clk);
++}
++
++static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section,
++                                struct mtd_oob_region *oob_region)
++{
++      struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
++      u32 eccsteps;
++
++      eccsteps = div_down(mtd->writesize, mtd->ecc_step_size);
++
++      if (section >= eccsteps)
++              return -EINVAL;
++
++      oob_region->length = nfc->info.fdm_reg_size - nfc->info.fdm_ecc_size;
++      oob_region->offset = section * nfc->info.fdm_reg_size
++              + nfc->info.fdm_ecc_size;
++
++      return 0;
++}
++
++static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
++                               struct mtd_oob_region *oob_region)
++{
++      struct nandx_nfc *nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
++      u32 eccsteps;
++
++      if (section)
++              return -EINVAL;
++
++      eccsteps = div_down(mtd->writesize, mtd->ecc_step_size);
++      oob_region->offset = nfc->info.fdm_reg_size * eccsteps;
++      oob_region->length = mtd->oobsize - oob_region->offset;
++
++      return 0;
++}
++
++static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = {
++      .rfree = mtk_nfc_ooblayout_free,
++      .ecc = mtk_nfc_ooblayout_ecc,
++};
++
++struct nfc_compatible {
++      enum mtk_ic_version ic_ver;
++
++      u32 clock_1x;
++      u32 *clock_2x;
++      int clock_2x_num;
++
++      int min_oob_req;
++};
++
++static const struct nfc_compatible nfc_compats_mt7622 = {
++      .ic_ver = NANDX_MT7622,
++      .clock_1x = 26000000,
++      .clock_2x = NULL,
++      .clock_2x_num = 8,
++      .min_oob_req = 1,
++};
++
++static const struct udevice_id ic_of_match[] = {
++      {.compatible = "mediatek,mt7622-nfc", .data = &nfc_compats_mt7622},
++      {}
++};
++
++static int nand_operation(struct mtd_info *mtd, loff_t addr, size_t len,
++            size_t *retlen, uint8_t *data, uint8_t *oob, bool read)
++{
++      struct nandx_split64 split = {0};
++      func_nandx_operation operation;
++      u64 block_oobs, val, align;
++      uint8_t *databuf, *oobbuf;
++      struct nandx_nfc *nfc;
++      bool readoob;
++      int ret = 0;
++
++      nfc = (struct nandx_nfc *)nand_get_controller_data;
++      spin_lock(&nfc->lock);
++
++      databuf = data;
++      oobbuf = oob;
++
++      readoob = data ? false : true;
++      block_oobs = div_up(mtd->erasesize, mtd->writesize) * mtd->oobavail;
++      align = readoob ? block_oobs : mtd->erasesize;
++
++      operation = read ? nandx_read : nandx_write;
++
++      nandx_split(&split, addr, len, val, align);
++
++      if (split.head_len) {
++              ret = operation((u8 *) databuf, oobbuf, addr, split.head_len);
++
++              if (databuf)
++                      databuf += split.head_len;
++
++              if (oobbuf)
++                      oobbuf += split.head_len;
++
++              addr += split.head_len;
++              *retlen += split.head_len;
++      }
++
++      if (split.body_len) {
++              while (div_up(split.body_len, align)) {
++                      ret = operation((u8 *) databuf, oobbuf, addr, align);
++
++                      if (databuf) {
++                              databuf += mtd->erasesize;
++                              split.body_len -= mtd->erasesize;
++                              *retlen += mtd->erasesize;
++                      }
++
++                      if (oobbuf) {
++                              oobbuf += block_oobs;
++                              split.body_len -= block_oobs;
++                              *retlen += block_oobs;
++                      }
++
++                      addr += mtd->erasesize;
++              }
++
++      }
++
++      if (split.tail_len) {
++              ret = operation((u8 *) databuf, oobbuf, addr, split.tail_len);
++              *retlen += split.tail_len;
++      }
++
++      spin_unlock(&nfc->lock);
++
++      return ret;
++}
++
++static int mtk_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
++            size_t *retlen, u_char *buf)
++{
++      return nand_operation(mtd, from, len, retlen, buf, NULL, true);
++}
++
++static int mtk_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
++             size_t *retlen, const u_char *buf)
++{
++      return nand_operation(mtd, to, len, retlen, (uint8_t *)buf,
++              NULL, false);
++}
++
++int mtk_nand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
++{
++      size_t retlen;
++
++      return nand_operation(mtd, from, ops->ooblen, &retlen, NULL,
++              ops->oobbuf, true);
++}
++
++int mtk_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
++{
++      size_t retlen;
++
++      return nand_operation(mtd, to, ops->ooblen, &retlen, NULL,
++              ops->oobbuf, false);
++}
++
++static int mtk_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++      struct nandx_nfc *nfc;
++      u64 erase_len, erase_addr;
++      u32 block_size;
++      int ret = 0;
++
++      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
++      block_size = nfc->info.block_size;
++      erase_len = instr->len;
++      erase_addr = instr->addr;
++      spin_lock(&nfc->lock);
++      instr->state = MTD_ERASING;
++
++      while (erase_len) {
++              if (mtk_nand_is_bad(mtd, erase_addr)) {
++                      pr_info("block(0x%llx) is bad, not erase\n",
++                              erase_addr);
++                      instr->state = MTD_ERASE_FAILED;
++                      goto erase_exit;
++              } else {
++                      ret = nandx_erase(erase_addr, block_size);
++                      if (ret < 0) {
++                              instr->state = MTD_ERASE_FAILED;
++                              goto erase_exit;
++                              pr_info("erase fail at blk %llu, ret:%d\n",
++                                      erase_addr, ret);
++                      }
++              }
++              erase_addr += block_size;
++              erase_len -= block_size;
++      }
++
++      instr->state = MTD_ERASE_DONE;
++
++erase_exit:
++      ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
++
++      spin_unlock(&nfc->lock);
++      /* Do mtd call back function */
++      if (!ret)
++              mtd_erase_callback(instr);
++
++      return ret;
++}
++
++int mtk_nand_is_bad(struct mtd_info *mtd, loff_t ofs)
++{
++      struct nandx_nfc *nfc;
++      int ret;
++
++      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
++      spin_lock(&nfc->lock);
++
++      /*ret = bbt_is_bad(&nfc->info, ofs);*/
++      ret = nandx_is_bad_block(ofs);
++      spin_unlock(&nfc->lock);
++
++      if (ret) {
++              pr_info("nand block 0x%x is bad, ret %d!\n", ofs, ret);
++              return 1;
++      } else {
++              return 0;
++      }
++}
++
++int mtk_nand_mark_bad(struct mtd_info *mtd, loff_t ofs)
++{
++      struct nandx_nfc *nfc;
++      int ret;
++
++      nfc = (struct nandx_nfc *)mtd_to_nfc(mtd);
++      spin_lock(&nfc->lock);
++      pr_info("%s, %d\n", __func__, __LINE__);
++      ret = bbt_mark_bad(&nfc->info, ofs);
++
++      spin_unlock(&nfc->lock);
++
++      return ret;
++}
++
++void mtk_nand_sync(struct mtd_info *mtd)
++{
++      nandx_sync();
++}
++
++static struct mtd_info *mtd_info_create(struct udevice *pdev,
++              struct nandx_nfc *nfc, struct nand_chip *nand)
++{
++      struct mtd_info *mtd = nand_to_mtd(nand);
++      int ret;
++
++      nand_set_controller_data(nand, nfc);
++
++      nand->flash_node = dev_of_offset(pdev);
++      nand->ecc.layout = &eccoob;
++
++      ret = nandx_ioctl(CORE_CTRL_NAND_INFO, &nfc->info);
++      if (ret) {
++              pr_info("fail to get nand info (%d)!\n", ret);
++              mem_free(mtd);
++              return NULL;
++      }
++
++      mtd->owner = THIS_MODULE;
++
++      mtd->name = "MTK-SNand";
++      mtd->writesize = nfc->info.page_size;
++      mtd->erasesize = nfc->info.block_size;
++      mtd->oobsize = nfc->info.oob_size;
++      mtd->size = nfc->info.total_size;
++      mtd->type = MTD_NANDFLASH;
++      mtd->flags = MTD_CAP_NANDFLASH;
++      mtd->_erase = mtk_nand_erase;
++      mtd->_read = mtk_nand_read;
++      mtd->_write = mtk_nand_write;
++      mtd->_read_oob = mtk_nand_read_oob;
++      mtd->_write_oob = mtk_nand_write_oob;
++      mtd->_sync = mtk_nand_sync;
++      mtd->_lock = NULL;
++      mtd->_unlock = NULL;
++      mtd->_block_isbad = mtk_nand_is_bad;
++      mtd->_block_markbad = mtk_nand_mark_bad;
++      mtd->writebufsize = mtd->writesize;
++
++      mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops);
++
++      mtd->ecc_strength = nfc->info.ecc_strength;
++      mtd->ecc_step_size = nfc->info.sector_size;
++
++      if (!mtd->bitflip_threshold)
++              mtd->bitflip_threshold = mtd->ecc_strength;
++
++      return mtd;
++}
++
++int board_nand_init(struct nand_chip *nand)
++{
++      struct udevice *dev;
++      struct mtd_info *mtd;
++      struct nandx_nfc *nfc;
++      int arg = 1;
++      int ret;
++
++      ret = uclass_get_device_by_driver(UCLASS_MTD,
++                                        DM_GET_DRIVER(mtk_snand_drv),
++                                        &dev);
++      if (ret) {
++              pr_err("Failed to get mtk_nand_drv. (error %d)\n", ret);
++              return ret;
++      }
++
++      nfc = dev_get_priv(dev);
++
++      ret = nandx_enable_clk(&nfc->clk);
++      if (ret) {
++              pr_err("failed to enable nfi clk (error %d)\n", ret);
++              return ret;
++      }
++
++      ret = nandx_init(nfc->res);
++      if (ret) {
++              pr_err("nandx init error (%d)!\n", ret);
++              goto disable_clk;
++      }
++
++      arg = 1;
++      nandx_ioctl(NFI_CTRL_DMA, &arg);
++      nandx_ioctl(NFI_CTRL_ECC, &arg);
++
++#ifdef NANDX_UNIT_TEST
++      nandx_unit_test(0x780000, 0x800);
++#endif
++
++      mtd = mtd_info_create(dev, nfc, nand);
++      if (!mtd) {
++              ret = -ENOMEM;
++              goto disable_clk;
++      }
++
++      spin_lock_init(&nfc->lock);
++#if 0
++      ret = scan_bbt(&nfc->info);
++      if (ret) {
++              pr_info("bbt init error (%d)!\n", ret);
++              goto disable_clk;
++      }
++#endif
++      return ret;
++
++disable_clk:
++      nandx_disable_clk(&nfc->clk);
++
++      return ret;
++}
++
++static int mtk_snand_ofdata_to_platdata(struct udevice *dev)
++{
++      struct nandx_nfc *nfc = dev_get_priv(dev);
++      struct nfc_compatible *compat;
++      struct nfi_resource *res;
++
++      int ret = 0;
++
++      res = mem_alloc(1, sizeof(struct nfi_resource));
++      if (!res)
++              return -ENOMEM;
++
++      nfc->res = res;
++
++      res->nfi_regs = (void *)dev_read_addr_index(dev, 0);
++      res->ecc_regs = (void *)dev_read_addr_index(dev, 1);
++      pr_debug("mtk snand nfi_regs:0x%x ecc_regs:0x%x\n",
++              res->nfi_regs, res->ecc_regs);
++
++      compat = (struct nfc_compatible *)dev_get_driver_data(dev);
++
++      res->ic_ver = (enum mtk_ic_version)(compat->ic_ver);
++      res->clock_1x = compat->clock_1x;
++      res->clock_2x = compat->clock_2x;
++      res->clock_2x_num = compat->clock_2x_num;
++
++      memset(&nfc->clk, 0, sizeof(struct nandx_clk));
++      nfc->clk.nfi_clk =
++          kmalloc(sizeof(*nfc->clk.nfi_clk), GFP_KERNEL);
++      nfc->clk.ecc_clk =
++          kmalloc(sizeof(*nfc->clk.ecc_clk), GFP_KERNEL);
++      nfc->clk.snfi_clk=
++          kmalloc(sizeof(*nfc->clk.snfi_clk), GFP_KERNEL);
++      nfc->clk.snfi_clk_sel =
++          kmalloc(sizeof(*nfc->clk.snfi_clk_sel), GFP_KERNEL);
++      nfc->clk.snfi_parent_50m =
++          kmalloc(sizeof(*nfc->clk.snfi_parent_50m), GFP_KERNEL);
++
++      if (!nfc->clk.nfi_clk || !nfc->clk.ecc_clk || !nfc->clk.snfi_clk ||
++              !nfc->clk.snfi_clk_sel || !nfc->clk.snfi_parent_50m) {
++              ret = -ENOMEM;
++              goto err;
++      }
++
++      ret = clk_get_by_name(dev, "nfi_clk", nfc->clk.nfi_clk);
++      if (IS_ERR(nfc->clk.nfi_clk)) {
++              ret = PTR_ERR(nfc->clk.nfi_clk);
++              goto err;
++      }
++
++      ret = clk_get_by_name(dev, "ecc_clk", nfc->clk.ecc_clk);
++      if (IS_ERR(nfc->clk.ecc_clk)) {
++              ret = PTR_ERR(nfc->clk.ecc_clk);
++              goto err;
++      }
++
++      ret = clk_get_by_name(dev, "snfi_clk", nfc->clk.snfi_clk);
++      if (IS_ERR(nfc->clk.snfi_clk)) {
++              ret = PTR_ERR(nfc->clk.snfi_clk);
++              goto err;
++      }
++
++      ret = clk_get_by_name(dev, "spinfi_sel", nfc->clk.snfi_clk_sel);
++      if (IS_ERR(nfc->clk.snfi_clk_sel)) {
++              ret = PTR_ERR(nfc->clk.snfi_clk_sel);
++              goto err;
++      }
++
++      ret = clk_get_by_name(dev, "spinfi_parent_50m", nfc->clk.snfi_parent_50m);
++      if (IS_ERR(nfc->clk.snfi_parent_50m))
++              pr_info("spinfi parent 50MHz is not configed\n");
++
++      return 0;
++err:
++      if (nfc->clk.nfi_clk)
++              kfree(nfc->clk.nfi_clk);
++      if (nfc->clk.snfi_clk)
++              kfree(nfc->clk.snfi_clk);
++      if (nfc->clk.ecc_clk)
++              kfree(nfc->clk.ecc_clk);
++      if (nfc->clk.snfi_clk_sel)
++              kfree(nfc->clk.snfi_clk_sel);
++      if (nfc->clk.snfi_parent_50m)
++              kfree(nfc->clk.snfi_parent_50m);
++
++      return ret;
++}
++
++U_BOOT_DRIVER(mtk_snand_drv) = {
++      .name = "mtk_snand",
++      .id = UCLASS_MTD,
++      .of_match = ic_of_match,
++      .ofdata_to_platdata = mtk_snand_ofdata_to_platdata,
++      .priv_auto_alloc_size = sizeof(struct nandx_nfc),
++};
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
++MODULE_AUTHOR("MediaTek");
+diff --git a/drivers/mtd/nandx/include/Nandx.mk b/drivers/mtd/nandx/include/Nandx.mk
+new file mode 100644
+index 0000000000..667402790e
+--- /dev/null
++++ b/drivers/mtd/nandx/include/Nandx.mk
+@@ -0,0 +1,16 @@
++#
++# Copyright (C) 2017 MediaTek Inc.
++# Licensed under either
++#     BSD Licence, (see NOTICE for more details)
++#     GNU General Public License, version 2.0, (see NOTICE for more details)
++#
++
++nandx-header-y += internal/nandx_core.h
++nandx-header-y += internal/nandx_errno.h
++nandx-header-y += internal/nandx_util.h
++nandx-header-$(NANDX_BBT_SUPPORT) += internal/bbt.h
++nandx-header-$(NANDX_SIMULATOR_SUPPORT) += simulator/nandx_os.h
++nandx-header-$(NANDX_CTP_SUPPORT) += ctp/nandx_os.h
++nandx-header-$(NANDX_LK_SUPPORT) += lk/nandx_os.h
++nandx-header-$(NANDX_KERNEL_SUPPORT) += kernel/nandx_os.h
++nandx-header-$(NANDX_UBOOT_SUPPORT) += uboot/nandx_os.h
+diff --git a/drivers/mtd/nandx/include/internal/bbt.h b/drivers/mtd/nandx/include/internal/bbt.h
+new file mode 100644
+index 0000000000..4676def1f5
+--- /dev/null
++++ b/drivers/mtd/nandx/include/internal/bbt.h
+@@ -0,0 +1,62 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __BBT_H__
++#define __BBT_H__
++
++#define BBT_BLOCK_GOOD      0x03
++#define BBT_BLOCK_WORN      0x02
++#define BBT_BLOCK_RESERVED      0x01
++#define BBT_BLOCK_FACTORY_BAD   0x00
++
++#define BBT_INVALID_ADDR 0
++/* The maximum number of blocks to scan for a bbt */
++#define NAND_BBT_SCAN_MAXBLOCKS 4
++#define NAND_BBT_USE_FLASH  0x00020000
++#define NAND_BBT_NO_OOB     0x00040000
++
++/* Search good / bad pattern on the first and the second page */
++#define NAND_BBT_SCAN2NDPAGE    0x00008000
++/* Search good / bad pattern on the last page of the eraseblock */
++#define NAND_BBT_SCANLASTPAGE   0x00010000
++
++#define NAND_DRAM_BUF_DATABUF_ADDR  (NAND_BUF_ADDR)
++
++struct bbt_pattern {
++      u8 *data;
++      int len;
++};
++
++struct bbt_desc {
++      struct bbt_pattern pattern;
++      u8 version;
++      u64 bbt_addr;/*0: invalid value; otherwise, valid value*/
++};
++
++struct bbt_manager {
++      /* main bbt descriptor and mirror descriptor */
++      struct bbt_desc desc[2];/* 0: main bbt; 1: mirror bbt */
++      int max_blocks;
++      u8 *bbt;
++};
++
++#define BBT_ENTRY_MASK      0x03
++#define BBT_ENTRY_SHIFT     2
++
++#define GET_BBT_LENGTH(blocks) (blocks >> 2)
++#define GET_ENTRY(block) ((block) >> BBT_ENTRY_SHIFT)
++#define GET_POSITION(block) (((block) & BBT_ENTRY_MASK) * 2)
++#define GET_MARK_VALUE(block, mark) \
++      (((mark) & BBT_ENTRY_MASK) << GET_POSITION(block))
++
++int scan_bbt(struct nandx_info *nand);
++
++int bbt_mark_bad(struct nandx_info *nand, off_t offset);
++
++int bbt_is_bad(struct nandx_info *nand, off_t offset);
++
++#endif /*__BBT_H__*/
+diff --git a/drivers/mtd/nandx/include/internal/nandx_core.h b/drivers/mtd/nandx/include/internal/nandx_core.h
+new file mode 100644
+index 0000000000..09aff72224
+--- /dev/null
++++ b/drivers/mtd/nandx/include/internal/nandx_core.h
+@@ -0,0 +1,250 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NANDX_CORE_H__
++#define __NANDX_CORE_H__
++
++/**
++ * mtk_ic_version - indicates specifical IC, IP need this to load some info
++ */
++enum mtk_ic_version {
++      NANDX_MT7622,
++};
++
++/**
++ * nandx_ioctl_cmd - operations supported by nandx
++ *
++ * @NFI_CTRL_DMA dma enable or not
++ * @NFI_CTRL_NFI_MODE customer/read/program/erase...
++ * @NFI_CTRL_ECC ecc enable or not
++ * @NFI_CTRL_ECC_MODE nfi/dma/pio
++ * @CHIP_CTRL_DRIVE_STRENGTH enum chip_ctrl_drive_strength
++ */
++enum nandx_ctrl_cmd {
++      CORE_CTRL_NAND_INFO,
++
++      NFI_CTRL_DMA,
++      NFI_CTRL_NFI_MODE,
++      NFI_CTRL_AUTOFORMAT,
++      NFI_CTRL_NFI_IRQ,
++      NFI_CTRL_PAGE_IRQ,
++      NFI_CTRL_RANDOMIZE,
++      NFI_CTRL_BAD_MARK_SWAP,
++
++      NFI_CTRL_ECC,
++      NFI_CTRL_ECC_MODE,
++      NFI_CTRL_ECC_CLOCK,
++      NFI_CTRL_ECC_IRQ,
++      NFI_CTRL_ECC_PAGE_IRQ,
++      NFI_CTRL_ECC_DECODE_MODE,
++
++      SNFI_CTRL_OP_MODE,
++      SNFI_CTRL_RX_MODE,
++      SNFI_CTRL_TX_MODE,
++      SNFI_CTRL_DELAY_MODE,
++
++      CHIP_CTRL_OPS_CACHE,
++      CHIP_CTRL_OPS_MULTI,
++      CHIP_CTRL_PSLC_MODE,
++      CHIP_CTRL_DRIVE_STRENGTH,
++      CHIP_CTRL_DDR_MODE,
++      CHIP_CTRL_ONDIE_ECC,
++      CHIP_CTRL_TIMING_MODE
++};
++
++enum snfi_ctrl_op_mode {
++      SNFI_CUSTOM_MODE,
++      SNFI_AUTO_MODE,
++      SNFI_MAC_MODE
++};
++
++enum snfi_ctrl_rx_mode {
++      SNFI_RX_111,
++      SNFI_RX_112,
++      SNFI_RX_114,
++      SNFI_RX_122,
++      SNFI_RX_144
++};
++
++enum snfi_ctrl_tx_mode {
++      SNFI_TX_111,
++      SNFI_TX_114,
++};
++
++enum chip_ctrl_drive_strength {
++      CHIP_DRIVE_NORMAL,
++      CHIP_DRIVE_HIGH,
++      CHIP_DRIVE_MIDDLE,
++      CHIP_DRIVE_LOW
++};
++
++enum chip_ctrl_timing_mode {
++      CHIP_TIMING_MODE0,
++      CHIP_TIMING_MODE1,
++      CHIP_TIMING_MODE2,
++      CHIP_TIMING_MODE3,
++      CHIP_TIMING_MODE4,
++      CHIP_TIMING_MODE5,
++};
++
++/**
++ * nandx_info - basic information
++ */
++struct nandx_info {
++      u32 max_io_count;
++      u32 min_write_pages;
++      u32 plane_num;
++      u32 oob_size;
++      u32 page_parity_size;
++      u32 page_size;
++      u32 block_size;
++      u64 total_size;
++      u32 fdm_reg_size;
++      u32 fdm_ecc_size;
++      u32 ecc_strength;
++      u32 sector_size;
++};
++
++/**
++ * nfi_resource - the resource needed by nfi & ecc to do initialization
++ */
++struct nfi_resource {
++      int ic_ver;
++      void *dev;
++
++      void *ecc_regs;
++      int ecc_irq_id;
++
++      void *nfi_regs;
++      int nfi_irq_id;
++
++      u32 clock_1x;
++      u32 *clock_2x;
++      int clock_2x_num;
++
++      int min_oob_req;
++};
++
++/**
++ * nandx_init - init all related modules below
++ *
++ * @res: basic resource of the project
++ *
++ * return 0 if init success, otherwise return negative error code
++ */
++int nandx_init(struct nfi_resource *res);
++
++/**
++ * nandx_exit - release resource those that obtained in init flow
++ */
++void nandx_exit(void);
++
++/**
++ * nandx_read - read data from nand this function can read data and related
++ *   oob from specifical address
++ *   if do multi_ops, set one operation per time, and call nandx_sync at last
++ *   in multi mode, not support page partial read
++ *   oob not support partial read
++ *
++ * @data: buf to receive data from nand
++ * @oob: buf to receive oob data from nand which related to data page
++ *   length of @oob should oob size aligned, oob not support partial read
++ * @offset: offset address on the whole flash
++ * @len: the length of @data that need to read
++ *
++ * if read success return 0, otherwise return negative error code
++ */
++int nandx_read(u8 *data, u8 *oob, u64 offset, size_t len);
++
++/**
++ * nandx_write -  write data to nand
++ *   this function can write data and related oob to specifical address
++ *   if do multi_ops, set one operation per time, and call nandx_sync at last
++ *
++ * @data: source data to be written to nand,
++ *   for multi operation, the length of @data should be page size aliged
++ * @oob: source oob which related to data page to be written to nand,
++ *   length of @oob should oob size aligned
++ * @offset: offset address on the whole flash, the value should be start address
++ *   of a page
++ * @len: the length of @data that need to write,
++ *   for multi operation, the len should be page size aliged
++ *
++ * if write success return 0, otherwise return negative error code
++ * if return value > 0, it indicates that how many pages still need to write,
++ * and data has not been written to nand
++ * please call nandx_sync after pages alligned $nandx_info.min_write_pages
++ */
++int nandx_write(u8 *data, u8 *oob, u64 offset, size_t len);
++
++/**
++ * nandx_erase - erase an area of nand
++ *   if do multi_ops, set one operation per time, and call nandx_sync at last
++ *
++ * @offset: offset address on the flash
++ * @len: erase length which should be block size aligned
++ *
++ * if erase success return 0, otherwise return negative error code
++ */
++int nandx_erase(u64 offset, size_t len);
++
++/**
++ * nandx_sync - sync all operations to nand
++ *   when do multi_ops, this function will be called at last operation
++ *   when write data, if number of pages not alligned
++ *   by $nandx_info.min_write_pages, this interface could be called to do
++ *   force write, 0xff will be padded to blanked pages.
++ */
++int nandx_sync(void);
++
++/**
++ * nandx_is_bad_block - check if the block is bad
++ *   only check the flag that marked by the flash vendor
++ *
++ * @offset: offset address on the whole flash
++ *
++ * return true if the block is bad, otherwise return false
++ */
++bool nandx_is_bad_block(u64 offset);
++
++/**
++ * nandx_ioctl - set/get property of nand chip
++ *
++ * @cmd: parameter that defined in enum nandx_ioctl_cmd
++ * @arg: operate parameter
++ *
++ * return 0 if operate success, otherwise return negative error code
++ */
++int nandx_ioctl(int cmd, void *arg);
++
++/**
++ * nandx_suspend - suspend nand, and store some data
++ *
++ * return 0 if suspend success, otherwise return negative error code
++ */
++int nandx_suspend(void);
++
++/**
++ * nandx_resume - resume nand, and replay some data
++ *
++ * return 0 if resume success, otherwise return negative error code
++ */
++int nandx_resume(void);
++
++#ifdef NANDX_UNIT_TEST
++/**
++ * nandx_unit_test - unit test
++ *
++ * @offset: offset address on the whole flash
++ * @len: should be not larger than a block size, we only test a block per time
++ *
++ * return 0 if test success, otherwise return negative error code
++ */
++int nandx_unit_test(u64 offset, size_t len);
++#endif
++
++#endif /* __NANDX_CORE_H__ */
+diff --git a/drivers/mtd/nandx/include/internal/nandx_errno.h b/drivers/mtd/nandx/include/internal/nandx_errno.h
+new file mode 100644
+index 0000000000..51fb299c03
+--- /dev/null
++++ b/drivers/mtd/nandx/include/internal/nandx_errno.h
+@@ -0,0 +1,40 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NANDX_ERRNO_H__
++#define __NANDX_ERRNO_H__
++
++#ifndef EIO
++#define EIO             5       /* I/O error */
++#define ENOMEM          12      /* Out of memory */
++#define EFAULT          14      /* Bad address */
++#define EBUSY           16      /* Device or resource busy */
++#define ENODEV          19      /* No such device */
++#define EINVAL          22      /* Invalid argument */
++#define ENOSPC          28      /* No space left on device */
++/* Operation not supported on transport endpoint */
++#define EOPNOTSUPP      95
++#define ETIMEDOUT       110     /* Connection timed out */
++#endif
++
++#define ENANDFLIPS      1024    /* Too many bitflips, uncorrected */
++#define ENANDREAD       1025    /* Read fail, can't correct */
++#define ENANDWRITE      1026    /* Write fail */
++#define ENANDERASE      1027    /* Erase fail */
++#define ENANDBAD        1028    /* Bad block */
++#define ENANDWP         1029
++
++#define IS_NAND_ERR(err)        ((err) >= -ENANDBAD && (err) <= -ENANDFLIPS)
++
++#ifndef MAX_ERRNO
++#define MAX_ERRNO       4096
++#define ERR_PTR(errno)  ((void *)((long)errno))
++#define PTR_ERR(ptr)    ((long)(ptr))
++#define IS_ERR(ptr)     ((unsigned long)(ptr) > (unsigned long)-MAX_ERRNO)
++#endif
++
++#endif /* __NANDX_ERRNO_H__ */
+diff --git a/drivers/mtd/nandx/include/internal/nandx_util.h b/drivers/mtd/nandx/include/internal/nandx_util.h
+new file mode 100644
+index 0000000000..1990b000ee
+--- /dev/null
++++ b/drivers/mtd/nandx/include/internal/nandx_util.h
+@@ -0,0 +1,221 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NANDX_UTIL_H__
++#define __NANDX_UTIL_H__
++
++typedef unsigned char u8;
++typedef unsigned short u16;
++typedef unsigned int u32;
++typedef unsigned long long u64;
++
++enum nand_irq_return {
++      NAND_IRQ_NONE,
++      NAND_IRQ_HANDLED,
++};
++
++enum nand_dma_operation {
++      NDMA_FROM_DEV,
++      NDMA_TO_DEV,
++};
++
++
++/*
++ * Compatible function
++ * used for preloader/lk/kernel environment
++ */
++#include "nandx_os.h"
++#include "nandx_errno.h"
++
++#ifndef BIT
++#define BIT(a)                  (1 << (a))
++#endif
++
++#ifndef min_t
++#define min_t(type, x, y) ({                    \
++      type __min1 = (x);                      \
++      type __min2 = (y);                      \
++      __min1 < __min2 ? __min1 : __min2; })
++
++#define max_t(type, x, y) ({                    \
++      type __max1 = (x);                      \
++      type __max2 = (y);                      \
++      __max1 > __max2 ? __max1 : __max2; })
++#endif
++
++#ifndef GENMASK
++#define GENMASK(h, l) \
++      (((~0UL) << (l)) & (~0UL >> ((sizeof(unsigned long) * 8) - 1 - (h))))
++#endif
++
++#ifndef __weak
++#define __weak __attribute__((__weak__))
++#endif
++
++#ifndef __packed
++#define __packed __attribute__((__packed__))
++#endif
++
++#ifndef KB
++#define KB(x)   ((x) << 10)
++#define MB(x)   (KB(x) << 10)
++#define GB(x)   (MB(x) << 10)
++#endif
++
++#ifndef offsetof
++#define offsetof(type, member) ((size_t)&((type *)0)->member)
++#endif
++
++#ifndef NULL
++#define NULL (void *)0
++#endif
++static inline u32 nandx_popcount(u32 x)
++{
++      x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
++      x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
++      x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
++      x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
++      x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
++
++      return x;
++}
++
++#ifndef zero_popcount
++#define zero_popcount(x) (32 - nandx_popcount(x))
++#endif
++
++#ifndef do_div
++#define do_div(n, base) \
++      ({ \
++              u32 __base = (base); \
++              u32 __rem; \
++              __rem = ((u64)(n)) % __base; \
++              (n) = ((u64)(n)) / __base; \
++              __rem; \
++      })
++#endif
++
++#define div_up(x, y) \
++      ({ \
++              u64 __temp = ((x) + (y) - 1); \
++              do_div(__temp, (y)); \
++              __temp; \
++      })
++
++#define div_down(x, y) \
++      ({ \
++              u64 __temp = (x); \
++              do_div(__temp, (y)); \
++              __temp; \
++      })
++
++#define div_round_up(x, y)      (div_up(x, y) * (y))
++#define div_round_down(x, y)    (div_down(x, y) * (y))
++
++#define reminder(x, y) \
++      ({ \
++              u64 __temp = (x); \
++              do_div(__temp, (y)); \
++      })
++
++#ifndef round_up
++#define round_up(x, y)          ((((x) - 1) | ((y) - 1)) + 1)
++#define round_down(x, y)        ((x) & ~((y) - 1))
++#endif
++
++#ifndef readx_poll_timeout_atomic
++#define readx_poll_timeout_atomic(op, addr, val, cond, delay_us, timeout_us) \
++      ({ \
++              u64 end = get_current_time_us() + timeout_us; \
++              for (;;) { \
++                      u64 now = get_current_time_us(); \
++                      (val) = op(addr); \
++                      if (cond) \
++                              break; \
++                      if (now > end) { \
++                              (val) = op(addr); \
++                              break; \
++                      } \
++              } \
++              (cond) ? 0 : -ETIMEDOUT; \
++      })
++
++#define readl_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
++      readx_poll_timeout_atomic(readl, addr, val, cond, delay_us, timeout_us)
++#define readw_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
++      readx_poll_timeout_atomic(readw, addr, val, cond, delay_us, timeout_us)
++#define readb_poll_timeout_atomic(addr, val, cond, delay_us, timeout_us) \
++      readx_poll_timeout_atomic(readb, addr, val, cond, delay_us, timeout_us)
++#endif
++
++struct nandx_split64 {
++      u64 head;
++      size_t head_len;
++      u64 body;
++      size_t body_len;
++      u64 tail;
++      size_t tail_len;
++};
++
++struct nandx_split32 {
++      u32 head;
++      u32 head_len;
++      u32 body;
++      u32 body_len;
++      u32 tail;
++      u32 tail_len;
++};
++
++#define nandx_split(split, offset, len, val, align) \
++      do { \
++              (split)->head = (offset); \
++              (val) = div_round_down((offset), (align)); \
++              (val) = (align) - ((offset) - (val)); \
++              if ((val) == (align)) \
++                      (split)->head_len = 0; \
++              else if ((val) > (len)) \
++                      (split)->head_len = len; \
++              else \
++                      (split)->head_len = val; \
++              (split)->body = (offset) + (split)->head_len; \
++              (split)->body_len = div_round_down((len) - \
++                                                 (split)->head_len,\
++                                                 (align)); \
++              (split)->tail = (split)->body + (split)->body_len; \
++              (split)->tail_len = (len) - (split)->head_len - \
++                                  (split)->body_len; \
++      } while (0)
++
++#ifndef container_of
++#define container_of(ptr, type, member) \
++      ({const __typeof__(((type *)0)->member) * __mptr = (ptr); \
++              (type *)((char *)__mptr - offsetof(type, member)); })
++#endif
++
++static inline u32 nandx_cpu_to_be32(u32 val)
++{
++      u32 temp = 1;
++      u8 *p_temp = (u8 *)&temp;
++
++      if (*p_temp)
++              return ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
++                     ((val >> 8) & 0xff00) | ((val >> 24) & 0xff);
++
++      return val;
++}
++
++static inline void nandx_set_bits32(unsigned long addr, u32 mask,
++                                  u32 val)
++{
++      u32 temp = readl((void *)addr);
++
++      temp &= ~(mask);
++      temp |= val;
++      writel(temp, (void *)addr);
++}
++
++#endif /* __NANDX_UTIL_H__ */
+diff --git a/drivers/mtd/nandx/include/uboot/nandx_os.h b/drivers/mtd/nandx/include/uboot/nandx_os.h
+new file mode 100644
+index 0000000000..8ea53378bf
+--- /dev/null
++++ b/drivers/mtd/nandx/include/uboot/nandx_os.h
+@@ -0,0 +1,78 @@
++/*
++ * Copyright (C) 2017 MediaTek Inc.
++ * Licensed under either
++ *     BSD Licence, (see NOTICE for more details)
++ *     GNU General Public License, version 2.0, (see NOTICE for more details)
++ */
++
++#ifndef __NANDX_OS_H__
++#define __NANDX_OS_H__
++
++#include <common.h>
++#include <dm.h>
++#include <clk.h>
++#include <asm/dma-mapping.h>
++#include <linux/io.h>
++#include <linux/err.h>
++#include <linux/errno.h>
++#include <linux/bitops.h>
++#include <linux/kernel.h>
++#include <linux/compiler-gcc.h>
++
++#define NANDX_BULK_IO_USE_DRAM 0
++
++#define nandx_event_create()     NULL
++#define nandx_event_destroy(event)
++#define nandx_event_complete(event)
++#define nandx_event_init(event)
++#define nandx_event_wait_complete(event, timeout)        true
++
++#define nandx_irq_register(dev, irq, irq_handler, name, data) NULL
++
++static inline void *mem_alloc(u32 count, u32 size)
++{
++      return kmalloc(count * size, GFP_KERNEL | __GFP_ZERO);
++}
++
++static inline void mem_free(void *mem)
++{
++      kfree(mem);
++}
++
++static inline u64 get_current_time_us(void)
++{
++      return timer_get_us();
++}
++
++static inline u32 nandx_dma_map(void *dev, void *buf, u64 len,
++                              enum nand_dma_operation op)
++{
++      unsigned long addr = (unsigned long)buf;
++      u64 size;
++
++      size = ALIGN(len, ARCH_DMA_MINALIGN);
++
++      if (op == NDMA_FROM_DEV)
++              invalidate_dcache_range(addr, addr + size);
++      else
++              flush_dcache_range(addr, addr + size);
++
++      return addr;
++}
++
++static inline void nandx_dma_unmap(void *dev, void *buf, void *addr,
++                                 u64 len, enum nand_dma_operation op)
++{
++      u64 size;
++
++      size = ALIGN(len, ARCH_DMA_MINALIGN);
++
++      if (op != NDMA_FROM_DEV)
++              invalidate_dcache_range((unsigned long)addr, addr + size);
++      else
++              flush_dcache_range((unsigned long)addr, addr + size);
++
++      return addr;
++}
++
++#endif /* __NANDX_OS_H__ */
+diff --git a/include/configs/mt7622.h b/include/configs/mt7622.h
+index dfd506ed24..6d0c956484 100644
+--- a/include/configs/mt7622.h
++++ b/include/configs/mt7622.h
+@@ -11,6 +11,31 @@
+ #include <linux/sizes.h>
++/* SPI Nand */
++#if defined(CONFIG_MTD_RAW_NAND)
++#define CONFIG_SYS_MAX_NAND_DEVICE 1
++#define CONFIG_SYS_NAND_BASE          0x1100d000
++
++#define ENV_BOOT_READ_IMAGE \
++      "boot_rd_img=" \
++      "nand read 0x4007ff28 0x380000 0x1400000" \
++      ";iminfo 0x4007ff28 \0"
++
++#define ENV_BOOT_WRITE_IMAGE \
++      "boot_wr_img=" \
++      "nand write 0x4007ff28 0x380000 0x1400000" \
++      ";iminfo 0x4007ff28 \0"
++
++#define ENV_BOOT_CMD \
++      "mtk_boot=run boot_rd_img;bootm;\0"
++
++#define CONFIG_EXTRA_ENV_SETTINGS \
++      ENV_BOOT_READ_IMAGE \
++      ENV_BOOT_CMD \
++      "bootcmd=run mtk_boot;\0"
++
++#endif
++
+ #define CONFIG_SYS_MAXARGS            8
+ #define CONFIG_SYS_BOOTM_LEN          SZ_64M
+ #define CONFIG_SYS_CBSIZE             SZ_1K
+-- 
+2.17.1
+
diff --git a/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch b/package/boot/uboot-mediatek/patches/003-mt7622-uboot-add-dts-and-config-for-spi-nand.patch
new file mode 100644 (file)
index 0000000..2c021e1
--- /dev/null
@@ -0,0 +1,64 @@
+From b1b3c3d2ce62872c8dec4a7d645af6b3c565e094 Mon Sep 17 00:00:00 2001
+From: Sam Shih <sam.shih@mediatek.com>
+Date: Mon, 20 Apr 2020 17:11:32 +0800
+Subject: [PATCH 2/3] mt7622 uboot: add dts and config for spi nand
+
+This patch add dts and config for mt7622 spi nand
+
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ arch/arm/dts/mt7622-rfb.dts |  6 ++++++
+ arch/arm/dts/mt7622.dtsi    | 20 ++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+diff --git a/arch/arm/dts/mt7622-rfb.dts b/arch/arm/dts/mt7622-rfb.dts
+index f05c3fe14d..05502bddec 100644
+--- a/arch/arm/dts/mt7622-rfb.dts
++++ b/arch/arm/dts/mt7622-rfb.dts
+@@ -143,6 +143,12 @@
+       };
+ };
++&nandc {
++      pinctrl-names = "default";
++      pinctrl-0 = <&snfi_pins>;
++      status = "okay";
++};
++
+ &uart0 {
+       pinctrl-names = "default";
+       pinctrl-0 = <&uart0_pins>;
+diff --git a/arch/arm/dts/mt7622.dtsi b/arch/arm/dts/mt7622.dtsi
+index 1e8ec9b48b..63fdb63d4a 100644
+--- a/arch/arm/dts/mt7622.dtsi
++++ b/arch/arm/dts/mt7622.dtsi
+@@ -52,6 +52,26 @@
+               #size-cells = <0>;
+       };
++      nandc: nfi@1100d000 {
++              compatible = "mediatek,mt7622-nfc";
++              reg = <0x1100d000 0x1000>,
++                    <0x1100e000 0x1000>;
++              interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>,
++                           <GIC_SPI 95 IRQ_TYPE_LEVEL_LOW>;
++              clocks = <&pericfg CLK_PERI_NFI_PD>,
++                       <&pericfg CLK_PERI_NFIECC_PD>,
++                       <&pericfg CLK_PERI_SNFI_PD>,
++                       <&topckgen CLK_TOP_NFI_INFRA_SEL>,
++                       <&topckgen CLK_TOP_UNIVPLL2_D8>;
++              clock-names = "nfi_clk",
++                            "ecc_clk",
++                            "snfi_clk",
++                            "spinfi_sel",
++                            "spinfi_parent_50m";
++              nand-ecc-mode = "hw";
++              status = "disabled";
++      };
++
+       timer {
+               compatible = "arm,armv8-timer";
+               interrupt-parent = <&gic>;
+-- 
+2.17.1
+
diff --git a/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch b/package/boot/uboot-mediatek/patches/004-configs-enable-mtd-and-mtk_spi_nand-in-defconfig.patch
new file mode 100644 (file)
index 0000000..752d816
--- /dev/null
@@ -0,0 +1,38 @@
+From e5745143a2984cf44fbfc0b3aedb49e57873f109 Mon Sep 17 00:00:00 2001
+From: Sam Shih <sam.shih@mediatek.com>
+Date: Mon, 20 Apr 2020 17:17:04 +0800
+Subject: [PATCH 3/3] configs: enable mtd and mtk_spi_nand in defconfig
+
+This patch enable mtk and mtk_spi_nand in mt7622_rfb defconfig
+
+Signed-off-by: Sam Shih <sam.shih@mediatek.com>
+---
+ configs/mt7622_rfb_defconfig | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/configs/mt7622_rfb_defconfig b/configs/mt7622_rfb_defconfig
+index 1ce6ebdfeb..816126267b 100644
+--- a/configs/mt7622_rfb_defconfig
++++ b/configs/mt7622_rfb_defconfig
+@@ -13,6 +13,7 @@ CONFIG_DEFAULT_FDT_FILE="mt7622-rfb"
+ CONFIG_SYS_PROMPT="MT7622> "
+ CONFIG_CMD_BOOTMENU=y
+ CONFIG_CMD_MMC=y
++CONFIG_CMD_NAND=y
+ CONFIG_CMD_SF_TEST=y
+ CONFIG_CMD_PING=y
+ CONFIG_CMD_SMC=y
+@@ -25,6 +26,10 @@ CONFIG_CLK=y
+ CONFIG_DM_MMC=y
+ CONFIG_MMC_HS200_SUPPORT=y
+ CONFIG_MMC_MTK=y
++CONFIG_MTD=y
++CONFIG_DM_MTD=y
++CONFIG_MTK_SPI_NAND=y
++CONFIG_MTD_RAW_NAND=y
+ CONFIG_DM_SPI_FLASH=y
+ CONFIG_SPI_FLASH_EON=y
+ CONFIG_SPI_FLASH_GIGADEVICE=y
+-- 
+2.17.1
+