[][MAC80211][misc][update autobuild folder for WiFi7]

[Description]
Refactor autobuild folder for external build PASS.
Update MT7996 mp2.0 FW to 20230608.

[Release-log]
N/A

Change-Id: Idf22eb7dd01a777657f90ff0ce170f31abb106f7
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7633627
diff --git a/autobuild_mac80211_release/feeds.conf.default b/autobuild_mac80211_release/feeds.conf.default
new file mode 100644
index 0000000..4eef7a8
--- /dev/null
+++ b/autobuild_mac80211_release/feeds.conf.default
@@ -0,0 +1,4 @@
+src-git packages https://git.openwrt.org/feed/packages.git
+src-git luci https://git.openwrt.org/project/luci.git
+src-git routing https://git.openwrt.org/feed/routing.git
+src-git mtk_openwrt_feed https://git01.mediatek.com/openwrt/feeds/mtk-openwrt-feeds
diff --git a/autobuild_mac80211_release/feeds.conf.default-21.02 b/autobuild_mac80211_release/feeds.conf.default-21.02
new file mode 100644
index 0000000..1f1c65f
--- /dev/null
+++ b/autobuild_mac80211_release/feeds.conf.default-21.02
@@ -0,0 +1,4 @@
+src-git packages https://git.openwrt.org/feed/packages.git;openwrt-21.02
+src-git luci https://git.openwrt.org/project/luci.git;openwrt-21.02
+src-git routing https://git.openwrt.org/feed/routing.git;openwrt-21.02.
+src-git mtk_openwrt_feed https://git01.mediatek.com/openwrt/feeds/mtk-openwrt-feeds;openwrt-21.02
diff --git a/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3021-mtk-wed-add-wed3-support.patch b/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3021-mtk-wed-add-wed3-support.patch
new file mode 100644
index 0000000..8c5f048
--- /dev/null
+++ b/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3021-mtk-wed-add-wed3-support.patch
@@ -0,0 +1,3720 @@
+From 400f8349a31ffc48538aa7df64a88111de9a738b Mon Sep 17 00:00:00 2001
+From: Sujuan Chen <sujuan.chen@mediatek.com>
+Date: Thu, 13 Apr 2023 15:51:08 +0800
+Subject: [PATCH] mtk:wed:add wed3 support
+
+Signed-off-by: sujuan.chen <sujuan.chen@mediatek.com>
+---
+ arch/arm64/boot/dts/mediatek/mt7988.dtsi      |  152 ++-
+ .../dts/mediatek/mt7988a-dsa-10g-spim-nor.dts |   16 +-
+ .../dts/mediatek/mt7988d-dsa-10g-spim-nor.dts |   16 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c   |    3 +-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h   |    5 +-
+ drivers/net/ethernet/mediatek/mtk_ppe.c       |   17 +-
+ drivers/net/ethernet/mediatek/mtk_ppe.h       |    2 +-
+ .../net/ethernet/mediatek/mtk_ppe_offload.c   |   13 +-
+ drivers/net/ethernet/mediatek/mtk_wed.c       | 1164 +++++++++++++----
+ drivers/net/ethernet/mediatek/mtk_wed.h       |   25 +-
+ .../net/ethernet/mediatek/mtk_wed_debugfs.c   |  584 ++++++++-
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.c   |   13 +-
+ drivers/net/ethernet/mediatek/mtk_wed_mcu.h   |    5 +-
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h  |  338 ++++-
+ include/linux/netdevice.h                     |    7 +
+ include/linux/soc/mediatek/mtk_wed.h          |   81 +-
+ 16 files changed, 1446 insertions(+), 333 deletions(-)
+ mode change 100755 => 100644 drivers/net/ethernet/mediatek/mtk_ppe.c
+
+diff --git a/arch/arm64/boot/dts/mediatek/mt7988.dtsi b/arch/arm64/boot/dts/mediatek/mt7988.dtsi
+index 364deef..f9a0120 100644
+--- a/arch/arm64/boot/dts/mediatek/mt7988.dtsi
++++ b/arch/arm64/boot/dts/mediatek/mt7988.dtsi
+@@ -191,44 +191,49 @@
+ 		status = "disabled";
+ 	};
+ 
+-	wed: wed@15010000 {
+-		compatible = "mediatek,wed";
+-		wed_num = <3>;
+-		/* add this property for wed get the pci slot number. */
+-		pci_slot_map = <0>, <1>, <2>;
+-		reg = <0 0x15010000 0 0x2000>,
+-		      <0 0x15012000 0 0x2000>,
+-		      <0 0x15014000 0 0x2000>;
++	wed0: wed@15010000 {
++		compatible = "mediatek,mt7988-wed",
++			     "syscon";
++		reg = <0 0x15010000 0 0x2000>;
+ 		interrupt-parent = <&gic>;
+-		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>;
+-	};
+-
+-	wed2: wed2@15012000 {
+-		compatible = "mediatek,wed2";
+-		wed_num = <3>;
+-		/* add this property for wed get the pci slot number. */
+-		reg = <0 0x15010000 0 0x2000>,
+-		      <0 0x15012000 0 0x2000>,
+-		      <0 0x15014000 0 0x2000>;
++		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>;
++		mediatek,wed_pcie = <&wed_pcie>;
++		mediatek,ap2woccif = <&ap2woccif0>;
++		mediatek,wocpu_ilm = <&wocpu0_ilm>;
++		mediatek,wocpu_dlm = <&wocpu0_dlm>;
++		mediatek,wocpu_boot = <&cpu0_boot>;
++		mediatek,wocpu_emi = <&wocpu0_emi>;
++		mediatek,wocpu_data = <&wocpu_data>;
++	};
++
++	wed1: wed@15012000 {
++		compatible = "mediatek,mt7988-wed",
++                             "syscon";
++		reg = <0 0x15012000 0 0x2000>;
+ 		interrupt-parent = <&gic>;
+-		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>;
+-	};
+-
+-	wed3: wed3@15014000 {
+-		compatible = "mediatek,wed3";
+-		wed_num = <3>;
+-		/* add this property for wed get the pci slot number. */
+-		reg = <0 0x15010000 0 0x2000>,
+-		      <0 0x15012000 0 0x2000>,
+-		      <0 0x15014000 0 0x2000>;
++		interrupts = <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>;
++		mediatek,wed_pcie = <&wed_pcie>;
++		mediatek,ap2woccif = <&ap2woccif1>;
++		mediatek,wocpu_ilm = <&wocpu1_ilm>;
++		mediatek,wocpu_dlm = <&wocpu1_dlm>;
++		mediatek,wocpu_boot = <&cpu1_boot>;
++		mediatek,wocpu_emi = <&wocpu1_emi>;
++		mediatek,wocpu_data = <&wocpu_data>;
++	};
++
++	wed2: wed@15014000 {
++		compatible = "mediatek,mt7988-wed",
++                             "syscon";
++		reg = <0 0x15014000 0 0x2000>;
+ 		interrupt-parent = <&gic>;
+-		interrupts = <GIC_SPI 205 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 270 IRQ_TYPE_LEVEL_HIGH>;
++		interrupts = <GIC_SPI 207 IRQ_TYPE_LEVEL_HIGH>;
++		mediatek,wed_pcie = <&wed_pcie>;
++		mediatek,ap2woccif = <&ap2woccif2>;
++		mediatek,wocpu_ilm = <&wocpu2_ilm>;
++		mediatek,wocpu_dlm = <&wocpu2_dlm>;
++		mediatek,wocpu_boot = <&cpu2_boot>;
++		mediatek,wocpu_emi = <&wocpu2_emi>;
++		mediatek,wocpu_data = <&wocpu_data>;
+ 	};
+ 
+ 	wdma: wdma@15104800 {
+@@ -238,15 +243,25 @@
+ 		      <0 0x15105000 0 0x400>;
+ 	};
+ 
+-	ap2woccif: ap2woccif@151A5000 {
+-		compatible = "mediatek,ap2woccif";
+-		reg = <0 0x151A5000 0 0x1000>,
+-		      <0 0x152A5000 0 0x1000>,
+-		      <0 0x153A5000 0 0x1000>;
++	ap2woccif0: ap2woccif@151A5000 {
++		compatible = "mediatek,ap2woccif", "syscon";
++		reg = <0 0x151A5000 0 0x1000>;
++		interrupt-parent = <&gic>;
++		interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>;
++	};
++
++	ap2woccif1: ap2woccif@152A5000 {
++		compatible = "mediatek,ap2woccif", "syscon";
++		reg = <0 0x152A5000 0 0x1000>;
+ 		interrupt-parent = <&gic>;
+-		interrupts = <GIC_SPI 211 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 212 IRQ_TYPE_LEVEL_HIGH>,
+-			     <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
++		interrupts = <GIC_SPI 212 IRQ_TYPE_LEVEL_HIGH>;
++	};
++
++	ap2woccif2: ap2woccif@153A5000 {
++		compatible = "mediatek,ap2woccif", "syscon";
++		reg = <0 0x153A5000 0 0x1000>;
++		interrupt-parent = <&gic>;
++		interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
+ 	};
+ 
+ 	wocpu0_ilm: wocpu0_ilm@151E0000 {
+@@ -254,31 +269,53 @@
+ 		reg = <0 0x151E0000 0 0x8000>;
+ 	};
+ 
+-	wocpu1_ilm: wocpu1_ilm@152E0000 {
+-		compatible = "mediatek,wocpu1_ilm";
++	wocpu1_ilm: wocpu_ilm@152E0000 {
++		compatible = "mediatek,wocpu_ilm";
+ 		reg = <0 0x152E0000 0 0x8000>;
+ 	};
+ 
+-	wocpu2_ilm: wocpu2_ilm@153E0000 {
+-		compatible = "mediatek,wocpu2_ilm";
+-		reg = <0 0x153E0000 0 0x8000>;
++	wocpu2_ilm: wocpu_ilm@153E0000 {
++                compatible = "mediatek,wocpu_ilm";
++                reg = <0 0x153E0000 0 0x8000>;
++    };
++
++	wocpu0_dlm: wocpu_dlm@151E8000 {
++		compatible = "mediatek,wocpu_dlm";
++		reg = <0 0x151E8000 0 0x2000>;
++
++		resets = <&ethsysrst 0>;
++		reset-names = "wocpu_rst";
++	};
++
++	wocpu1_dlm: wocpu_dlm@0x152E8000 {
++		compatible = "mediatek,wocpu_dlm";
++		reg = <0 0x152E8000 0 0x2000>;
++
++		resets = <&ethsysrst 0>;
++		reset-names = "wocpu_rst";
+ 	};
+ 
+-	wocpu_dlm: wocpu_dlm@151E8000 {
++	wocpu2_dlm: wocpu_dlm@0x153E8000 {
+ 		compatible = "mediatek,wocpu_dlm";
+-		reg = <0 0x151E8000 0 0x2000>,
+-		      <0 0x152E8000 0 0x2000>,
+-		      <0 0x153E8000 0 0x2000>;
++		reg = <0 0x153E8000 0 0x2000>;
+ 
+ 		resets = <&ethsysrst 0>;
+ 		reset-names = "wocpu_rst";
+ 	};
+ 
+-	cpu_boot: wocpu_boot@15194000 {
+-		compatible = "mediatek,wocpu_boot";
+-		reg = <0 0x15194000 0 0x1000>,
+-		      <0 0x15294000 0 0x1000>,
+-		      <0 0x15394000 0 0x1000>;
++	cpu0_boot: wocpu_boot@15194000 {
++		compatible = "mediatek,wocpu0_boot";
++		reg = <0 0x15194000 0 0x1000>;
++	};
++
++	cpu1_boot: wocpu_boot@15294000 {
++		compatible = "mediatek,wocpu1_boot";
++		reg = <0 0x15294000 0 0x1000>;
++	};
++
++	cpu2_boot: wocpu_boot@15394000 {
++		compatible = "mediatek,wocpu2_boot";
++		reg = <0 0x15394000 0 0x1000>;
+ 	};
+ 
+ 	reserved-memory {
+@@ -827,6 +864,7 @@
+ 					 <&topckgen CK_TOP_CB_SGM_325M>;
+ 		mediatek,ethsys = <&ethsys>;
+ 		mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>;
++		mediatek,wed = <&wed0>, <&wed1>, <&wed2>;
+ 		mediatek,usxgmiisys = <&usxgmiisys0>, <&usxgmiisys1>;
+ 		mediatek,xfi_pextp = <&xfi_pextp0>, <&xfi_pextp1>;
+ 		mediatek,xfi_pll = <&xfi_pll>;
+diff --git a/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts b/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
+index 7db5164..0a6db8b 100644
+--- a/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
++++ b/arch/arm64/boot/dts/mediatek/mt7988a-dsa-10g-spim-nor.dts
+@@ -341,9 +341,23 @@
+ 	status = "okay";
+ };
+ 
+-&wed {
++&wed0 {
+ 	dy_txbm_enable = "true";
+ 	dy_txbm_budge = <8>;
+ 	txbm_init_sz = <10>;
+ 	status = "okay";
+ };
++
++&wed1 {
++	dy_txbm_enable = "true";
++	dy_txbm_budge = <8>;
++	txbm_init_sz = <10>;
++	status = "okay";
++};
++
++&wed2 {
++	dy_txbm_enable = "true";
++	dy_txbm_budge = <8>;
++	txbm_init_sz = <10>;
++	status = "okay";
++};
+\ No newline at end of file
+diff --git a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
+index 67c6508..c407b33 100644
+--- a/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
++++ b/arch/arm64/boot/dts/mediatek/mt7988d-dsa-10g-spim-nor.dts
+@@ -325,9 +325,23 @@
+ 	status = "okay";
+ };
+ 
+-&wed {
++&wed0 {
+ 	dy_txbm_enable = "true";
+ 	dy_txbm_budge = <8>;
+ 	txbm_init_sz = <10>;
+ 	status = "okay";
+ };
++
++&wed1 {
++	dy_txbm_enable = "true";
++	dy_txbm_budge = <8>;
++	txbm_init_sz = <10>;
++	status = "okay";
++};
++
++&wed2 {
++	dy_txbm_enable = "true";
++	dy_txbm_budge = <8>;
++	txbm_init_sz = <10>;
++	status = "okay";
++};
+\ No newline at end of file
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 388982c..d59c29f 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -4709,7 +4709,8 @@ static int mtk_probe(struct platform_device *pdev)
+ 							  "mediatek,wed", i);
+ 		static const u32 wdma_regs[] = {
+ 			MTK_WDMA0_BASE,
+-			MTK_WDMA1_BASE
++			MTK_WDMA1_BASE,
++			MTK_WDMA2_BASE
+ 		};
+ 		void __iomem *wdma;
+ 		u32 wdma_phy;
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index a9feaed..70e8377 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -605,9 +605,12 @@
+ #define RX_DMA_SPORT_MASK       0x7
+ #define RX_DMA_SPORT_MASK_V2    0xf
+ 
+-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
++#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
+ #define MTK_WDMA0_BASE		0x4800
+ #define MTK_WDMA1_BASE		0x4c00
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++#define MTK_WDMA2_BASE		0x5000
++#endif
+ #else
+ #define MTK_WDMA0_BASE		0x2800
+ #define MTK_WDMA1_BASE		0x2c00
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
+old mode 100755
+new mode 100644
+index bc13a9b..3910163
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
+@@ -9,6 +9,7 @@
+ #include <linux/if_ether.h>
+ #include <linux/if_vlan.h>
+ #include <net/dsa.h>
++#include <net/route.h>
+ #include "mtk_eth_soc.h"
+ #include "mtk_ppe.h"
+ #include "mtk_ppe_regs.h"
+@@ -396,7 +397,7 @@ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid)
+ }
+ 
+ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+-			   int bss, int wcid)
++			   int bss, int wcid, bool amsdu_en)
+ {
+ 	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
+ 	u32 *ib2 = mtk_foe_entry_ib2(entry);
+@@ -408,6 +409,9 @@ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+ 
+ 	l2->winfo = FIELD_PREP(MTK_FOE_WINFO_WCID, wcid) |
+ 		    FIELD_PREP(MTK_FOE_WINFO_BSS, bss);
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++	l2->winfo_pao = FIELD_PREP(MTK_FOE_WINFO_PAO_AMSDU_EN, amsdu_en);
++#endif
+ #else
+ 	if (wdma_idx)
+ 		*ib2 |= MTK_FOE_IB2_WDMA_DEVIDX;
+@@ -443,6 +447,17 @@ int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp)
+ 	*ib2 &= ~MTK_FOE_IB2_DSCP;
+ 	*ib2 |= FIELD_PREP(MTK_FOE_IB2_DSCP, dscp);
+ 
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++	struct mtk_foe_mac_info *l2 = mtk_foe_entry_l2(entry);
++
++	if (*ib2 & MTK_FOE_IB2_WDMA_WINFO &&
++	    l2->winfo_pao & MTK_FOE_WINFO_PAO_AMSDU_EN) {
++		u8 tid = rt_tos2priority(dscp) & 0xf;
++
++		l2->winfo_pao |= FIELD_PREP(MTK_FOE_WINFO_PAO_TID, tid);
++	}
++#endif
++
+ 	return 0;
+ }
+ 
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
+index df10040..9e7d5aa 100644
+--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
++++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
+@@ -428,7 +428,7 @@ int mtk_foe_entry_set_dsa(struct mtk_foe_entry *entry, int port);
+ int mtk_foe_entry_set_vlan(struct mtk_foe_entry *entry, int vid);
+ int mtk_foe_entry_set_pppoe(struct mtk_foe_entry *entry, int sid);
+ int mtk_foe_entry_set_wdma(struct mtk_foe_entry *entry, int wdma_idx, int txq,
+-			   int bss, int wcid);
++			   int bss, int wcid, bool amsdu_en);
+ int mtk_foe_entry_set_qid(struct mtk_foe_entry *entry, int qid);
+ int mtk_foe_entry_set_dscp(struct mtk_foe_entry *entry, int dscp);
+ int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry);
+diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+index 9bc0857..86fc9a1 100644
+--- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
++++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c
+@@ -112,6 +112,7 @@ mtk_flow_get_wdma_info(struct net_device *dev, const u8 *addr, struct mtk_wdma_i
+ 	info->queue = path.mtk_wdma.queue;
+ 	info->bss = path.mtk_wdma.bss;
+ 	info->wcid = path.mtk_wdma.wcid;
++	info->amsdu_en = path.mtk_wdma.amsdu_en;
+ 
+ 	return 0;
+ }
+@@ -193,13 +194,15 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe,
+ 
+ 	if (mtk_flow_get_wdma_info(dev, dest_mac, &info) == 0) {
+ 		mtk_foe_entry_set_wdma(foe, info.wdma_idx, info.queue, info.bss,
+-				       info.wcid);
++				       info.wcid, info.amsdu_en);
+ 		pse_port = PSE_PPE0_PORT;
+ #if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
+ 		if (info.wdma_idx == 0)
+ 			pse_port = PSE_WDMA0_PORT;
+ 		else if (info.wdma_idx == 1)
+ 			pse_port = PSE_WDMA1_PORT;
++		else if (info.wdma_idx == 2)
++			pse_port = PSE_WDMA2_PORT;
+ 		else
+ 			return -EOPNOTSUPP;
+ #endif
+@@ -458,8 +461,8 @@ mtk_flow_offload_replace(struct mtk_eth *eth, struct flow_cls_offload *f)
+ 	if (err)
+ 		return err;
+ 
+-	if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
+-		return err;
++	/*if (wed_index >= 0 && (err = mtk_wed_flow_add(wed_index)) < 0)
++		return err;*/
+ 
+ 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ 	if (!entry)
+@@ -499,8 +502,8 @@ clear:
+ 	mtk_foe_entry_clear(eth->ppe[i], entry);
+ free:
+ 	kfree(entry);
+-	if (wed_index >= 0)
+-	    mtk_wed_flow_remove(wed_index);
++	/*if (wed_index >= 0)
++	    mtk_wed_flow_remove(wed_index);*/
+ 	return err;
+ }
+ 
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
+index 37a86c3..e3809db 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -28,7 +28,7 @@ struct wo_cmd_ring {
+ 	u32 cnt;
+ 	u32 unit;
+ };
+-static struct mtk_wed_hw *hw_list[2];
++static struct mtk_wed_hw *hw_list[3];
+ static DEFINE_MUTEX(hw_lock);
+ 
+ static void
+@@ -73,6 +73,26 @@ mtk_wdma_read_reset(struct mtk_wed_device *dev)
+ 	return wdma_r32(dev, MTK_WDMA_GLO_CFG);
+ }
+ 
++static u32
++mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++	if (wed_r32(dev, reg) & mask)
++		return true;
++
++	return false;
++}
++
++static int
++mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
++{
++	int sleep = 1000;
++	int timeout = 100 * sleep;
++	u32 val;
++
++	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
++				 timeout, false, dev, reg, mask);
++}
++
+ static int
+ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
+ {
+@@ -235,6 +255,8 @@ mtk_wed_assign(struct mtk_wed_device *dev)
+ 			continue;
+ 
+ 		hw->wed_dev = dev;
++		hw->pci_base = MTK_WED_PCIE_BASE;
++
+ 		return hw;
+ 	}
+ 
+@@ -242,23 +264,84 @@ mtk_wed_assign(struct mtk_wed_device *dev)
+ }
+ 
+ static int
+-mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
++mtk_wed_pao_buffer_alloc(struct mtk_wed_device *dev)
++{
++	struct mtk_wed_pao *pao;
++	int i, j;
++
++	pao = kzalloc(sizeof(struct mtk_wed_pao), GFP_KERNEL);
++	if (!pao)
++		return -ENOMEM;
++
++	dev->hw->wed_pao = pao;
++
++	for (i = 0; i < 32; i++) {
++		/* each segment is 64K*/
++		pao->hif_txd[i] = (char *)__get_free_pages(GFP_ATOMIC |
++							   GFP_DMA32 |
++							   __GFP_ZERO, 4);
++		if (!pao->hif_txd[i])
++			goto err;
++
++		pao->hif_txd_phys[i] = dma_map_single(dev->hw->dev,
++						      pao->hif_txd[i],
++						      16 * PAGE_SIZE,
++						      DMA_TO_DEVICE);
++		if (unlikely(dma_mapping_error(dev->hw->dev,
++					       pao->hif_txd_phys[i])))
++			goto err;
++	}
++
++	return 0;
++
++err:
++	for (j = 0; j < i; j++)
++		dma_unmap_single(dev->hw->dev, pao->hif_txd_phys[j],
++			     16 * PAGE_SIZE, DMA_TO_DEVICE);
++
++	return -ENOMEM;
++}
++
++static int
++mtk_wed_pao_free_buffer(struct mtk_wed_device *dev)
++{
++	struct mtk_wed_pao *pao = dev->hw->wed_pao;
++	int i;
++
++	for (i = 0; i < 32; i++) {
++		dma_unmap_single(dev->hw->dev, pao->hif_txd_phys[i],
++				 16 * PAGE_SIZE, DMA_TO_DEVICE);
++		free_pages((unsigned long)pao->hif_txd[i], 4);
++	}
++
++	return 0;
++}
++
++static int
++mtk_wed_tx_buffer_alloc(struct mtk_wed_device *dev)
+ {
+ 	struct mtk_wdma_desc *desc;
++	void *desc_ptr;
+ 	dma_addr_t desc_phys;
+-	void **page_list;
++	struct dma_page_info *page_list;
+ 	u32 last_seg = MTK_WDMA_DESC_CTRL_LAST_SEG1;
+ 	int token = dev->wlan.token_start;
+-	int ring_size, n_pages, page_idx;
+-	int i;
+-
++	int ring_size, pkt_nums, n_pages, page_idx;
++	int i, ret = 0;
+ 
+ 	if (dev->ver == MTK_WED_V1) {
+ 		ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
+-	} else {
++		pkt_nums = ring_size;
++		dev->tx_buf_ring.desc_size = sizeof(struct mtk_wdma_desc);
++	} else if (dev->hw->version == 2) {
+ 		ring_size = MTK_WED_VLD_GROUP_SIZE * MTK_WED_PER_GROUP_PKT +
+ 			    MTK_WED_WDMA_RING_SIZE * 2;
+ 		last_seg = MTK_WDMA_DESC_CTRL_LAST_SEG0;
++		dev->tx_buf_ring.desc_size = sizeof(struct mtk_wdma_desc);
++	} else if (dev->hw->version == 3) {
++		ring_size = MTK_WED_TX_BM_DMA_SIZE;
++		pkt_nums = MTK_WED_TX_BM_PKT_CNT;
++		dev->tx_buf_ring.desc_size = sizeof(struct mtk_rxbm_desc);
+ 	}
+ 
+ 	n_pages = ring_size / MTK_WED_BUF_PER_PAGE;
+@@ -267,18 +350,20 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
+ 	if (!page_list)
+ 		return -ENOMEM;
+ 
+-	dev->buf_ring.size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
+-	dev->buf_ring.pages = page_list;
++	dev->tx_buf_ring.size = ring_size;
++	dev->tx_buf_ring.pages = page_list;
++	dev->tx_buf_ring.pkt_nums = pkt_nums;
+ 
+-	desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
+-				  &desc_phys, GFP_KERNEL);
+-	if (!desc)
++	desc_ptr = dma_alloc_coherent(dev->hw->dev,
++				      ring_size * dev->tx_buf_ring.desc_size,
++				      &desc_phys, GFP_KERNEL);
++	if (!desc_ptr)
+ 		return -ENOMEM;
+ 
+-	dev->buf_ring.desc = desc;
+-	dev->buf_ring.desc_phys = desc_phys;
++	dev->tx_buf_ring.desc = desc_ptr;
++	dev->tx_buf_ring.desc_phys = desc_phys;
+ 
+-	for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
++	for (i = 0, page_idx = 0; i < pkt_nums; i += MTK_WED_BUF_PER_PAGE) {
+ 		dma_addr_t page_phys, buf_phys;
+ 		struct page *page;
+ 		void *buf;
+@@ -295,7 +380,10 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
+ 			return -ENOMEM;
+ 		}
+ 
+-		page_list[page_idx++] = page;
++		page_list[page_idx].addr = page;
++		page_list[page_idx].addr_phys = page_phys;
++		page_idx++;
++
+ 		dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
+ 					DMA_BIDIRECTIONAL);
+ 
+@@ -303,19 +391,23 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
+ 		buf_phys = page_phys;
+ 
+ 		for (s = 0; s < MTK_WED_BUF_PER_PAGE; s++) {
+-			u32 txd_size;
+-
+-			txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
+-
++			desc = desc_ptr;
+ 			desc->buf0 = buf_phys;
+-			desc->buf1 = buf_phys + txd_size;
+-			desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0,
+-						txd_size) |
+-				     FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
+-						MTK_WED_BUF_SIZE - txd_size) |
+-						last_seg;
+-			desc->info = 0;
+-			desc++;
++			if (dev->hw->version < 3) {
++				u32 txd_size;
++
++				txd_size = dev->wlan.init_buf(buf, buf_phys, token++);
++				desc->buf1 = buf_phys + txd_size;
++				desc->ctrl = FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN0,
++							txd_size) |
++					     FIELD_PREP(MTK_WDMA_DESC_CTRL_LEN1,
++							MTK_WED_BUF_SIZE - txd_size) |
++							last_seg;
++				desc->info = 0;
++			} else {
++				desc->ctrl = token << 16;
++			}
++			desc_ptr += dev->tx_buf_ring.desc_size;
+ 
+ 			buf += MTK_WED_BUF_SIZE;
+ 			buf_phys += MTK_WED_BUF_SIZE;
+@@ -325,15 +417,18 @@ mtk_wed_buffer_alloc(struct mtk_wed_device *dev)
+ 					   DMA_BIDIRECTIONAL);
+ 	}
+ 
+-	return 0;
++	if (dev->hw->version == 3)
++		ret = mtk_wed_pao_buffer_alloc(dev);
++
++	return ret;
+ }
+ 
+ static void
+-mtk_wed_free_buffer(struct mtk_wed_device *dev)
++mtk_wed_free_tx_buffer(struct mtk_wed_device *dev)
+ {
+-	struct mtk_wdma_desc *desc = dev->buf_ring.desc;
+-	void **page_list = dev->buf_ring.pages;
+-	int ring_size, page_idx;
++	struct mtk_rxbm_desc *desc = dev->tx_buf_ring.desc;
++	struct dma_page_info *page_list = dev->tx_buf_ring.pages;
++	int ring_size, page_idx, pkt_nums;
+ 	int i;
+ 
+ 	if (!page_list)
+@@ -342,33 +437,33 @@ mtk_wed_free_buffer(struct mtk_wed_device *dev)
+ 	if (!desc)
+ 		goto free_pagelist;
+ 
+-	if (dev->ver == MTK_WED_V1) {
+-		ring_size = dev->wlan.nbuf & ~(MTK_WED_BUF_PER_PAGE - 1);
+-	} else {
+-		ring_size = MTK_WED_VLD_GROUP_SIZE * MTK_WED_PER_GROUP_PKT +
+-			    MTK_WED_WDMA_RING_SIZE * 2;
++	pkt_nums = ring_size = dev->tx_buf_ring.size;
++	if (dev->hw->version == 3) {
++		mtk_wed_pao_free_buffer(dev);
++		pkt_nums = dev->tx_buf_ring.pkt_nums;
+ 	}
+ 
+-	for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_BUF_PER_PAGE) {
+-		void *page = page_list[page_idx++];
++	for (i = 0, page_idx = 0; i < pkt_nums; i += MTK_WED_BUF_PER_PAGE) {
++		void *page = page_list[page_idx].addr;
+ 
+ 		if (!page)
+ 			break;
+ 
+-		dma_unmap_page(dev->hw->dev, desc[i].buf0,
++		dma_unmap_page(dev->hw->dev, page_list[page_idx].addr_phys,
+ 			       PAGE_SIZE, DMA_BIDIRECTIONAL);
+ 		__free_page(page);
++		page_idx++;
+ 	}
+ 
+-	dma_free_coherent(dev->hw->dev, ring_size * sizeof(*desc),
+-			  desc, dev->buf_ring.desc_phys);
++	dma_free_coherent(dev->hw->dev, ring_size * dev->tx_buf_ring.desc_size,
++			  dev->tx_buf_ring.desc, dev->tx_buf_ring.desc_phys);
+ 
+ free_pagelist:
+ 	kfree(page_list);
+ }
+ 
+ static int
+-mtk_wed_rx_bm_alloc(struct mtk_wed_device *dev)
++mtk_wed_rx_buffer_alloc(struct mtk_wed_device *dev)
+ {
+ 	struct mtk_rxbm_desc *desc;
+ 	dma_addr_t desc_phys;
+@@ -389,7 +484,7 @@ mtk_wed_rx_bm_alloc(struct mtk_wed_device *dev)
+ }
+ 
+ static void
+-mtk_wed_free_rx_bm(struct mtk_wed_device *dev)
++mtk_wed_free_rx_buffer(struct mtk_wed_device *dev)
+ {
+ 	struct mtk_rxbm_desc *desc = dev->rx_buf_ring.desc;
+ 	int ring_size = dev->rx_buf_ring.size;
+@@ -403,6 +498,113 @@ mtk_wed_free_rx_bm(struct mtk_wed_device *dev)
+ 			  desc, dev->rx_buf_ring.desc_phys);
+ }
+ 
++/* TODO */
++static int
++mtk_wed_rx_page_buffer_alloc(struct mtk_wed_device *dev)
++{
++	int ring_size = dev->wlan.rx_nbuf, buf_num = MTK_WED_RX_PG_BM_CNT;
++	struct mtk_rxbm_desc *desc;
++	dma_addr_t desc_phys;
++	struct dma_page_info *page_list;
++	int n_pages, page_idx;
++	int i;
++
++	n_pages = buf_num / MTK_WED_RX_PAGE_BUF_PER_PAGE;
++
++	page_list = kcalloc(n_pages, sizeof(*page_list), GFP_KERNEL);
++	if (!page_list)
++		return -ENOMEM;
++
++	dev->rx_page_buf_ring.size = ring_size & ~(MTK_WED_BUF_PER_PAGE - 1);
++	dev->rx_page_buf_ring.pages = page_list;
++	dev->rx_page_buf_ring.pkt_nums = buf_num;
++
++	desc = dma_alloc_coherent(dev->hw->dev, ring_size * sizeof(*desc),
++	                         &desc_phys, GFP_KERNEL);
++	if (!desc)
++		return -ENOMEM;
++
++	dev->rx_page_buf_ring.desc = desc;
++	dev->rx_page_buf_ring.desc_phys = desc_phys;
++
++	for (i = 0, page_idx = 0; i < buf_num; i += MTK_WED_RX_PAGE_BUF_PER_PAGE) {
++		dma_addr_t page_phys, buf_phys;
++		struct page *page;
++		void *buf;
++		int s;
++
++		page = __dev_alloc_pages(GFP_KERNEL, 0);
++		if (!page)
++			return -ENOMEM;
++
++		page_phys = dma_map_page(dev->hw->dev, page, 0, PAGE_SIZE,
++		                        DMA_BIDIRECTIONAL);
++		if (dma_mapping_error(dev->hw->dev, page_phys)) {
++			__free_page(page);
++			return -ENOMEM;
++		}
++
++		page_list[page_idx].addr= page;
++		page_list[page_idx].addr_phys= page_phys;
++		page_idx++;
++
++		dma_sync_single_for_cpu(dev->hw->dev, page_phys, PAGE_SIZE,
++		                       DMA_BIDIRECTIONAL);
++
++		buf = page_to_virt(page);
++		buf_phys = page_phys;
++
++		for (s = 0; s < MTK_WED_RX_PAGE_BUF_PER_PAGE; s++) {
++
++			desc->buf0 = cpu_to_le32(buf_phys);
++			desc++;
++
++			buf += MTK_WED_PAGE_BUF_SIZE;
++			buf_phys += MTK_WED_PAGE_BUF_SIZE;
++		}
++
++		dma_sync_single_for_device(dev->hw->dev, page_phys, PAGE_SIZE,
++					   DMA_BIDIRECTIONAL);
++	}
++
++	return 0;
++}
++
++static void
++mtk_wed_rx_page_free_buffer(struct mtk_wed_device *dev)
++{
++	struct mtk_rxbm_desc *desc = dev->rx_page_buf_ring.desc;
++	struct dma_page_info *page_list = dev->rx_page_buf_ring.pages;
++	int ring_size, page_idx;
++	int i;
++
++	if (!page_list)
++		return;
++
++	if (!desc)
++		goto free_pagelist;
++
++	ring_size = dev->rx_page_buf_ring.pkt_nums;
++
++	for (i = 0, page_idx = 0; i < ring_size; i += MTK_WED_RX_PAGE_BUF_PER_PAGE) {
++		void *page = page_list[page_idx].addr;
++
++		if (!page)
++			break;
++
++		dma_unmap_page(dev->hw->dev, page_list[page_idx].addr_phys,
++                              PAGE_SIZE, DMA_BIDIRECTIONAL);
++		__free_page(page);
++		page_idx++;
++       }
++
++	dma_free_coherent(dev->hw->dev, ring_size * sizeof(*desc),
++                         desc, dev->rx_page_buf_ring.desc_phys);
++
++free_pagelist:
++       kfree(page_list);
++}
++
+ static void
+ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, int scale)
+ {
+@@ -416,19 +618,25 @@ mtk_wed_free_ring(struct mtk_wed_device *dev, struct mtk_wed_ring *ring, int sca
+ static void
+ mtk_wed_free_tx_rings(struct mtk_wed_device *dev)
+ {
+-	int i;
++	int i, scale = dev->hw->version > 1 ? 2 : 1;
+ 
+ 	for (i = 0; i < ARRAY_SIZE(dev->tx_ring); i++)
+-		mtk_wed_free_ring(dev, &dev->tx_ring[i], 1);
++		if (!(dev->rx_ring[i].flags & MTK_WED_RING_CONFIGURED))
++			mtk_wed_free_ring(dev, &dev->tx_ring[i], 1);
++
+ 	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
+-		mtk_wed_free_ring(dev, &dev->tx_wdma[i], dev->ver);
++		if ((dev->rx_ring[i].flags & MTK_WED_RING_CONFIGURED))
++			mtk_wed_free_ring(dev, &dev->tx_wdma[i], scale);
+ }
+ 
+ static void
+ mtk_wed_free_rx_rings(struct mtk_wed_device *dev)
+ {
+-	mtk_wed_free_rx_bm(dev);
++	mtk_wed_free_rx_buffer(dev);
+ 	mtk_wed_free_ring(dev, &dev->rro.rro_ring, 1);
++
++	if (dev->wlan.hwrro)
++		mtk_wed_rx_page_free_buffer(dev);
+ }
+ 
+ static void
+@@ -437,7 +645,7 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+ 	u32 wdma_mask;
+ 
+ 	wdma_mask = FIELD_PREP(MTK_WDMA_INT_MASK_RX_DONE, GENMASK(1, 0));
+-	if (dev->ver > MTK_WED_V1)
++	if (mtk_wed_get_rx_capa(dev))
+ 		wdma_mask |= FIELD_PREP(MTK_WDMA_INT_MASK_TX_DONE,
+ 					GENMASK(1, 0));
+ 	/* wed control cr set */
+@@ -447,7 +655,7 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+ 		MTK_WED_CTRL_WED_TX_BM_EN |
+ 		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+ 
+-	if (dev->ver == MTK_WED_V1) {
++	if (dev->hw->version == 1) {
+ 		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER,
+ 			MTK_WED_PCIE_INT_TRIGGER_STATUS);
+ 
+@@ -458,6 +666,8 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+ 		wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
+ 			MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
+ 	} else {
++		if (dev->hw->version == 3)
++			wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_TKID_ALI_EN);
+ 
+ 		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_TX,
+ 			MTK_WED_WPDMA_INT_CTRL_TX0_DONE_EN |
+@@ -475,18 +685,20 @@ mtk_wed_set_int(struct mtk_wed_device *dev, u32 irq_mask)
+ 			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_TX_FREE_DONE_TRIG,
+ 				    dev->wlan.txfree_tbit));
+ 
+-		wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
+-			MTK_WED_WPDMA_INT_CTRL_RX0_EN |
+-			MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
+-			MTK_WED_WPDMA_INT_CTRL_RX1_EN |
+-			MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
+-			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
+-				   dev->wlan.rx_tbit[0]) |
+-			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
+-				   dev->wlan.rx_tbit[1]));
++		if (mtk_wed_get_rx_capa(dev))
++			wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RX,
++				MTK_WED_WPDMA_INT_CTRL_RX0_EN |
++				MTK_WED_WPDMA_INT_CTRL_RX0_CLR |
++				MTK_WED_WPDMA_INT_CTRL_RX1_EN |
++				MTK_WED_WPDMA_INT_CTRL_RX1_CLR |
++				FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX0_DONE_TRIG,
++					   dev->wlan.rx_tbit[0]) |
++				FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RX1_DONE_TRIG,
++					   dev->wlan.rx_tbit[1]));
+ 	}
++
+ 	wed_w32(dev, MTK_WED_WDMA_INT_TRIGGER, wdma_mask);
+-	if (dev->ver == MTK_WED_V1) {
++	if (dev->hw->version == 1) {
+ 		wed_clr(dev, MTK_WED_WDMA_INT_CTRL, wdma_mask);
+ 	} else {
+ 		wed_w32(dev, MTK_WED_WDMA_INT_CLR, wdma_mask);
+@@ -506,6 +718,21 @@ mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en)
+ {
+ 	u32 mask = MTK_WED_EXT_INT_STATUS_ERROR_MASK;
+ 
++	switch (dev->hw->version) {
++	case 1:
++		mask |= MTK_WED_EXT_INT_STATUS_TX_DRV_R_RESP_ERR;
++		break;
++	case 2 :
++		mask |= MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH2 |
++			MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH2 |
++			MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT |
++			MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR;
++		break;
++	case 3:
++		mask = MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT;
++		break;
++	}
++
+ 	if (!dev->hw->num_flows)
+ 		mask &= ~MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD;
+ 
+@@ -514,31 +741,86 @@ mtk_wed_set_ext_int(struct mtk_wed_device *dev, bool en)
+ }
+ 
+ static void
+-mtk_wed_set_512_support(struct mtk_wed_device *dev, bool en)
++mtk_wed_pao_init(struct mtk_wed_device *dev)
+ {
+-	if (en) {
+-		wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
+-		wed_w32(dev, MTK_WED_TXP_DW1,
+-			FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0103));
+-	} else {
+-		wed_w32(dev, MTK_WED_TXP_DW1,
+-			FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0100));
+-		wed_clr(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
++	struct mtk_wed_pao *pao = dev->hw->wed_pao;
++	int i;
++
++	for (i = 0; i < 32; i++)
++		wed_w32(dev, MTK_WED_PAO_HIFTXD_BASE_L(i),
++			pao->hif_txd_phys[i]);
++
++	/* init all sta parameter */
++	wed_w32(dev, MTK_WED_PAO_STA_INFO_INIT, MTK_WED_PAO_STA_RMVL |
++		MTK_WED_PAO_STA_WTBL_HDRT_MODE |
++		FIELD_PREP(MTK_WED_PAO_STA_MAX_AMSDU_LEN,
++			   dev->wlan.max_amsdu_len >> 8) |
++		FIELD_PREP(MTK_WED_PAO_STA_MAX_AMSDU_NUM,
++			   dev->wlan.max_amsdu_nums));
++
++	wed_w32(dev, MTK_WED_PAO_STA_INFO, MTK_WED_PAO_STA_INFO_DO_INIT);
++
++	if (mtk_wed_poll_busy(dev, MTK_WED_PAO_STA_INFO,
++			      MTK_WED_PAO_STA_INFO_DO_INIT)) {
++		dev_err(dev->hw->dev, "mtk_wed%d: pao init failed!\n",
++			dev->hw->index);
++		return;
+ 	}
++
++	/* init pao txd src */
++	wed_set(dev, MTK_WED_PAO_HIFTXD_CFG,
++		FIELD_PREP(MTK_WED_PAO_HIFTXD_SRC, dev->hw->index));
++
++	/* init qmem */
++	wed_set(dev, MTK_WED_PAO_PSE, MTK_WED_PAO_PSE_RESET);
++	if (mtk_wed_poll_busy(dev, MTK_WED_PAO_MON_QMEM_STS1, BIT(29))) {
++		pr_info("%s: init pao qmem fail\n", __func__);
++		return;
++	}
++
++	/* eagle E1 PCIE1 tx ring 22 flow control issue */
++	if (dev->wlan.chip_id == 0x7991) {
++		wed_clr(dev, MTK_WED_PAO_AMSDU_FIFO,
++			MTK_WED_PAO_AMSDU_IS_PRIOR0_RING);
++	}
++
++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_PAO_EN);
++
++	return;
+ }
+ 
+-static void
+-mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
++static int
++mtk_wed_hwrro_init(struct mtk_wed_device *dev)
+ {
+-#define MTK_WFMDA_RX_DMA_EN 	BIT(2)
++	if (!mtk_wed_get_rx_capa(dev))
++		return 0;
++
++	wed_set(dev, MTK_WED_RRO_PG_BM_RX_DMAM,
++		FIELD_PREP(MTK_WED_RRO_PG_BM_RX_SDL0, 128));
++
++	wed_w32(dev, MTK_WED_RRO_PG_BM_BASE,
++		dev->rx_page_buf_ring.desc_phys);
++
++	wed_w32(dev, MTK_WED_RRO_PG_BM_INIT_PTR,
++		MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX |
++		FIELD_PREP(MTK_WED_RRO_PG_BM_SW_TAIL_IDX,
++			   MTK_WED_RX_PG_BM_CNT));
++
++	/* enable rx_page_bm to fetch dmad */
++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
++
++	return 0;
++}
+ 
++static int
++mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev,
++			   struct mtk_wed_ring *ring)
++{
+ 	int timeout = 3;
+-	u32 cur_idx, regs;
++	u32 cur_idx;
+ 
+ 	do {
+-		regs = MTK_WED_WPDMA_RING_RX_DATA(idx) +
+-		       MTK_WED_RING_OFS_CPU_IDX;
+-		cur_idx = wed_r32(dev, regs);
++		cur_idx = readl(ring->wpdma + MTK_WED_RING_OFS_CPU_IDX);
+ 		if (cur_idx == MTK_WED_RX_RING_SIZE - 1)
+ 			break;
+ 
+@@ -546,70 +828,133 @@ mtk_wed_check_wfdma_rx_fill(struct mtk_wed_device *dev, int idx)
+ 		timeout--;
+ 	} while (timeout > 0);
+ 
+-	if (timeout) {
+-		unsigned int val;
++	return timeout;
++}
+ 
+-		val = wifi_r32(dev, dev->wlan.wpdma_rx_glo -
+-			       dev->wlan.phy_base);
+-		val |= MTK_WFMDA_RX_DMA_EN;
+ 
+-		wifi_w32(dev, dev->wlan.wpdma_rx_glo -
+-			 dev->wlan.phy_base, val);
++static void
++mtk_wed_set_512_support(struct mtk_wed_device *dev, bool en)
++{
++	if (en) {
++		wed_w32(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
++		wed_w32(dev, MTK_WED_TXP_DW1,
++			FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0103));
+ 	} else {
+-		dev_err(dev->hw->dev, "mtk_wed%d: rx(%d) dma enable failed!\n",
+-			       dev->hw->index, idx);
++		wed_w32(dev, MTK_WED_TXP_DW1,
++			FIELD_PREP(MTK_WED_WPDMA_WRITE_TXP, 0x0100));
++		wed_clr(dev, MTK_WED_TXDP_CTRL, MTK_WED_TXDP_DW9_OVERWR);
+ 	}
+ }
+ 
+ static void
+ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+ {
+-	wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
+-		MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
++#define MTK_WFMDA_RX_DMA_EN 	BIT(2)
++
++	if (dev->hw->version == 1)
++		wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
++			MTK_WED_WPDMA_INT_CTRL_SUBRT_ADV);
+ 
+ 	wed_set(dev, MTK_WED_GLO_CFG,
+ 		MTK_WED_GLO_CFG_TX_DMA_EN |
+ 		MTK_WED_GLO_CFG_RX_DMA_EN);
++
++	wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
++		FIELD_PREP(MTK_WED_WDMA_RX_PREF_BURST_SIZE, 0x10) |
++		FIELD_PREP(MTK_WED_WDMA_RX_PREF_LOW_THRES, 0x8));
++	wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++		MTK_WED_WDMA_RX_PREF_DDONE2_EN);
++
++	wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN);
++
+ 	wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
+ 		MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
+-		MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
++		MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN |
++		MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR);
+ 	wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+ 		MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+ 
+ 	wdma_set(dev, MTK_WDMA_GLO_CFG,
+-		 MTK_WDMA_GLO_CFG_TX_DMA_EN |
++		 MTK_WDMA_GLO_CFG_TX_DMA_EN /*|
+ 		 MTK_WDMA_GLO_CFG_RX_INFO1_PRERES |
+-		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES);
++		 MTK_WDMA_GLO_CFG_RX_INFO2_PRERES*/);
+ 
+-	if (dev->ver == MTK_WED_V1) {
++	if (dev->hw->version == 1) {
+ 		wdma_set(dev, MTK_WDMA_GLO_CFG,
+ 			 MTK_WDMA_GLO_CFG_RX_INFO3_PRERES);
+ 	} else {
+ 		int idx = 0;
+ 
+-		wed_set(dev, MTK_WED_WPDMA_CTRL,
+-			MTK_WED_WPDMA_CTRL_SDL1_FIXED);
+-
+-		wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+-			MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
+-			MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
++		if (mtk_wed_get_rx_capa(dev))
++			wed_set(dev, MTK_WED_WDMA_GLO_CFG,
++				MTK_WED_WDMA_GLO_CFG_TX_DRV_EN |
++				MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
+ 
+ 		wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
+ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_PKT_PROC |
+ 			MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC);
+ 
++		if (dev->hw->version == 3) {
++			wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
++				MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST);
++			wed_set(dev, MTK_WED_WPDMA_GLO_CFG,
++				MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK |
++				MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK |
++				MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
++
++			wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++			//wdma_w32(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++			if (mtk_wed_get_rx_capa(dev)) {
++				wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++					MTK_WED_WPDMA_RX_D_PREF_EN |
++					FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE, 0x10) |
++					FIELD_PREP(MTK_WED_WPDMA_RX_D_PREF_LOW_THRES, 0x8));
++
++				wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
++
++				wdma_set(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++
++				wdma_set(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++			}
++		}
++
+ 		wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+ 			MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP |
+ 			MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV);
+ 
++		if (!mtk_wed_get_rx_capa(dev))
++			return;
++
++		wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RXD_READ_LEN);
+ 		wed_set(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+ 			MTK_WED_WPDMA_RX_D_RX_DRV_EN |
+ 			FIELD_PREP(MTK_WED_WPDMA_RX_D_RXD_READ_LEN, 0x18) |
+ 			FIELD_PREP(MTK_WED_WPDMA_RX_D_INIT_PHASE_RXEN_SEL,
+ 				   0x2));
+ 
+-		for (idx = 0; idx < dev->hw->ring_num; idx++)
+-			mtk_wed_check_wfdma_rx_fill(dev, idx);
++		for (idx = 0; idx < dev->hw->ring_num; idx++) {
++			struct mtk_wed_ring *ring = &dev->rx_ring[idx];
++
++			if(!(ring->flags & MTK_WED_RING_CONFIGURED))
++				continue;
++
++			if(mtk_wed_check_wfdma_rx_fill(dev, ring)) {
++				unsigned int val;
++
++				val = wifi_r32(dev, dev->wlan.wpdma_rx_glo -
++					       dev->wlan.phy_base);
++				val |= MTK_WFMDA_RX_DMA_EN;
++
++				wifi_w32(dev, dev->wlan.wpdma_rx_glo -
++					 dev->wlan.phy_base, val);
++
++				dev_err(dev->hw->dev, "mtk_wed%d: rx(%d) dma enable successful!\n",
++						dev->hw->index, idx);
++			} else {
++				dev_err(dev->hw->dev, "mtk_wed%d: rx(%d) dma enable failed!\n",
++					dev->hw->index, idx);
++			}
++		}
+ 	}
+ }
+ 
+@@ -644,15 +989,20 @@ mtk_wed_dma_disable(struct mtk_wed_device *dev)
+ 			MTK_WED_WPDMA_RX_D_RX_DRV_EN);
+ 		wed_clr(dev, MTK_WED_WDMA_GLO_CFG,
+ 			MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK);
+-	}
+ 
+-	mtk_wed_set_512_support(dev, false);
++		if (dev->hw->version == 3 && mtk_wed_get_rx_capa(dev)) {
++			wdma_clr(dev, MTK_WDMA_PREF_TX_CFG,
++				 MTK_WDMA_PREF_TX_CFG_PREF_EN);
++			wdma_clr(dev, MTK_WDMA_PREF_RX_CFG,
++				 MTK_WDMA_PREF_RX_CFG_PREF_EN);
++		}
++	}
+ }
+ 
+ static void
+ mtk_wed_stop(struct mtk_wed_device *dev)
+ {
+-	if (dev->ver > MTK_WED_V1) {
++	if (mtk_wed_get_rx_capa(dev)) {
+ 		wed_w32(dev, MTK_WED_EXT_INT_MASK1, 0);
+ 		wed_w32(dev, MTK_WED_EXT_INT_MASK2, 0);
+ 	}
+@@ -677,13 +1027,21 @@ mtk_wed_deinit(struct mtk_wed_device *dev)
+ 		MTK_WED_CTRL_WED_TX_BM_EN |
+ 		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+ 
+-	if (dev->hw->ver == 1)
++	if (dev->hw->version == 1)
+ 		return;
+ 
+ 	wed_clr(dev, MTK_WED_CTRL,
+ 		MTK_WED_CTRL_RX_ROUTE_QM_EN |
+ 		MTK_WED_CTRL_WED_RX_BM_EN |
+ 		MTK_WED_CTRL_RX_RRO_QM_EN);
++
++	if (dev->hw->version == 3) {
++		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_PAO_EN);
++		wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_TX_PAO);
++		wed_clr(dev, MTK_WED_PCIE_INT_CTRL,
++			MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
++			MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER);
++	}
+ }
+ 
+ static void
+@@ -702,9 +1060,9 @@ mtk_wed_detach(struct mtk_wed_device *dev)
+ 
+ 	mtk_wdma_tx_reset(dev);
+ 
+-	mtk_wed_free_buffer(dev);
++	mtk_wed_free_tx_buffer(dev);
+ 	mtk_wed_free_tx_rings(dev);
+-	if (dev->ver > MTK_WED_V1) {
++	if (mtk_wed_get_rx_capa(dev)) {
+ 		mtk_wed_wo_reset(dev);
+ 		mtk_wed_free_rx_rings(dev);
+ 		mtk_wed_wo_exit(hw);
+@@ -728,73 +1086,97 @@ mtk_wed_detach(struct mtk_wed_device *dev)
+ 	mutex_unlock(&hw_lock);
+ }
+ 
++#define IRQ_MASK_APMCU		0x1000301c
+ static void
+ mtk_wed_bus_init(struct mtk_wed_device *dev)
+ {
+-#define PCIE_BASE_ADDR0 0x11280000
++	switch (dev->wlan.bus_type) {
++	case MTK_WED_BUS_PCIE: {
++		struct device_node *np = dev->hw->eth->dev->of_node;
++		struct regmap *regs;
++		unsigned long addr;
++		u32 value;
+ 
+-	if (dev->wlan.bus_type == MTK_WED_BUS_PCIE) {
+-		struct device_node *node;
+-		void __iomem * base_addr;
+-		u32 value = 0;
++		if (dev->hw->version == 2) {
++			regs = syscon_regmap_lookup_by_phandle(np,
++							       "mediatek,wed-pcie");
++			if (IS_ERR(regs))
++				break;
+ 
+-		node = of_parse_phandle(dev->hw->node, "mediatek,wed_pcie", 0);
+-		if (!node) {
+-			pr_err("%s: no wed_pcie node\n", __func__);
+-			return;
++			regmap_update_bits(regs, 0, BIT(0), BIT(0));
+ 		}
+ 
+-		base_addr = of_iomap(node, 0);
+-
+-		value = readl(base_addr);
+-		value |= BIT(0);
+-		writel(value, base_addr);
++		if (dev->wlan.msi) {
++		     wed_w32(dev, MTK_WED_PCIE_CFG_INTM, dev->hw->pci_base| 0xc08);
++		     wed_w32(dev, MTK_WED_PCIE_CFG_BASE, dev->hw->pci_base | 0xc04);
++		     wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(8));
++		} else {
++		     wed_w32(dev, MTK_WED_PCIE_CFG_INTM, dev->hw->pci_base | 0x180);
++		     wed_w32(dev, MTK_WED_PCIE_CFG_BASE, dev->hw->pci_base | 0x184);
++		     wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
++		}
+ 
+-		wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
+-			FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
++		if (dev->hw->version < 3 || dev->hw->index) {
++			wed_w32(dev, MTK_WED_PCIE_INT_CTRL,
++				FIELD_PREP(MTK_WED_PCIE_INT_CTRL_POLL_EN, 2));
++		} else {
++			/* set mask apmcu */
++			addr = (unsigned long)ioremap(IRQ_MASK_APMCU, 4);
++			value = readl((void *)addr);
++			value |= 0x7;
++			writel(value, (void *)addr);
++			iounmap((void *)addr);
++		}
+ 
+ 		/* pcie interrupt control: pola/source selection */
+ 		wed_set(dev, MTK_WED_PCIE_INT_CTRL,
+ 			MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA |
+-			FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, 1));
+-		wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
++			MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER |
++			FIELD_PREP(MTK_WED_PCIE_INT_CTRL_SRC_SEL, dev->hw->index));
+ 
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
+-		wed_w32(dev, MTK_WED_PCIE_CFG_INTM, PCIE_BASE_ADDR0 | 0x180);
+-		wed_w32(dev, MTK_WED_PCIE_CFG_BASE, PCIE_BASE_ADDR0 | 0x184);
+-
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_INTM);
+-		value = wed_r32(dev, MTK_WED_PCIE_CFG_BASE);
+-
+-		wed_w32(dev, MTK_WED_PCIE_INT_TRIGGER, BIT(24));
+-		wed_r32(dev, MTK_WED_PCIE_INT_TRIGGER);
+-
+-		/* pola setting */
+-		value = wed_r32(dev, MTK_WED_PCIE_INT_CTRL);
+-		wed_set(dev, MTK_WED_PCIE_INT_CTRL,
+-			MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA);
+-	} else if (dev->wlan.bus_type == MTK_WED_BUS_AXI) {
++		break;
++	}
++	case MTK_WED_BUS_AXI:
+ 		wed_set(dev, MTK_WED_WPDMA_INT_CTRL,
+ 			MTK_WED_WPDMA_INT_CTRL_SIG_SRC |
+ 			FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_SRC_SEL, 0));
++		break;
++	default:
++		break;
+ 	}
++
+ 	return;
+ }
+ 
+ static void
+ mtk_wed_set_wpdma(struct mtk_wed_device *dev)
+ {
+-	if (dev->ver > MTK_WED_V1) {
++	if (dev->hw->version == 1) {
++		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
++	} else {
++		mtk_wed_bus_init(dev);
++
+ 		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_int);
+ 		wed_w32(dev, MTK_WED_WPDMA_CFG_INT_MASK,  dev->wlan.wpdma_mask);
+-		wed_w32(dev, MTK_WED_WPDMA_CFG_TX,  dev->wlan.wpdma_tx);
++		wed_w32(dev, MTK_WED_WPDMA_CFG_TX, dev->wlan.wpdma_tx);
+ 		wed_w32(dev, MTK_WED_WPDMA_CFG_TX_FREE,  dev->wlan.wpdma_txfree);
+ 
+-		wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG,  dev->wlan.wpdma_rx_glo);
+-		wed_w32(dev, MTK_WED_WPDMA_RX_RING,  dev->wlan.wpdma_rx);
+-	} else {
+-		wed_w32(dev, MTK_WED_WPDMA_CFG_BASE,  dev->wlan.wpdma_phys);
++		if (mtk_wed_get_rx_capa(dev)) {
++			int i;
++
++			wed_w32(dev, MTK_WED_WPDMA_RX_GLO_CFG,  dev->wlan.wpdma_rx_glo);
++			wed_w32(dev, MTK_WED_WPDMA_RX_RING0,  dev->wlan.wpdma_rx);
++			wed_w32(dev, MTK_WED_WPDMA_RX_RING1,  dev->wlan.wpdma_rx + 0x10);
++
++			if (dev->wlan.hwrro) {
++	                       wed_w32(dev, MTK_WED_RRO_RX_D_CFG(0), dev->wlan.wpdma_rx_rro[0]);
++	                       wed_w32(dev, MTK_WED_RRO_RX_D_CFG(1), dev->wlan.wpdma_rx_rro[1]);
++	                       for (i = 0; i < MTK_WED_RX_PAGE_QUEUES; i++) {
++	                               wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING_CFG(i),
++	                                       dev->wlan.wpdma_rx_pg + i * 0x10);
++			       }
++			}
++		}
+ 	}
+ }
+ 
+@@ -806,21 +1188,25 @@ mtk_wed_hw_init_early(struct mtk_wed_device *dev)
+ 	mtk_wed_deinit(dev);
+ 	mtk_wed_reset(dev, MTK_WED_RESET_WED);
+ 
+-	if (dev->ver > MTK_WED_V1)
+-		mtk_wed_bus_init(dev);
+-
+ 	mtk_wed_set_wpdma(dev);
+ 
+-	mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
+-	       MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
+-	       MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
+-	set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) |
+-	      MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
+-	      MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
++	if (dev->hw->version == 3) {
++		mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE;
++		set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2);
++	} else {
++		mask = MTK_WED_WDMA_GLO_CFG_BT_SIZE |
++		       MTK_WED_WDMA_GLO_CFG_DYNAMIC_DMAD_RECYCLE |
++		       MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
++		set = FIELD_PREP(MTK_WED_WDMA_GLO_CFG_BT_SIZE, 2) |
++		      MTK_WED_WDMA_GLO_CFG_DYNAMIC_SKIP_DMAD_PREP |
++		      MTK_WED_WDMA_GLO_CFG_IDLE_DMAD_SUPPLY;
++	}
++
+ 	wed_m32(dev, MTK_WED_WDMA_GLO_CFG, mask, set);
+ 
+-	if (dev->ver == MTK_WED_V1) {
++	if (dev->hw->version == 1) {
+ 		u32 offset;
++
+ 		offset = dev->hw->index ? 0x04000400 : 0;
+ 		wed_w32(dev, MTK_WED_WDMA_OFFSET0, 0x2a042a20 + offset);
+ 		wed_w32(dev, MTK_WED_WDMA_OFFSET1, 0x29002800 + offset);
+@@ -907,11 +1293,16 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
+ 	} while (1);
+ 
+ 	/* configure RX_ROUTE_QM */
+-	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
+-	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
+-	wed_set(dev, MTK_WED_RTQM_GLO_CFG,
+-		FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
+-	wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++	if (dev->hw->version == 2) {
++		wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++		wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_TXDMAD_FPORT);
++		wed_set(dev, MTK_WED_RTQM_GLO_CFG,
++			FIELD_PREP(MTK_WED_RTQM_TXDMAD_FPORT, 0x3 + dev->hw->index));
++		wed_clr(dev, MTK_WED_RTQM_GLO_CFG, MTK_WED_RTQM_Q_RST);
++	} else {
++		wed_set(dev, MTK_WED_RTQM_ENQ_CFG0,
++			FIELD_PREP(MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT, 0x3 + dev->hw->index));
++	}
+ 
+ 	/* enable RX_ROUTE_QM */
+ 	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
+@@ -920,23 +1311,45 @@ mtk_wed_route_qm_hw_init(struct mtk_wed_device *dev)
+ static void
+ mtk_wed_tx_hw_init(struct mtk_wed_device *dev)
+ {
+-	int size = dev->buf_ring.size;
++	int size = dev->wlan.nbuf;
+ 	int rev_size = MTK_WED_TX_RING_SIZE / 2;
+-	int thr = 1;
++	int thr_lo = 1, thr_hi = 1;
+ 
+-	if (dev->ver > MTK_WED_V1) {
++	if (dev->hw->version == 1) {
++		wed_w32(dev, MTK_WED_TX_BM_CTRL,
++			MTK_WED_TX_BM_CTRL_PAUSE |
++			FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, size / 128) |
++			FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, rev_size / 128));
++	} else {
+ 		size = MTK_WED_WDMA_RING_SIZE * ARRAY_SIZE(dev->tx_wdma) +
+-		       dev->buf_ring.size;
++		       dev->tx_buf_ring.size;
+ 		rev_size = size;
+-		thr = 0;
++		thr_lo = 0;
++		thr_hi = MTK_WED_TX_BM_DYN_THR_HI;
++
++		wed_w32(dev, MTK_WED_TX_TKID_CTRL,
++			MTK_WED_TX_TKID_CTRL_PAUSE |
++			FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM,
++				   size / 128) |
++			FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
++				   size / 128));
++
++		/* return SKBID + SDP back to bm */
++		if (dev->ver == 3) {
++			wed_set(dev, MTK_WED_TX_TKID_CTRL,
++				MTK_WED_TX_TKID_CTRL_FREE_FORMAT);
++			 size = dev->wlan.nbuf;
++			 rev_size = size;
++		} else {
++			wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
++				FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
++				MTK_WED_TX_TKID_DYN_THR_HI);
++		}
+ 	}
+ 
+-	wed_w32(dev, MTK_WED_TX_BM_CTRL,
+-		MTK_WED_TX_BM_CTRL_PAUSE |
+-		FIELD_PREP(MTK_WED_TX_BM_CTRL_VLD_GRP_NUM, size / 128) |
+-		FIELD_PREP(MTK_WED_TX_BM_CTRL_RSV_GRP_NUM, rev_size / 128));
++	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
+ 
+-	wed_w32(dev, MTK_WED_TX_BM_BASE, dev->buf_ring.desc_phys);
++	wed_w32(dev, MTK_WED_TX_BM_BASE, dev->tx_buf_ring.desc_phys);
+ 
+ 	wed_w32(dev, MTK_WED_TX_BM_TKID,
+ 		FIELD_PREP(MTK_WED_TX_BM_TKID_START,
+@@ -946,25 +1359,44 @@ mtk_wed_tx_hw_init(struct mtk_wed_device *dev)
+ 
+ 	wed_w32(dev, MTK_WED_TX_BM_BUF_LEN, MTK_WED_PKT_SIZE);
+ 
+-	wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
+-		FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, thr) |
+-		MTK_WED_TX_BM_DYN_THR_HI);
++	if (dev->hw->version < 3)
++		wed_w32(dev, MTK_WED_TX_BM_DYN_THR,
++			FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, thr_lo) |
++			FIELD_PREP(MTK_WED_TX_BM_DYN_THR_LO, thr_hi));
++	else {
++		/* change to new bm */
++		wed_w32(dev, MTK_WED_TX_BM_INIT_PTR, dev->tx_buf_ring.pkt_nums |
++				MTK_WED_TX_BM_INIT_SW_TAIL_IDX);
++		wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_LEGACY_EN);
++	}
+ 
+-	if (dev->ver > MTK_WED_V1) {
++	if (dev->hw->version != 1) {
+ 		wed_w32(dev, MTK_WED_TX_TKID_CTRL,
+ 			MTK_WED_TX_TKID_CTRL_PAUSE |
+ 			FIELD_PREP(MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM,
+-				   dev->buf_ring.size / 128) |
++				   size / 128) |
+ 			FIELD_PREP(MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM,
+-				   dev->buf_ring.size / 128));
+-		wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
+-			FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
+-			MTK_WED_TX_TKID_DYN_THR_HI);
++				   size / 128));
++
++		/* return SKBID + SDP back to bm */
++		if (dev->ver == 3)
++			wed_set(dev, MTK_WED_TX_TKID_CTRL,
++				MTK_WED_TX_TKID_CTRL_FREE_FORMAT);
++		else
++			wed_w32(dev, MTK_WED_TX_TKID_DYN_THR,
++				FIELD_PREP(MTK_WED_TX_TKID_DYN_THR_LO, 0) |
++				MTK_WED_TX_TKID_DYN_THR_HI);
+ 	}
+-	mtk_wed_reset(dev, MTK_WED_RESET_TX_BM);
++	wed_w32(dev, MTK_WED_TX_BM_TKID,
++		FIELD_PREP(MTK_WED_TX_BM_TKID_START,
++			   dev->wlan.token_start) |
++		FIELD_PREP(MTK_WED_TX_BM_TKID_END,
++			   dev->wlan.token_start + dev->wlan.nbuf - 1));
+ 
++	wed_w32(dev, MTK_WED_TX_BM_INIT_PTR, dev->tx_buf_ring.pkt_nums |
++		MTK_WED_TX_BM_INIT_SW_TAIL_IDX);
+ 	wed_clr(dev, MTK_WED_TX_BM_CTRL, MTK_WED_TX_BM_CTRL_PAUSE);
+-	if (dev->ver > MTK_WED_V1)
++	if (dev->hw->version != 1)
+ 		wed_clr(dev, MTK_WED_TX_TKID_CTRL, MTK_WED_TX_TKID_CTRL_PAUSE);
+ }
+ 
+@@ -977,7 +1409,26 @@ mtk_wed_rx_hw_init(struct mtk_wed_device *dev)
+ 
+ 	wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX, 0);
+ 
++	/* reset prefetch index of ring */
++	wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
++		MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++	wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX,
++		MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++
++	wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
++		MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++	wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX,
++		MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR);
++
++	/* reset prefetch FIFO of ring */
++	wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG,
++		MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR |
++		MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR);
++	wed_w32(dev, MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG, 0);
++
+ 	mtk_wed_rx_bm_hw_init(dev);
++	if (dev->wlan.hwrro)
++		mtk_wed_hwrro_init(dev);
+ 	mtk_wed_rro_hw_init(dev);
+ 	mtk_wed_route_qm_hw_init(dev);
+ }
+@@ -991,7 +1442,7 @@ mtk_wed_hw_init(struct mtk_wed_device *dev)
+ 	dev->init_done = true;
+ 	mtk_wed_set_ext_int(dev, false);
+ 	mtk_wed_tx_hw_init(dev);
+-	if (dev->ver > MTK_WED_V1)
++	if (mtk_wed_get_rx_capa(dev))
+ 		mtk_wed_rx_hw_init(dev);
+ }
+ 
+@@ -1015,26 +1466,6 @@ mtk_wed_ring_reset(struct mtk_wdma_desc *desc, int size, int scale, bool tx)
+ 	}
+ }
+ 
+-static u32
+-mtk_wed_check_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+-{
+- 	if (wed_r32(dev, reg) & mask)
+-		return true;
+-
+-	return false;
+-}
+-
+-static int
+-mtk_wed_poll_busy(struct mtk_wed_device *dev, u32 reg, u32 mask)
+-{
+-	int sleep = 1000;
+-	int timeout = 100 * sleep;
+-	u32 val;
+-
+-	return read_poll_timeout(mtk_wed_check_busy, val, !val, sleep,
+-				 timeout, false, dev, reg, mask);
+-}
+-
+ static void
+ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ {
+@@ -1133,7 +1564,7 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 		mtk_wed_ring_reset(desc, MTK_WED_RX_RING_SIZE, 1, false);
+ 	}
+ 
+-	mtk_wed_free_rx_bm(dev);
++	mtk_wed_free_rx_buffer(dev);
+ }
+ 
+ 
+@@ -1271,12 +1702,15 @@ mtk_wed_wdma_rx_ring_setup(struct mtk_wed_device *dev,
+ 		int idx, int size, bool reset)
+ {
+ 	struct mtk_wed_ring *wdma = &dev->tx_wdma[idx];
++	int scale = dev->hw->version > 1 ? 2 : 1;
+ 
+ 	if(!reset)
+ 		if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
+-				       dev->ver, true))
++				       scale, true))
+ 			return -ENOMEM;
+ 
++	wdma->flags |= MTK_WED_RING_CONFIGURED;
++
+ 	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_BASE,
+ 		 wdma->desc_phys);
+ 	wdma_w32(dev, MTK_WDMA_RING_RX(idx) + MTK_WED_RING_OFS_COUNT,
+@@ -1296,12 +1730,31 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev,
+ 	int idx, int size, bool reset)
+ {
+ 	struct mtk_wed_ring *wdma = &dev->rx_wdma[idx];
++	int scale = dev->hw->version > 1 ? 2 : 1;
+ 
+ 	if (!reset)
+ 		if (mtk_wed_ring_alloc(dev, wdma, MTK_WED_WDMA_RING_SIZE,
+-				       dev->ver, true))
++				       scale, true))
+ 			return -ENOMEM;
+ 
++	if (dev->hw->version == 3) {
++		struct mtk_wdma_desc *desc = wdma->desc;
++		int i;
++
++		for (i = 0; i < MTK_WED_WDMA_RING_SIZE; i++) {
++			desc->buf0 = 0;
++			desc->ctrl = MTK_WDMA_DESC_CTRL_DMA_DONE;
++			desc->buf1 = 0;
++			desc->info = MTK_WDMA_TXD0_DESC_INFO_DMA_DONE;
++			desc++;
++			desc->buf0 = 0;
++			desc->ctrl = MTK_WDMA_DESC_CTRL_DMA_DONE;
++			desc->buf1 = 0;
++			desc->info = MTK_WDMA_TXD1_DESC_INFO_DMA_DONE;
++			desc++;
++		}
++	}
++
+ 	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_BASE,
+ 		 wdma->desc_phys);
+ 	wdma_w32(dev, MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_COUNT,
+@@ -1312,7 +1765,7 @@ mtk_wed_wdma_tx_ring_setup(struct mtk_wed_device *dev,
+ 		 MTK_WDMA_RING_TX(idx) + MTK_WED_RING_OFS_DMA_IDX, 0);
+ 	if (reset)
+ 		mtk_wed_ring_reset(wdma->desc, MTK_WED_WDMA_RING_SIZE,
+-				   dev->ver, true);
++				   scale, true);
+ 	if (idx == 0)  {
+ 		wed_w32(dev, MTK_WED_WDMA_RING_TX
+ 			+ MTK_WED_RING_OFS_BASE, wdma->desc_phys);
+@@ -1395,7 +1848,7 @@ mtk_wed_send_msg(struct mtk_wed_device *dev, int cmd_id, void *data, int len)
+ {
+ 	struct mtk_wed_wo *wo = dev->hw->wed_wo;
+ 
+-	if (dev->ver == MTK_WED_V1)
++	if (!mtk_wed_get_rx_capa(dev))
+ 		return 0;
+ 
+ 	return mtk_wed_mcu_send_msg(wo, MODULE_ID_WO, cmd_id, data, len, true);
+@@ -1420,13 +1873,87 @@ mtk_wed_ppe_check(struct mtk_wed_device *dev, struct sk_buff *skb,
+ 	}
+ }
+ 
++static void
++mtk_wed_start_hwrro(struct mtk_wed_device *dev, u32 irq_mask)
++{
++	int idx, ret;
++
++	wed_w32(dev, MTK_WED_WPDMA_INT_MASK, irq_mask);
++	wed_w32(dev, MTK_WED_INT_MASK, irq_mask);
++
++	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hwrro)
++		return;
++
++	wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
++	wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_CLR);
++
++	wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_RX,
++		MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN |
++		MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR |
++		MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN |
++		MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR |
++		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG,
++			   dev->wlan.rro_rx_tbit[0]) |
++		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG,
++			   dev->wlan.rro_rx_tbit[1]));
++
++	wed_w32(dev, MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG,
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN |
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR |
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN |
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR |
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN |
++		MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR |
++		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG,
++			   dev->wlan.rx_pg_tbit[0]) |
++		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG,
++			   dev->wlan.rx_pg_tbit[1])|
++		FIELD_PREP(MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG,
++			   dev->wlan.rx_pg_tbit[2]));
++
++	/*
++	 * RRO_MSDU_PG_RING2_CFG1_FLD_DRV_EN should be enabled after
++	 * WM FWDL completed, otherwise RRO_MSDU_PG ring may broken
++	 */
++	wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_EN);
++
++	for (idx = 0; idx < MTK_WED_RX_QUEUES; idx++) {
++		struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx];
++
++		if(!(ring->flags & MTK_WED_RING_CONFIGURED))
++			continue;
++
++		ret = mtk_wed_check_wfdma_rx_fill(dev, ring);
++		if (!ret)
++			dev_err(dev->hw->dev, "mtk_wed%d: rx_rro_ring(%d) init failed!\n",
++				dev->hw->index, idx);
++	}
++
++	for (idx = 0; idx < MTK_WED_RX_PAGE_QUEUES; idx++){
++		struct mtk_wed_ring *ring = &dev->rx_page_ring[idx];
++		if(!(ring->flags & MTK_WED_RING_CONFIGURED))
++			continue;
++
++		ret = mtk_wed_check_wfdma_rx_fill(dev, ring);
++		if (!ret)
++			dev_err(dev->hw->dev, "mtk_wed%d: rx_page_ring(%d) init failed!\n",
++				dev->hw->index, idx);
++	}
++}
++
+ static void
+ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+ {
+ 	int i, ret;
+ 
+-	if (dev->ver > MTK_WED_V1)
+-		ret = mtk_wed_rx_bm_alloc(dev);
++	if (mtk_wed_get_rx_capa(dev)) {
++		ret = mtk_wed_rx_buffer_alloc(dev);
++		if (ret)
++			return;
++
++		if (dev->wlan.hwrro)
++			mtk_wed_rx_page_buffer_alloc(dev);
++	}
+ 
+ 	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
+ 		if (!dev->tx_wdma[i].desc)
+@@ -1437,7 +1964,7 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+ 	mtk_wed_set_int(dev, irq_mask);
+ 	mtk_wed_set_ext_int(dev, true);
+ 
+-	if (dev->ver == MTK_WED_V1) {
++	if (dev->hw->version == 1) {
+ 		u32 val;
+ 
+ 		val = dev->wlan.wpdma_phys |
+@@ -1448,33 +1975,52 @@ mtk_wed_start(struct mtk_wed_device *dev, u32 irq_mask)
+ 			val |= BIT(1);
+ 		val |= BIT(0);
+ 		regmap_write(dev->hw->mirror, dev->hw->index * 4, val);
+-	} else {
++	} else if (mtk_wed_get_rx_capa(dev)) {
+ 		/* driver set mid ready and only once */
+ 		wed_w32(dev, MTK_WED_EXT_INT_MASK1,
+ 			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
+ 		wed_w32(dev, MTK_WED_EXT_INT_MASK2,
+ 			MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
++		if (dev->hw->version == 3)
++			wed_w32(dev, MTK_WED_EXT_INT_MASK3,
++				MTK_WED_EXT_INT_STATUS_WPDMA_MID_RDY);
+ 
+ 		wed_r32(dev, MTK_WED_EXT_INT_MASK1);
+ 		wed_r32(dev, MTK_WED_EXT_INT_MASK2);
++		if (dev->hw->version == 3)
++			wed_r32(dev, MTK_WED_EXT_INT_MASK3);
+ 
+ 		ret = mtk_wed_rro_cfg(dev);
+ 		if (ret)
+ 			return;
+ 	}
+-	mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
++
++	if (dev->hw->version == 2)
++		mtk_wed_set_512_support(dev, dev->wlan.wcid_512);
++	else if (dev->hw->version == 3)
++		mtk_wed_pao_init(dev);
+ 
+ 	mtk_wed_dma_enable(dev);
+ 	dev->running = true;
+ }
+ 
++static int
++mtk_wed_get_pci_base(struct mtk_wed_device *dev)
++{
++	if (dev->hw->index == 0)
++		return MTK_WED_PCIE_BASE0;
++	else if (dev->hw->index == 1)
++		return MTK_WED_PCIE_BASE1;
++	else
++		return MTK_WED_PCIE_BASE2;
++}
++
+ static int
+ mtk_wed_attach(struct mtk_wed_device *dev)
+ 	__releases(RCU)
+ {
+ 	struct mtk_wed_hw *hw;
+ 	struct device *device;
+-	u16 ver;
+ 	int ret = 0;
+ 
+ 	RCU_LOCKDEP_WARN(!rcu_read_lock_held(),
+@@ -1494,34 +2040,30 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+ 		goto out;
+ 	}
+ 
+-	device = dev->wlan.bus_type == MTK_WED_BUS_PCIE
+-	? &dev->wlan.pci_dev->dev
+-	: &dev->wlan.platform_dev->dev;
++	device = dev->wlan.bus_type == MTK_WED_BUS_PCIE ?
++				       &dev->wlan.pci_dev->dev
++				       : &dev->wlan.platform_dev->dev;
+ 	dev_info(device, "attaching wed device %d version %d\n",
+-		 hw->index, hw->ver);
++		 hw->index, hw->version);
+ 
+ 	dev->hw = hw;
+ 	dev->dev = hw->dev;
+ 	dev->irq = hw->irq;
+ 	dev->wdma_idx = hw->index;
++	dev->ver = hw->version;
++
++	if (dev->hw->version == 3)
++		dev->hw->pci_base = mtk_wed_get_pci_base(dev);
+ 
+ 	if (hw->eth->dma_dev == hw->eth->dev &&
+ 	    of_dma_is_coherent(hw->eth->dev->of_node))
+ 		mtk_eth_set_dma_device(hw->eth, hw->dev);
+ 
+-	dev->ver = FIELD_GET(MTK_WED_REV_ID_MAJOR,
+-			    wed_r32(dev, MTK_WED_REV_ID));
+-	if (dev->ver > MTK_WED_V1)
+-		ver = FIELD_GET(MTK_WED_REV_ID_MINOR,
+-			    wed_r32(dev, MTK_WED_REV_ID));
+-
+-	dev->rev_id = ((dev->ver << 28) | ver << 16);
+-
+-	ret = mtk_wed_buffer_alloc(dev);
++	ret = mtk_wed_tx_buffer_alloc(dev);
+ 	if (ret)
+ 		goto error;
+ 
+-	if (dev->ver > MTK_WED_V1) {
++	if (mtk_wed_get_rx_capa(dev)) {
+ 		ret = mtk_wed_rro_alloc(dev);
+ 		if (ret)
+ 			goto error;
+@@ -1533,15 +2075,20 @@ mtk_wed_attach(struct mtk_wed_device *dev)
+ 	init_completion(&dev->wlan_reset_done);
+ 	atomic_set(&dev->fe_reset, 0);
+ 
+-	if (dev->ver == MTK_WED_V1)
++	if (dev->hw->version != 1)
++		dev->rev_id = wed_r32(dev, MTK_WED_REV_ID);
++	else
+ 		regmap_update_bits(hw->hifsys, HIFSYS_DMA_AG_MAP,
+ 				   BIT(hw->index), 0);
+-	else
++
++	if (mtk_wed_get_rx_capa(dev))
+ 		ret = mtk_wed_wo_init(hw);
+ 
+ error:
+-	if (ret)
++	if (ret) {
++		pr_info("%s: detach wed\n", __func__);
+ 		mtk_wed_detach(dev);
++	}
+ out:
+ 	mutex_unlock(&hw_lock);
+ 
+@@ -1576,8 +2123,26 @@ mtk_wed_tx_ring_setup(struct mtk_wed_device *dev, int idx,
+ 	if (mtk_wed_wdma_rx_ring_setup(dev, idx, MTK_WED_WDMA_RING_SIZE, reset))
+ 		return -ENOMEM;
+ 
++	if (dev->hw->version == 3 && idx == 1) {
++		/* reset prefetch index */
++		wed_set(dev, MTK_WED_WDMA_RX_PREF_CFG,
++		       MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
++		       MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
++
++		wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG,
++		       MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR |
++		       MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR);
++
++		/* reset prefetch FIFO */
++		wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG,
++		       MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR |
++		       MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR);
++		wed_w32(dev, MTK_WED_WDMA_RX_PREF_FIFO_CFG, 0);
++	}
++
+ 	ring->reg_base = MTK_WED_RING_TX(idx);
+ 	ring->wpdma = regs;
++	ring->flags |= MTK_WED_RING_CONFIGURED;
+ 
+ 	/* WED -> WPDMA */
+ 	wpdma_tx_w32(dev, idx, MTK_WED_RING_OFS_BASE, ring->desc_phys);
+@@ -1599,7 +2164,7 @@ mtk_wed_txfree_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
+ 	struct mtk_wed_ring *ring = &dev->txfree_ring;
+ 	int i, idx = 1;
+ 
+-	if(dev->ver > MTK_WED_V1)
++	if(dev->hw->version > 1)
+ 		idx = 0;
+ 
+ 	/*
+@@ -1652,6 +2217,129 @@ mtk_wed_rx_ring_setup(struct mtk_wed_device *dev,
+ 	return 0;
+ }
+ 
++static int
++mtk_wed_rro_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++{
++	struct mtk_wed_ring *ring = &dev->rx_rro_ring[idx];
++
++	ring->wpdma = regs;
++
++	wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_BASE,
++		readl(regs));
++	wed_w32(dev, MTK_WED_RRO_RX_D_RX(idx) + MTK_WED_RING_OFS_COUNT,
++		readl(regs + MTK_WED_RING_OFS_COUNT));
++
++	ring->flags |= MTK_WED_RING_CONFIGURED;
++
++	return 0;
++}
++
++static int
++mtk_wed_msdu_pg_rx_ring_setup(struct mtk_wed_device *dev, int idx, void __iomem *regs)
++{
++	struct mtk_wed_ring *ring = &dev->rx_page_ring[idx];
++
++	ring->wpdma = regs;
++
++	wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_BASE,
++		readl(regs));
++	wed_w32(dev, MTK_WED_RRO_MSDU_PG_CTRL0(idx) + MTK_WED_RING_OFS_COUNT,
++		readl(regs + MTK_WED_RING_OFS_COUNT));
++
++	ring->flags |= MTK_WED_RING_CONFIGURED;
++
++	return 0;
++}
++
++static int
++mtk_wed_ind_rx_ring_setup(struct mtk_wed_device *dev, void __iomem *regs)
++{
++	struct mtk_wed_ring *ring = &dev->ind_cmd_ring;
++	u32 val = readl(regs + MTK_WED_RING_OFS_COUNT);
++	int i = 0, cnt = 0;
++
++	ring->wpdma = regs;
++
++	if (readl(regs) & 0xf)
++		pr_info("%s(): address is not 16-byte alignment\n", __func__);
++
++	wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_BASE,
++		readl(regs) & 0xfffffff0);
++
++	wed_w32(dev, MTK_WED_IND_CMD_RX_CTRL1 + MTK_WED_RING_OFS_COUNT,
++		readl(regs + MTK_WED_RING_OFS_COUNT));
++
++	/* ack sn cr */
++	wed_w32(dev, MTK_WED_RRO_CFG0, dev->wlan.phy_base +
++		dev->wlan.ind_cmd.ack_sn_addr);
++	wed_w32(dev, MTK_WED_RRO_CFG1,
++		FIELD_PREP(MTK_WED_RRO_CFG1_MAX_WIN_SZ,
++			   dev->wlan.ind_cmd.win_size) |
++		FIELD_PREP(MTK_WED_RRO_CFG1_PARTICL_SE_ID,
++			   dev->wlan.ind_cmd.particular_sid));
++
++	/* particular session addr element */
++	wed_w32(dev, MTK_WED_ADDR_ELEM_CFG0, dev->wlan.ind_cmd.particular_se_phys);
++
++	for (i = 0; i < dev->wlan.ind_cmd.se_group_nums; i++) {
++		wed_w32(dev, MTK_WED_RADDR_ELEM_TBL_WDATA,
++			dev->wlan.ind_cmd.addr_elem_phys[i] >> 4);
++		wed_w32(dev, MTK_WED_ADDR_ELEM_TBL_CFG,
++			MTK_WED_ADDR_ELEM_TBL_WR | (i & 0x7f));
++
++		val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
++		while (!(val & MTK_WED_ADDR_ELEM_TBL_WR_RDY) &&
++			 cnt < 100) {
++			val = wed_r32(dev, MTK_WED_ADDR_ELEM_TBL_CFG);
++			cnt++;
++		}
++		if (cnt >= 100) {
++			dev_err(dev->hw->dev, "mtk_wed%d: write ba session base failed!\n",
++				dev->hw->index);
++		}
++		/*if (mtk_wed_poll_busy(dev, MTK_WED_ADDR_ELEM_TBL_CFG,
++				      MTK_WED_ADDR_ELEM_TBL_WR_RDY)) {
++			dev_err(dev->hw->dev, "mtk_wed%d: write ba session base failed!\n",
++				dev->hw->index);
++			return -1;
++		}*/
++	}
++
++	/* pn check init */
++	for (i = 0; i < dev->wlan.ind_cmd.particular_sid; i++) {
++		wed_w32(dev, MTK_WED_PN_CHECK_WDATA_M,
++			MTK_WED_PN_CHECK_IS_FIRST);
++
++		wed_w32(dev, MTK_WED_PN_CHECK_CFG, MTK_WED_PN_CHECK_WR |
++			FIELD_PREP(MTK_WED_PN_CHECK_SE_ID, i));
++
++		cnt = 0;
++		val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
++		while (!(val & MTK_WED_PN_CHECK_WR_RDY) &&
++			 cnt < 100) {
++			val = wed_r32(dev, MTK_WED_PN_CHECK_CFG);
++			cnt++;
++		}
++		if (cnt >= 100) {
++			dev_err(dev->hw->dev, "mtk_wed%d: session(%d) init failed!\n",
++				dev->hw->index, i);
++		}
++		/*if (mtk_wed_poll_busy(dev, MTK_WED_PN_CHECK_CFG,
++				      MTK_WED_PN_CHECK_WR_RDY)) {
++			dev_err(dev->hw->dev, "mtk_wed%d: session(%d) init failed!\n",
++				dev->hw->index, i);
++			//return -1;
++		}*/
++	}
++
++	wed_w32(dev, MTK_WED_RX_IND_CMD_CNT0, MTK_WED_RX_IND_CMD_DBG_CNT_EN);
++
++	wed_set(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
++
++	return 0;
++}
++
++
+ static u32
+ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+ {
+@@ -1660,6 +2348,8 @@ mtk_wed_irq_get(struct mtk_wed_device *dev, u32 mask)
+ 	val = wed_r32(dev, MTK_WED_EXT_INT_STATUS);
+ 	wed_w32(dev, MTK_WED_EXT_INT_STATUS, val);
+ 	val &= MTK_WED_EXT_INT_STATUS_ERROR_MASK;
++	if (dev->hw->version == 3)
++		val &= MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT;
+ 	WARN_RATELIMIT(val, "mtk_wed%d: error status=%08x\n",
+ 		       dev->hw->index, val);
+ 
+@@ -1752,6 +2442,9 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+ 		.tx_ring_setup = mtk_wed_tx_ring_setup,
+ 		.txfree_ring_setup = mtk_wed_txfree_ring_setup,
+ 		.rx_ring_setup = mtk_wed_rx_ring_setup,
++		.rro_rx_ring_setup = mtk_wed_rro_rx_ring_setup,
++		.msdu_pg_rx_ring_setup = mtk_wed_msdu_pg_rx_ring_setup,
++		.ind_rx_ring_setup = mtk_wed_ind_rx_ring_setup,
+ 		.msg_update = mtk_wed_send_msg,
+ 		.start = mtk_wed_start,
+ 		.stop = mtk_wed_stop,
+@@ -1763,6 +2456,7 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+ 		.detach = mtk_wed_detach,
+ 		.setup_tc = mtk_wed_eth_setup_tc,
+ 		.ppe_check = mtk_wed_ppe_check,
++		.start_hwrro = mtk_wed_start_hwrro,
+ 	};
+ 	struct device_node *eth_np = eth->dev->of_node;
+ 	struct platform_device *pdev;
+@@ -1802,9 +2496,10 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+ 	hw->wdma_phy = wdma_phy;
+ 	hw->index = index;
+ 	hw->irq = irq;
+-	hw->ver = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
++	hw->version = MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) ?
++		      3 : MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) ? 2 : 1;
+ 
+-	if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
++	if (hw->version == 1) {
+ 		hw->mirror = syscon_regmap_lookup_by_phandle(eth_np,
+ 							     "mediatek,pcie-mirror");
+ 		hw->hifsys = syscon_regmap_lookup_by_phandle(eth_np,
+@@ -1819,7 +2514,6 @@ void mtk_wed_add_hw(struct device_node *np, struct mtk_eth *eth,
+ 			regmap_write(hw->mirror, 0, 0);
+ 			regmap_write(hw->mirror, 4, 0);
+ 		}
+-		hw->ver = MTK_WED_V1;
+ 	}
+ 
+ 	mtk_wed_hw_add_debugfs(hw);
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed.h b/drivers/net/ethernet/mediatek/mtk_wed.h
+index 490873c..fcf7bd0 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed.h
+@@ -10,10 +10,13 @@
+ #include <linux/netdevice.h>
+ #define MTK_PCIE_BASE(n)		(0x1a143000 + (n) * 0x2000)
+ 
+-#define MTK_WED_PKT_SIZE		1900
++#define MTK_WED_PKT_SIZE		1920//1900
+ #define MTK_WED_BUF_SIZE		2048
++#define MTK_WED_PAGE_BUF_SIZE		128
+ #define MTK_WED_BUF_PER_PAGE		(PAGE_SIZE / 2048)
++#define MTK_WED_RX_PAGE_BUF_PER_PAGE	(PAGE_SIZE / 128)
+ #define MTK_WED_RX_RING_SIZE		1536
++#define MTK_WED_RX_PG_BM_CNT		8192
+ 
+ #define MTK_WED_TX_RING_SIZE		2048
+ #define MTK_WED_WDMA_RING_SIZE		512
+@@ -27,6 +30,9 @@
+ #define MTK_WED_RRO_QUE_CNT		8192
+ #define MTK_WED_MIOD_ENTRY_CNT		128
+ 
++#define MTK_WED_TX_BM_DMA_SIZE		65536
++#define MTK_WED_TX_BM_PKT_CNT		32768
++
+ #define MODULE_ID_WO		1
+ 
+ struct mtk_eth;
+@@ -43,6 +49,8 @@ struct mtk_wed_hw {
+ 	struct dentry *debugfs_dir;
+ 	struct mtk_wed_device *wed_dev;
+ 	struct mtk_wed_wo *wed_wo;
++	struct mtk_wed_pao *wed_pao;
++	u32 pci_base;
+ 	u32 debugfs_reg;
+ 	u32 num_flows;
+ 	u32 wdma_phy;
+@@ -50,7 +58,8 @@ struct mtk_wed_hw {
+ 	int ring_num;
+ 	int irq;
+ 	int index;
+-	u32 ver;
++	int token_id;
++	u32 version;
+ };
+ 
+ struct mtk_wdma_info {
+@@ -58,6 +67,18 @@ struct mtk_wdma_info {
+ 	u8 queue;
+ 	u16 wcid;
+ 	u8 bss;
++	u32 usr_info;
++	u8 tid;
++	u8 is_fixedrate;
++	u8 is_prior;
++	u8 is_sp;
++	u8 hf;
++	u8 amsdu_en;
++};
++
++struct mtk_wed_pao {
++	char *hif_txd[32];
++	dma_addr_t hif_txd_phys[32];
+ };
+ 
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+index 4a9e684..51e3d7c 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_debugfs.c
+@@ -11,9 +11,11 @@ struct reg_dump {
+ 	u16 offset;
+ 	u8 type;
+ 	u8 base;
++	u32 mask;
+ };
+ 
+ enum {
++	DUMP_TYPE_END,
+ 	DUMP_TYPE_STRING,
+ 	DUMP_TYPE_WED,
+ 	DUMP_TYPE_WDMA,
+@@ -23,8 +25,11 @@ enum {
+ 	DUMP_TYPE_WED_RRO,
+ };
+ 
++#define DUMP_END() { .type = DUMP_TYPE_END }
+ #define DUMP_STR(_str) { _str, 0, DUMP_TYPE_STRING }
+ #define DUMP_REG(_reg, ...) { #_reg, MTK_##_reg, __VA_ARGS__ }
++#define DUMP_REG_MASK(_reg, _mask) { #_mask, MTK_##_reg, DUMP_TYPE_WED, 0, MTK_##_mask }
++
+ #define DUMP_RING(_prefix, _base, ...)				\
+ 	{ _prefix " BASE", _base, __VA_ARGS__ },		\
+ 	{ _prefix " CNT",  _base + 0x4, __VA_ARGS__ },	\
+@@ -32,6 +37,7 @@ enum {
+ 	{ _prefix " DIDX", _base + 0xc, __VA_ARGS__ }
+ 
+ #define DUMP_WED(_reg) DUMP_REG(_reg, DUMP_TYPE_WED)
++#define DUMP_WED_MASK(_reg, _mask) DUMP_REG_MASK(_reg, _mask)
+ #define DUMP_WED_RING(_base) DUMP_RING(#_base, MTK_##_base, DUMP_TYPE_WED)
+ 
+ #define DUMP_WDMA(_reg) DUMP_REG(_reg, DUMP_TYPE_WDMA)
+@@ -52,36 +58,49 @@ print_reg_val(struct seq_file *s, const char *name, u32 val)
+ 
+ static void
+ dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev,
+-	      const struct reg_dump *regs, int n_regs)
++	      const struct reg_dump **regs)
+ {
+-	const struct reg_dump *cur;
++	const struct reg_dump **cur_o = regs, *cur;
++	bool newline = false;
+ 	u32 val;
+ 
+-	for (cur = regs; cur < &regs[n_regs]; cur++) {
+-		switch (cur->type) {
+-		case DUMP_TYPE_STRING:
+-			seq_printf(s, "%s======== %s:\n",
+-				   cur > regs ? "\n" : "",
+-				   cur->name);
+-			continue;
+-		case DUMP_TYPE_WED:
+-		case DUMP_TYPE_WED_RRO:
+-			val = wed_r32(dev, cur->offset);
+-			break;
+-		case DUMP_TYPE_WDMA:
+-			val = wdma_r32(dev, cur->offset);
+-			break;
+-		case DUMP_TYPE_WPDMA_TX:
+-			val = wpdma_tx_r32(dev, cur->base, cur->offset);
+-			break;
+-		case DUMP_TYPE_WPDMA_TXFREE:
+-			val = wpdma_txfree_r32(dev, cur->offset);
+-			break;
+-		case DUMP_TYPE_WPDMA_RX:
+-			val = wpdma_rx_r32(dev, cur->base, cur->offset);
+-			break;
++	while (*cur_o) {
++		cur = *cur_o;
++
++		while (cur->type != DUMP_TYPE_END) {
++			switch (cur->type) {
++			case DUMP_TYPE_STRING:
++				seq_printf(s, "%s======== %s:\n",
++					   newline ? "\n" : "",
++					   cur->name);
++				newline = true;
++				cur++;
++				continue;
++			case DUMP_TYPE_WED:
++			case DUMP_TYPE_WED_RRO:
++				val = wed_r32(dev, cur->offset);
++				break;
++			case DUMP_TYPE_WDMA:
++				val = wdma_r32(dev, cur->offset);
++				break;
++			case DUMP_TYPE_WPDMA_TX:
++				val = wpdma_tx_r32(dev, cur->base, cur->offset);
++				break;
++			case DUMP_TYPE_WPDMA_TXFREE:
++				val = wpdma_txfree_r32(dev, cur->offset);
++				break;
++			case DUMP_TYPE_WPDMA_RX:
++				val = wpdma_rx_r32(dev, cur->base, cur->offset);
++				break;
++			}
++
++			if (cur->mask)
++				val = (cur->mask & val) >> (ffs(cur->mask) - 1);
++
++			print_reg_val(s, cur->name, val);
++			cur++;
+ 		}
+-		print_reg_val(s, cur->name, val);
++		cur_o++;
+ 	}
+ }
+ 
+@@ -89,7 +108,7 @@ dump_wed_regs(struct seq_file *s, struct mtk_wed_device *dev,
+ static int
+ wed_txinfo_show(struct seq_file *s, void *data)
+ {
+-	static const struct reg_dump regs[] = {
++	static const struct reg_dump regs_common[] = {
+ 		DUMP_STR("WED TX"),
+ 		DUMP_WED(WED_TX_MIB(0)),
+ 		DUMP_WED_RING(WED_RING_TX(0)),
+@@ -128,16 +147,32 @@ wed_txinfo_show(struct seq_file *s, void *data)
+ 		DUMP_WDMA_RING(WDMA_RING_RX(0)),
+ 		DUMP_WDMA_RING(WDMA_RING_RX(1)),
+ 
+-		DUMP_STR("TX FREE"),
++		DUMP_STR("WED TX FREE"),
+ 		DUMP_WED(WED_RX_MIB(0)),
++		DUMP_WED_RING(WED_RING_RX(0)),
++		DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(0)),
++
++		DUMP_WED(WED_RX_MIB(1)),
++		DUMP_WED_RING(WED_RING_RX(1)),
++		DUMP_WED(WED_WPDMA_RX_COHERENT_MIB(1)),
++		DUMP_STR("WED_WPDMA TX FREE"),
++		DUMP_WED_RING(WED_WPDMA_RING_RX(0)),
++		DUMP_WED_RING(WED_WPDMA_RING_RX(1)),
++		DUMP_END(),
++	};
++
++	static const struct reg_dump *regs[] = {
++		&regs_common[0],
++		NULL,
+ 	};
++
+ 	struct mtk_wed_hw *hw = s->private;
+ 	struct mtk_wed_device *dev = hw->wed_dev;
+ 
+ 	if (!dev)
+ 		return 0;
+ 
+-	dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++	dump_wed_regs(s, dev, regs);
+ 
+ 	return 0;
+ }
+@@ -146,7 +181,7 @@ DEFINE_SHOW_ATTRIBUTE(wed_txinfo);
+ static int
+ wed_rxinfo_show(struct seq_file *s, void *data)
+ {
+-	static const struct reg_dump regs[] = {
++	static const struct reg_dump regs_common[] = {
+ 		DUMP_STR("WPDMA RX"),
+ 		DUMP_WPDMA_RX_RING(0),
+ 		DUMP_WPDMA_RX_RING(1),
+@@ -164,7 +199,7 @@ wed_rxinfo_show(struct seq_file *s, void *data)
+ 		DUMP_WED_RING(WED_RING_RX_DATA(0)),
+ 		DUMP_WED_RING(WED_RING_RX_DATA(1)),
+ 
+-		DUMP_STR("WED RRO"),
++		DUMP_STR("WED WO RRO"),
+ 		DUMP_WED_RRO_RING(WED_RROQM_MIOD_CTRL0),
+ 		DUMP_WED(WED_RROQM_MID_MIB),
+ 		DUMP_WED(WED_RROQM_MOD_MIB),
+@@ -175,16 +210,6 @@ wed_rxinfo_show(struct seq_file *s, void *data)
+ 		DUMP_WED(WED_RROQM_FDBK_ANC_MIB),
+ 		DUMP_WED(WED_RROQM_FDBK_ANC2H_MIB),
+ 
+-		DUMP_STR("WED Route QM"),
+-		DUMP_WED(WED_RTQM_R2H_MIB(0)),
+-		DUMP_WED(WED_RTQM_R2Q_MIB(0)),
+-		DUMP_WED(WED_RTQM_Q2H_MIB(0)),
+-		DUMP_WED(WED_RTQM_R2H_MIB(1)),
+-		DUMP_WED(WED_RTQM_R2Q_MIB(1)),
+-		DUMP_WED(WED_RTQM_Q2H_MIB(1)),
+-		DUMP_WED(WED_RTQM_Q2N_MIB),
+-		DUMP_WED(WED_RTQM_Q2B_MIB),
+-		DUMP_WED(WED_RTQM_PFDBK_MIB),
+ 
+ 		DUMP_STR("WED WDMA TX"),
+ 		DUMP_WED(WED_WDMA_TX_MIB),
+@@ -205,15 +230,99 @@ wed_rxinfo_show(struct seq_file *s, void *data)
+ 		DUMP_WED(WED_RX_BM_INTF2),
+ 		DUMP_WED(WED_RX_BM_INTF),
+ 		DUMP_WED(WED_RX_BM_ERR_STS),
++		DUMP_END()
++	};
++
++	static const struct reg_dump regs_v2[] = {
++		DUMP_STR("WED Route QM"),
++		DUMP_WED(WED_RTQM_R2H_MIB(0)),
++		DUMP_WED(WED_RTQM_R2Q_MIB(0)),
++		DUMP_WED(WED_RTQM_Q2H_MIB(0)),
++		DUMP_WED(WED_RTQM_R2H_MIB(1)),
++		DUMP_WED(WED_RTQM_R2Q_MIB(1)),
++		DUMP_WED(WED_RTQM_Q2H_MIB(1)),
++		DUMP_WED(WED_RTQM_Q2N_MIB),
++		DUMP_WED(WED_RTQM_Q2B_MIB),
++		DUMP_WED(WED_RTQM_PFDBK_MIB),
++
++		DUMP_END()
++	};
++
++	static const struct reg_dump regs_v3[] = {
++		DUMP_STR("WED RX RRO DATA"),
++		DUMP_WED_RING(WED_RRO_RX_D_RX(0)),
++		DUMP_WED_RING(WED_RRO_RX_D_RX(1)),
++
++		DUMP_STR("WED RX MSDU PAGE"),
++		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(0)),
++		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(1)),
++		DUMP_WED_RING(WED_RRO_MSDU_PG_CTRL0(2)),
++
++		DUMP_STR("WED RX IND CMD"),
++		DUMP_WED(WED_IND_CMD_RX_CTRL1),
++		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL2, WED_IND_CMD_MAX_CNT),
++		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_PROC_IDX),
++		DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_DMA_IDX),
++		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0, WED_IND_CMD_MAGIC_CNT),
++		DUMP_WED_MASK(RRO_IND_CMD_SIGNATURE, RRO_IND_CMD_MAGIC_CNT),
++		DUMP_WED_MASK(WED_IND_CMD_RX_CTRL0,
++			      WED_IND_CMD_PREFETCH_FREE_CNT),
++		DUMP_WED_MASK(WED_RRO_CFG1, WED_RRO_CFG1_PARTICL_SE_ID),
++
++		DUMP_STR("WED ADDR ELEM"),
++		DUMP_WED(WED_ADDR_ELEM_CFG0),
++		DUMP_WED_MASK(WED_ADDR_ELEM_CFG1,
++			      WED_ADDR_ELEM_PREFETCH_FREE_CNT),
++
++		DUMP_STR("WED Route QM"),
++		DUMP_WED(WED_RTQM_ENQ_I2Q_DMAD_CNT),
++		DUMP_WED(WED_RTQM_ENQ_I2N_DMAD_CNT),
++		DUMP_WED(WED_RTQM_ENQ_I2Q_PKT_CNT),
++		DUMP_WED(WED_RTQM_ENQ_I2N_PKT_CNT),
++		DUMP_WED(WED_RTQM_ENQ_USED_ENTRY_CNT),
++		DUMP_WED(WED_RTQM_ENQ_ERR_CNT),
++
++		DUMP_WED(WED_RTQM_DEQ_DMAD_CNT),
++		DUMP_WED(WED_RTQM_DEQ_Q2I_DMAD_CNT),
++		DUMP_WED(WED_RTQM_DEQ_PKT_CNT),
++		DUMP_WED(WED_RTQM_DEQ_Q2I_PKT_CNT),
++		DUMP_WED(WED_RTQM_DEQ_USED_PFDBK_CNT),
++		DUMP_WED(WED_RTQM_DEQ_ERR_CNT),
++
++		DUMP_END()
++	};
++
++	static const struct reg_dump *regs_new_v2[] = {
++		&regs_common[0],
++		&regs_v2[0],
++		NULL,
++	};
++
++	static const struct reg_dump *regs_new_v3[] = {
++		&regs_common[0],
++		&regs_v3[0],
++		NULL,
+ 	};
+ 
+ 	struct mtk_wed_hw *hw = s->private;
+ 	struct mtk_wed_device *dev = hw->wed_dev;
++	const struct reg_dump **regs;
+ 
+ 	if (!dev)
+ 		return 0;
+ 
+-	dump_wed_regs(s, dev, regs, ARRAY_SIZE(regs));
++	switch(dev->hw->version) {
++	case 2:
++		regs = regs_new_v2;
++		break;
++	case 3:
++		regs = regs_new_v3;
++		break;
++	default:
++		return 0;
++	}
++
++	dump_wed_regs(s, dev, regs);
+ 
+ 	return 0;
+ }
+@@ -248,6 +357,383 @@ mtk_wed_reg_get(void *data, u64 *val)
+ DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mtk_wed_reg_get, mtk_wed_reg_set,
+              "0x%08llx\n");
+ 
++static int
++wed_token_txd_show(struct seq_file *s, void *data)
++{
++	struct mtk_wed_hw *hw = s->private;
++	struct mtk_wed_device *dev = hw->wed_dev;
++	struct dma_page_info *page_list = dev->tx_buf_ring.pages;
++	int token = dev->wlan.token_start;
++	u32 val = hw->token_id, size = 1;
++	int page_idx = (val - token) / 2;
++	int i;
++
++	if (val < token) {
++		size = val;
++		page_idx = 0;
++	}
++
++	for (i = 0; i < size; i += MTK_WED_BUF_PER_PAGE) {
++		void *page = page_list[page_idx++].addr;
++		void *buf;
++		int j;
++
++		if (!page)
++			break;
++
++		buf = page_to_virt(page);
++
++		for (j = 0; j < MTK_WED_BUF_PER_PAGE; j++) {
++			printk("[TXD]:token id = %d\n", token + 2 * (page_idx - 1) + j);
++			print_hex_dump(KERN_ERR , "", DUMP_PREFIX_OFFSET, 16, 1, (u8 *)buf, 128, false);
++			seq_printf(s, "\n");
++
++			buf += MTK_WED_BUF_SIZE;
++		}
++	}
++
++	return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(wed_token_txd);
++
++static int
++wed_pao_show(struct seq_file *s, void *data)
++{
++	static const struct reg_dump regs_common[] = {
++		DUMP_STR("PAO AMDSU INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_FIFO_DMAD),
++
++		DUMP_STR("PAO AMDSU ENG0 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(0)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(0)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(0)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(0)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(0)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(0),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(0),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(0),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(0),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(0),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG1 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(1)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(1)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(1)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(1)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(1)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(1),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(1),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(1),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(2),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(2),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG2 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(2)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(2)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(2)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(2)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(2)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(2),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(2),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(2),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(2),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(2),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG3 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(3)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(3)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(3)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(3)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(3)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(3),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(3),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(3),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(3),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(3),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG4 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(4)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(4)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(4)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(4)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(4)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(4),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(4),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(4),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(4),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(4),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG5 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(5)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(5)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(5)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(5)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(5)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(5),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(5),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(5),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(5),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(5),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG6 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(6)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(6)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(6)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(6)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(6)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(6),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(6),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(6),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(6),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(6),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG7 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(7)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(7)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(7)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(7)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(7)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(7),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(7),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(7),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(7),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(4),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO AMDSU ENG8 INFO"),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_DMAD(8)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QFPL(8)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENI(8)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_QENO(8)),
++		DUMP_WED(WED_PAO_MON_AMSDU_ENG_MERG(8)),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(8),
++			      WED_PAO_AMSDU_ENG_MAX_PL_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT8(8),
++			      WED_PAO_AMSDU_ENG_MAX_QGPP_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(8),
++			      WED_PAO_AMSDU_ENG_CUR_ENTRY),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(8),
++			      WED_PAO_AMSDU_ENG_MAX_BUF_MERGED),
++		DUMP_WED_MASK(WED_PAO_MON_AMSDU_ENG_CNT9(8),
++			      WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED),
++
++		DUMP_STR("PAO QMEM INFO"),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(0), WED_PAO_QMEM_FQ_CNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(0), WED_PAO_QMEM_SP_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(1), WED_PAO_QMEM_TID0_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(1), WED_PAO_QMEM_TID1_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(2), WED_PAO_QMEM_TID2_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(2), WED_PAO_QMEM_TID3_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(3), WED_PAO_QMEM_TID4_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(3), WED_PAO_QMEM_TID5_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(4), WED_PAO_QMEM_TID6_QCNT),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_CNT(4), WED_PAO_QMEM_TID7_QCNT),
++
++
++		DUMP_STR("PAO QMEM HEAD INFO"),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(0), WED_PAO_QMEM_FQ_HEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(0), WED_PAO_QMEM_SP_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(1), WED_PAO_QMEM_TID0_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(1), WED_PAO_QMEM_TID1_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(2), WED_PAO_QMEM_TID2_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(2), WED_PAO_QMEM_TID3_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(3), WED_PAO_QMEM_TID4_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(3), WED_PAO_QMEM_TID5_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(4), WED_PAO_QMEM_TID6_QHEAD),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(4), WED_PAO_QMEM_TID7_QHEAD),
++
++		DUMP_STR("PAO QMEM TAIL INFO"),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(5), WED_PAO_QMEM_FQ_TAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(5), WED_PAO_QMEM_SP_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(6), WED_PAO_QMEM_TID0_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(6), WED_PAO_QMEM_TID1_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(7), WED_PAO_QMEM_TID2_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(7), WED_PAO_QMEM_TID3_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(8), WED_PAO_QMEM_TID4_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(8), WED_PAO_QMEM_TID5_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(9), WED_PAO_QMEM_TID6_QTAIL),
++		DUMP_WED_MASK(WED_PAO_MON_QMEM_PTR(9), WED_PAO_QMEM_TID7_QTAIL),
++
++		DUMP_STR("PAO HIFTXD MSDU INFO"),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(1)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(2)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(3)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(4)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(5)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(6)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(7)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(8)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(9)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(10)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(11)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(12)),
++		DUMP_WED(WED_PAO_MON_HIFTXD_FETCH_MSDU(13)),
++		DUMP_END()
++	};
++
++	static const struct reg_dump *regs[] = {
++		&regs_common[0],
++		NULL,
++	};
++	struct mtk_wed_hw *hw = s->private;
++	struct mtk_wed_device *dev = hw->wed_dev;
++
++	if (!dev)
++		return 0;
++
++	dump_wed_regs(s, dev, regs);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_pao);
++
++static int
++wed_rtqm_show(struct seq_file *s, void *data)
++{
++	static const struct reg_dump regs_common[] = {
++		DUMP_STR("WED Route QM IGRS0(N2H + Recycle)"),
++		DUMP_WED(WED_RTQM_IGRS0_I2HW_DMAD_CNT),
++		DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS0_I2H_DMAD_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS0_I2HW_PKT_CNT),
++		DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS0_I2H_PKT_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS0_FDROP_CNT),
++
++
++		DUMP_STR("WED Route QM IGRS1(Legacy)"),
++		DUMP_WED(WED_RTQM_IGRS1_I2HW_DMAD_CNT),
++		DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS1_I2H_DMAD_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS1_I2HW_PKT_CNT),
++		DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS1_I2H_PKT_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS1_FDROP_CNT),
++
++		DUMP_STR("WED Route QM IGRS2(RRO3.0)"),
++		DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
++		DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS2_I2H_DMAD_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS2_I2HW_PKT_CNT),
++		DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS2_I2H_PKT_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS2_FDROP_CNT),
++
++		DUMP_STR("WED Route QM IGRS3(DEBUG)"),
++		DUMP_WED(WED_RTQM_IGRS2_I2HW_DMAD_CNT),
++		DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS3_I2H_DMAD_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS3_I2HW_PKT_CNT),
++		DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(0)),
++		DUMP_WED(WED_RTQM_IGRS3_I2H_PKT_CNT(1)),
++		DUMP_WED(WED_RTQM_IGRS3_FDROP_CNT),
++
++		DUMP_END()
++	};
++
++	static const struct reg_dump *regs[] = {
++		&regs_common[0],
++		NULL,
++	};
++	struct mtk_wed_hw *hw = s->private;
++	struct mtk_wed_device *dev = hw->wed_dev;
++
++	if (!dev)
++		return 0;
++
++	dump_wed_regs(s, dev, regs);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_rtqm);
++
++
++static int
++wed_rro_show(struct seq_file *s, void *data)
++{
++	static const struct reg_dump regs_common[] = {
++		DUMP_STR("RRO/IND CMD CNT"),
++		DUMP_WED(WED_RX_IND_CMD_CNT(1)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(2)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(3)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(4)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(5)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(6)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(7)),
++		DUMP_WED(WED_RX_IND_CMD_CNT(8)),
++		DUMP_WED_MASK(WED_RX_IND_CMD_CNT(9),
++			      WED_IND_CMD_MAGIC_CNT_FAIL_CNT),
++
++		DUMP_WED(WED_RX_ADDR_ELEM_CNT(0)),
++		DUMP_WED_MASK(WED_RX_ADDR_ELEM_CNT(1),
++			      WED_ADDR_ELEM_SIG_FAIL_CNT),
++		DUMP_WED(WED_RX_MSDU_PG_CNT(1)),
++		DUMP_WED(WED_RX_MSDU_PG_CNT(2)),
++		DUMP_WED(WED_RX_MSDU_PG_CNT(3)),
++		DUMP_WED(WED_RX_MSDU_PG_CNT(4)),
++		DUMP_WED(WED_RX_MSDU_PG_CNT(5)),
++		DUMP_WED_MASK(WED_RX_PN_CHK_CNT,
++			      WED_PN_CHK_FAIL_CNT),
++
++		DUMP_END()
++	};
++
++	static const struct reg_dump *regs[] = {
++		&regs_common[0],
++		NULL,
++	};
++	struct mtk_wed_hw *hw = s->private;
++	struct mtk_wed_device *dev = hw->wed_dev;
++
++	if (!dev)
++		return 0;
++
++	dump_wed_regs(s, dev, regs);
++
++	return 0;
++}
++DEFINE_SHOW_ATTRIBUTE(wed_rro);
++
+ void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
+ {
+ 	struct dentry *dir;
+@@ -261,8 +747,18 @@ void mtk_wed_hw_add_debugfs(struct mtk_wed_hw *hw)
+ 	debugfs_create_u32("regidx", 0600, dir, &hw->debugfs_reg);
+ 	debugfs_create_file_unsafe("regval", 0600, dir, hw, &fops_regval);
+ 	debugfs_create_file_unsafe("txinfo", 0400, dir, hw, &wed_txinfo_fops);
+-	debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, &wed_rxinfo_fops);
+-	if (hw->ver != MTK_WED_V1) {
++	debugfs_create_u32("token_id", 0600, dir, &hw->token_id);
++	debugfs_create_file_unsafe("token_txd", 0600, dir, hw, &wed_token_txd_fops);
++
++	if (hw->version == 3)
++		debugfs_create_file_unsafe("pao", 0400, dir, hw, &wed_pao_fops);
++
++	if (hw->version != 1) {
++		debugfs_create_file_unsafe("rxinfo", 0400, dir, hw, &wed_rxinfo_fops);
++		if (hw->version == 3) {
++			debugfs_create_file_unsafe("rtqm", 0400, dir, hw, &wed_rtqm_fops);
++			debugfs_create_file_unsafe("rro", 0400, dir, hw, &wed_rro_fops);
++		}
+ 		wed_wo_mcu_debugfs(hw, dir);
+ 	}
+ }
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+index 96e30a3..055594d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.c
+@@ -242,7 +242,7 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+ 	u32 ofs = 0;
+ 	u32 boot_cr, val;
+ 
+-	mcu = wo->hw->index ? MT7986_FIRMWARE_WO_2 : MT7986_FIRMWARE_WO_1;
++	mcu = wo->hw->index ? MTK_FIRMWARE_WO_1 : MTK_FIRMWARE_WO_0;
+ 
+ 	ret = request_firmware(&fw, mcu, wo->hw->dev);
+ 	if (ret)
+@@ -289,8 +289,12 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+ 	}
+ 
+ 	/* write the start address */
+-	boot_cr = wo->hw->index ?
+-		WOX_MCU_CFG_LS_WA_BOOT_ADDR_ADDR : WOX_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
++	if (wo->hw->version == 3)
++		boot_cr = WOX_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
++	else
++		boot_cr = wo->hw->index ?
++			WOX_MCU_CFG_LS_WA_BOOT_ADDR_ADDR : WOX_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
++
+ 	wo_w32(wo, boot_cr, (wo->region[WO_REGION_EMI].addr_pa >> 16));
+ 
+ 	/* wo firmware reset */
+@@ -298,8 +302,7 @@ mtk_wed_load_firmware(struct mtk_wed_wo *wo)
+ 
+ 	val = wo_r32(wo, WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
+ 
+-	val |= wo->hw->index ? WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_WA_CPU_RSTB_MASK :
+-		WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_WM_CPU_RSTB_MASK;
++	val |= WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_WM_CPU_RSTB_MASK;
+ 
+ 	wo_w32(wo, WOX_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
+ 
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
+index 19e1199..c07bdb6 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_mcu.h
+@@ -16,8 +16,9 @@
+ #define WARP_OK_STATUS (0)
+ #define WARP_ALREADY_DONE_STATUS (1)
+ 
+-#define MT7986_FIRMWARE_WO_1		"mediatek/mt7986_wo_0.bin"
+-#define MT7986_FIRMWARE_WO_2		"mediatek/mt7986_wo_1.bin"
++#define MTK_FIRMWARE_WO_0		"mediatek/mtk_wo_0.bin"
++#define MTK_FIRMWARE_WO_1		"mediatek/mtk_wo_1.bin"
++#define MTK_FIRMWARE_WO_2		"mediatek/mtk_wo_2.bin"
+ 
+ #define WOCPU_EMI_DEV_NODE		"mediatek,wocpu_emi"
+ #define WOCPU_ILM_DEV_NODE		"mediatek,wocpu_ilm"
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+index 403a36b..4e619ff 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -20,6 +20,9 @@
+ #define MTK_WDMA_DESC_CTRL_DMA_DONE		BIT(31)
+ #define MTK_WED_RX_BM_TOKEN			GENMASK(31, 16)
+ 
++#define MTK_WDMA_TXD0_DESC_INFO_DMA_DONE	BIT(29)
++#define MTK_WDMA_TXD1_DESC_INFO_DMA_DONE	BIT(31)
++
+ struct mtk_wdma_desc {
+ 	__le32 buf0;
+ 	__le32 ctrl;
+@@ -51,6 +54,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET_WDMA_INT_AGENT			BIT(19)
+ #define MTK_WED_RESET_RX_RRO_QM				BIT(20)
+ #define MTK_WED_RESET_RX_ROUTE_QM			BIT(21)
++#define MTK_WED_RESET_TX_PAO				BIT(22)
+ #define MTK_WED_RESET_WED				BIT(31)
+ 
+ #define MTK_WED_CTRL					0x00c
+@@ -58,6 +62,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_WPDMA_INT_AGENT_BUSY		BIT(1)
+ #define MTK_WED_CTRL_WDMA_INT_AGENT_EN			BIT(2)
+ #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY		BIT(3)
++#define MTK_WED_CTRL_WED_RX_IND_CMD_EN			BIT(5)
++#define MTK_WED_CTRL_WED_RX_PG_BM_EN			BIT(6)
++#define MTK_WED_CTRL_WED_RX_PG_BM_BUSU			BIT(7)
+ #define MTK_WED_CTRL_WED_TX_BM_EN			BIT(8)
+ #define MTK_WED_CTRL_WED_TX_BM_BUSY			BIT(9)
+ #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN		BIT(10)
+@@ -68,9 +75,14 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_RX_RRO_QM_BUSY			BIT(15)
+ #define MTK_WED_CTRL_RX_ROUTE_QM_EN			BIT(16)
+ #define MTK_WED_CTRL_RX_ROUTE_QM_BUSY			BIT(17)
++#define MTK_WED_CTRL_TX_TKID_ALI_EN			BIT(20)
++#define MTK_WED_CTRL_TX_TKID_ALI_BUSY			BIT(21)
++#define MTK_WED_CTRL_TX_PAO_EN				BIT(22)
++#define MTK_WED_CTRL_TX_PAO_BUSY			BIT(23)
+ #define MTK_WED_CTRL_FINAL_DIDX_READ			BIT(24)
+ #define MTK_WED_CTRL_ETH_DMAD_FMT			BIT(25)
+ #define MTK_WED_CTRL_MIB_READ_CLEAR			BIT(28)
++#define MTK_WED_CTRL_FLD_MIB_RD_CLR			BIT(28)
+ 
+ #define MTK_WED_EXT_INT_STATUS				0x020
+ #define MTK_WED_EXT_INT_STATUS_TF_LEN_ERR		BIT(0)
+@@ -78,12 +90,10 @@ struct mtk_wdma_desc {
+ #define MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID	BIT(4)
+ #define MTK_WED_EXT_INT_STATUS_TX_FBUF_LO_TH		BIT(8)
+ #define MTK_WED_EXT_INT_STATUS_TX_FBUF_HI_TH		BIT(9)
+-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+-#define MTK_WED_EXT_INT_STATUS_TX_TKID_LO_TH		BIT(10)
+-#define MTK_WED_EXT_INT_STATUS_TX_TKID_HI_TH		BIT(11)
+-#endif
+-#define MTK_WED_EXT_INT_STATUS_RX_FREE_AT_EMPTY		BIT(12)
+-#define MTK_WED_EXT_INT_STATUS_RX_FBUF_DMAD_ER		BIT(13)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH2		BIT(10)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH2		BIT(11)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_LO_TH		BIT(12)
++#define MTK_WED_EXT_INT_STATUS_RX_FBUF_HI_TH		BIT(13)
+ #define MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR	BIT(16)
+ #define MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR	BIT(17)
+ #define MTK_WED_EXT_INT_STATUS_RX_DRV_COHERENT		BIT(18)
+@@ -100,17 +110,15 @@ struct mtk_wdma_desc {
+ #define MTK_WED_EXT_INT_STATUS_ERROR_MASK		(MTK_WED_EXT_INT_STATUS_TF_LEN_ERR | \
+ 							 MTK_WED_EXT_INT_STATUS_TKID_WO_PYLD | \
+ 							 MTK_WED_EXT_INT_STATUS_TKID_TITO_INVALID | \
+-							 MTK_WED_EXT_INT_STATUS_RX_FREE_AT_EMPTY | \
+-							 MTK_WED_EXT_INT_STATUS_RX_FBUF_DMAD_ER | \
+ 							 MTK_WED_EXT_INT_STATUS_RX_DRV_R_RESP_ERR | \
+ 							 MTK_WED_EXT_INT_STATUS_RX_DRV_W_RESP_ERR | \
+ 							 MTK_WED_EXT_INT_STATUS_RX_DRV_INIT_WDMA_EN | \
+-							 MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR | \
+-							 MTK_WED_EXT_INT_STATUS_TX_DMA_W_RESP_ERR)
++							 MTK_WED_EXT_INT_STATUS_TX_DMA_R_RESP_ERR)
+ 
+ #define MTK_WED_EXT_INT_MASK				0x028
+ #define MTK_WED_EXT_INT_MASK1				0x02c
+ #define MTK_WED_EXT_INT_MASK2				0x030
++#define MTK_WED_EXT_INT_MASK3				0x034
+ 
+ #define MTK_WED_STATUS					0x060
+ #define MTK_WED_STATUS_TX				GENMASK(15, 8)
+@@ -118,9 +126,14 @@ struct mtk_wdma_desc {
+ #define MTK_WED_TX_BM_CTRL				0x080
+ #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM			GENMASK(6, 0)
+ #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM			GENMASK(22, 16)
++#define MTK_WED_TX_BM_CTRL_LEGACY_EN			BIT(26)
++#define MTK_WED_TX_TKID_CTRL_FREE_FORMAT		BIT(27)
+ #define MTK_WED_TX_BM_CTRL_PAUSE			BIT(28)
+ 
+ #define MTK_WED_TX_BM_BASE				0x084
++#define MTK_WED_TX_BM_INIT_PTR				0x088
++#define MTK_WED_TX_BM_SW_TAIL_IDX			GENMASK(16, 0)
++#define MTK_WED_TX_BM_INIT_SW_TAIL_IDX			BIT(16)
+ 
+ #define MTK_WED_TX_BM_BUF_LEN				0x08c
+ 
+@@ -134,22 +147,24 @@ struct mtk_wdma_desc {
+ #if defined(CONFIG_MEDIATEK_NETSYS_V2)
+ #define MTK_WED_TX_BM_DYN_THR_LO			GENMASK(8, 0)
+ #define MTK_WED_TX_BM_DYN_THR_HI			GENMASK(24, 16)
+-
+-#define MTK_WED_TX_BM_TKID				0x0c8
+-#define MTK_WED_TX_BM_TKID_START			GENMASK(15, 0)
+-#define MTK_WED_TX_BM_TKID_END				GENMASK(31, 16)
+ #else
+ #define MTK_WED_TX_BM_DYN_THR_LO			GENMASK(6, 0)
+ #define MTK_WED_TX_BM_DYN_THR_HI			GENMASK(22, 16)
++#endif
+ 
+-#define MTK_WED_TX_BM_TKID				0x088
++#define MTK_WED_TX_BM_TKID				0x0c8
+ #define MTK_WED_TX_BM_TKID_START			GENMASK(15, 0)
+ #define MTK_WED_TX_BM_TKID_END				GENMASK(31, 16)
+-#endif
+ 
+ #define MTK_WED_TX_TKID_CTRL				0x0c0
++#if defined(CONFIG_MEDIATEK_NETSYS_V3)
++#define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM		GENMASK(7, 0)
++#define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM		GENMASK(23, 16)
++#else
+ #define MTK_WED_TX_TKID_CTRL_VLD_GRP_NUM		GENMASK(6, 0)
+ #define MTK_WED_TX_TKID_CTRL_RSV_GRP_NUM		GENMASK(22, 16)
++#endif
++
+ #define MTK_WED_TX_TKID_CTRL_PAUSE			BIT(28)
+ 
+ #define MTK_WED_TX_TKID_DYN_THR				0x0e0
+@@ -220,12 +235,15 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_PKT_PROC	BIT(5)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R0_CRX_SYNC	BIT(6)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_R1_CRX_SYNC	BIT(7)
+-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER	GENMASK(18, 16)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_VER	GENMASK(15, 12)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4	BIT(18)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNSUPPORT_FMT	BIT(19)
+-#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_UEVENT_PKT_FMT_CHK BIT(20)
++#define MTK_WED_WPDMA_GLO_CFG_RX_DRV_EVENT_PKT_FMT_CHK	BIT(20)
+ #define MTK_WED_WPDMA_GLO_CFG_RX_DDONE2_WR		BIT(21)
+ #define MTK_WED_WPDMA_GLO_CFG_TX_TKID_KEEP		BIT(24)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK_LAST		BIT(25)
+ #define MTK_WED_WPDMA_GLO_CFG_TX_DMAD_DW3_PREV		BIT(28)
++#define MTK_WED_WPDMA_GLO_CFG_TX_DDONE_CHK		BIT(30)
+ 
+ /* CONFIG_MEDIATEK_NETSYS_V1 */
+ #define MTK_WED_WPDMA_GLO_CFG_RX_BT_SIZE		GENMASK(5, 4)
+@@ -288,9 +306,11 @@ struct mtk_wdma_desc {
+ #define MTK_WED_PCIE_INT_TRIGGER_STATUS			BIT(16)
+ 
+ #define MTK_WED_PCIE_INT_CTRL				0x57c
+-#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA		BIT(20)
+-#define MTK_WED_PCIE_INT_CTRL_SRC_SEL			GENMASK(17, 16)
+ #define MTK_WED_PCIE_INT_CTRL_POLL_EN 			GENMASK(13, 12)
++#define MTK_WED_PCIE_INT_CTRL_SRC_SEL			GENMASK(17, 16)
++#define MTK_WED_PCIE_INT_CTRL_MSK_EN_POLA		BIT(20)
++#define MTK_WED_PCIE_INT_CTRL_MSK_IRQ_FILTER		BIT(21)
++
+ #define MTK_WED_WPDMA_CFG_BASE				0x580
+ #define MTK_WED_WPDMA_CFG_INT_MASK			0x584
+ #define MTK_WED_WPDMA_CFG_TX				0x588
+@@ -319,20 +339,50 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24)
+ 
+ #define MTK_WED_WPDMA_RX_GLO_CFG			0x76c
+-#define MTK_WED_WPDMA_RX_RING				0x770
++#if defined(CONFIG_MEDIATEK_NETSYS_V2)
++#define MTK_WED_WPDMA_RX_RING0				0x770
++#else
++#define MTK_WED_WPDMA_RX_RING0				0x7d0
++#endif
++#define MTK_WED_WPDMA_RX_RING1				0x7d8
+ 
+ #define MTK_WED_WPDMA_RX_D_MIB(_n)			(0x774 + (_n) * 4)
+ #define MTK_WED_WPDMA_RX_D_PROCESSED_MIB(_n)		(0x784 + (_n) * 4)
+ #define MTK_WED_WPDMA_RX_D_COHERENT_MIB			0x78c
+ 
++#define MTK_WED_WPDMA_RX_D_PREF_CFG			0x7b4
++#define MTK_WED_WPDMA_RX_D_PREF_EN			BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE		GENMASK(12, 8)
++#define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES		GENMASK(21, 16)
++
++#define MTK_WED_WPDMA_RX_D_PREF_RX0_SIDX		0x7b8
++#define MTK_WED_WPDMA_RX_D_PREF_SIDX_IDX_CLR		BIT(15)
++
++#define MTK_WED_WPDMA_RX_D_PREF_RX1_SIDX		0x7bc
++
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG		0x7c0
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R0_CLR		BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_FIFO_CFG_R1_CLR		BIT(16)
++
+ #define MTK_WED_WDMA_RING_TX				0x800
+ 
+ #define MTK_WED_WDMA_TX_MIB				0x810
+ 
+-
+ #define MTK_WED_WDMA_RING_RX(_n)			(0x900 + (_n) * 0x10)
+ #define MTK_WED_WDMA_RX_THRES(_n)			(0x940 + (_n) * 0x4)
+ 
++#define MTK_WED_WDMA_RX_PREF_CFG			0x950
++#define MTK_WED_WDMA_RX_PREF_EN				BIT(0)
++#define MTK_WED_WDMA_RX_PREF_BURST_SIZE			GENMASK(12, 8)
++#define MTK_WED_WDMA_RX_PREF_LOW_THRES			GENMASK(21, 16)
++#define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR		BIT(24)
++#define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR		BIT(25)
++#define MTK_WED_WDMA_RX_PREF_DDONE2_EN			BIT(26)
++
++#define MTK_WED_WDMA_RX_PREF_FIFO_CFG			0x95C
++#define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR		BIT(0)
++#define MTK_WED_WDMA_RX_PREF_FIFO_RX1_CLR		BIT(16)
++
+ #define MTK_WED_WDMA_GLO_CFG				0xa04
+ #define MTK_WED_WDMA_GLO_CFG_TX_DRV_EN			BIT(0)
+ #define MTK_WED_WDMA_GLO_CFG_TX_DDONE_CHK		BIT(1)
+@@ -365,6 +415,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WDMA_INT_TRIGGER_RX_DONE		GENMASK(17, 16)
+ 
+ #define MTK_WED_WDMA_INT_CTRL				0xa2c
++#define MTK_WED_WDMA_INT_POLL_PRD			GENMASK(7, 0)
+ #define MTK_WED_WDMA_INT_POLL_SRC_SEL			GENMASK(17, 16)
+ 
+ #define MTK_WED_WDMA_CFG_BASE				0xaa0
+@@ -426,6 +477,18 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_INT_GRP1				0x250
+ #define MTK_WDMA_INT_GRP2				0x254
+ 
++#define MTK_WDMA_PREF_TX_CFG				0x2d0
++#define MTK_WDMA_PREF_TX_CFG_PREF_EN			BIT(0)
++
++#define MTK_WDMA_PREF_RX_CFG				0x2dc
++#define MTK_WDMA_PREF_RX_CFG_PREF_EN			BIT(0)
++
++#define MTK_WDMA_WRBK_TX_CFG				0x300
++#define MTK_WDMA_WRBK_TX_CFG_WRBK_EN			BIT(30)
++
++#define MTK_WDMA_WRBK_RX_CFG				0x344
++#define MTK_WDMA_WRBK_RX_CFG_WRBK_EN			BIT(30)
++
+ #define MTK_PCIE_MIRROR_MAP(n)				((n) ? 0x4 : 0x0)
+ #define MTK_PCIE_MIRROR_MAP_EN				BIT(0)
+ #define MTK_PCIE_MIRROR_MAP_WED_ID			BIT(1)
+@@ -439,6 +502,31 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q_DBG_BYPASS			BIT(5)
+ #define MTK_WED_RTQM_TXDMAD_FPORT			GENMASK(23, 20)
+ 
++#define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT		0xb1c
++#define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)		(0xb20 + (_n) * 0x4)
++#define	MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT			0xb28
++#define MTK_WED_RTQM_IGRS0_I2H_PKT_CNT(_n)		(0xb2c + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS0_FDROP_CNT			0xb34
++
++
++#define MTK_WED_RTQM_IGRS1_I2HW_DMAD_CNT		0xb44
++#define MTK_WED_RTQM_IGRS1_I2H_DMAD_CNT(_n)		(0xb48 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS1_I2HW_PKT_CNT			0xb50
++#define MTK_WED_RTQM_IGRS1_I2H_PKT_CNT(_n)		(0xb54+ (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS1_FDROP_CNT			0xb5c
++
++#define MTK_WED_RTQM_IGRS2_I2HW_DMAD_CNT		0xb6c
++#define MTK_WED_RTQM_IGRS2_I2H_DMAD_CNT(_n)		(0xb70 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS2_I2HW_PKT_CNT			0xb78
++#define MTK_WED_RTQM_IGRS2_I2H_PKT_CNT(_n)		(0xb7c+ (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS2_FDROP_CNT			0xb84
++
++#define MTK_WED_RTQM_IGRS3_I2HW_DMAD_CNT		0xb94
++#define MTK_WED_RTQM_IGRS3_I2H_DMAD_CNT(_n)		(0xb98 + (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS3_I2HW_PKT_CNT			0xba0
++#define MTK_WED_RTQM_IGRS3_I2H_PKT_CNT(_n)		(0xba4+ (_n) * 0x4)
++#define MTK_WED_RTQM_IGRS3_FDROP_CNT			0xbac
++
+ #define MTK_WED_RTQM_R2H_MIB(_n)			(0xb70 + (_n) * 0x4)
+ #define MTK_WED_RTQM_R2Q_MIB(_n)			(0xb78 + (_n) * 0x4)
+ #define MTK_WED_RTQM_Q2N_MIB				0xb80
+@@ -447,6 +535,24 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q2B_MIB				0xb8c
+ #define MTK_WED_RTQM_PFDBK_MIB				0xb90
+ 
++#define MTK_WED_RTQM_ENQ_CFG0				0xbb8
++#define MTK_WED_RTQM_ENQ_CFG_TXDMAD_FPORT		GENMASK(15, 12)
++
++#define MTK_WED_RTQM_FDROP_MIB				0xb84
++#define MTK_WED_RTQM_ENQ_I2Q_DMAD_CNT			0xbbc
++#define MTK_WED_RTQM_ENQ_I2N_DMAD_CNT			0xbc0
++#define MTK_WED_RTQM_ENQ_I2Q_PKT_CNT			0xbc4
++#define MTK_WED_RTQM_ENQ_I2N_PKT_CNT			0xbc8
++#define MTK_WED_RTQM_ENQ_USED_ENTRY_CNT			0xbcc
++#define MTK_WED_RTQM_ENQ_ERR_CNT			0xbd0
++
++#define MTK_WED_RTQM_DEQ_DMAD_CNT			0xbd8
++#define MTK_WED_RTQM_DEQ_Q2I_DMAD_CNT			0xbdc
++#define MTK_WED_RTQM_DEQ_PKT_CNT			0xbe0
++#define MTK_WED_RTQM_DEQ_Q2I_PKT_CNT			0xbe4
++#define MTK_WED_RTQM_DEQ_USED_PFDBK_CNT			0xbe8
++#define MTK_WED_RTQM_DEQ_ERR_CNT			0xbec
++
+ #define MTK_WED_RROQM_GLO_CFG				0xc04
+ #define MTK_WED_RROQM_RST_IDX				0xc08
+ #define MTK_WED_RROQM_RST_IDX_MIOD 			BIT(0)
+@@ -487,8 +593,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RX_BM_BASE				0xd84
+ #define MTK_WED_RX_BM_INIT_PTR				0xd88
+ #define MTK_WED_RX_BM_PTR	      			0xd8c
+-#define MTK_WED_RX_BM_PTR_HEAD				GENMASK(32, 16)
+ #define MTK_WED_RX_BM_PTR_TAIL				GENMASK(15, 0)
++#define MTK_WED_RX_BM_PTR_HEAD				GENMASK(32, 16)
+ 
+ #define MTK_WED_RX_BM_BLEN	      			0xd90
+ #define MTK_WED_RX_BM_STS				0xd94
+@@ -496,7 +602,193 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RX_BM_INTF				0xd9c
+ #define MTK_WED_RX_BM_ERR_STS				0xda8
+ 
++#define MTK_RRO_IND_CMD_SIGNATURE			0xe00
++#define MTK_RRO_IND_CMD_DMA_IDX				GENMASK(11, 0)
++#define MTK_RRO_IND_CMD_MAGIC_CNT			GENMASK(30, 28)
++
++#define MTK_WED_IND_CMD_RX_CTRL0			0xe04
++#define MTK_WED_IND_CMD_PROC_IDX			GENMASK(11, 0)
++#define MTK_WED_IND_CMD_PREFETCH_FREE_CNT		GENMASK(19, 16)
++#define MTK_WED_IND_CMD_MAGIC_CNT			GENMASK(30, 28)
++
++#define MTK_WED_IND_CMD_RX_CTRL1			0xe08
++#define MTK_WED_IND_CMD_RX_CTRL2			0xe0c
++#define MTK_WED_IND_CMD_MAX_CNT				GENMASK(11, 0)
++#define MTK_WED_IND_CMD_BASE_M				GENMASK(19, 16)
++
++#define MTK_WED_RRO_CFG0				0xe10
++#define MTK_WED_RRO_CFG1				0xe14
++#define MTK_WED_RRO_CFG1_MAX_WIN_SZ			GENMASK(31, 29)
++#define MTK_WED_RRO_CFG1_ACK_SN_BASE_M			GENMASK(19, 16)
++#define MTK_WED_RRO_CFG1_PARTICL_SE_ID			GENMASK(11, 0)
++
++#define MTK_WED_ADDR_ELEM_CFG0				0xe18
++#define MTK_WED_ADDR_ELEM_CFG1				0xe1c
++#define MTK_WED_ADDR_ELEM_PREFETCH_FREE_CNT		GENMASK(19, 16)
++
++#define MTK_WED_ADDR_ELEM_TBL_CFG 			0xe20
++#define MTK_WED_ADDR_ELEM_TBL_OFFSET			GENMASK(6, 0)
++#define MTK_WED_ADDR_ELEM_TBL_RD_RDY			BIT(28)
++#define MTK_WED_ADDR_ELEM_TBL_WR_RDY			BIT(29)
++#define MTK_WED_ADDR_ELEM_TBL_RD			BIT(30)
++#define MTK_WED_ADDR_ELEM_TBL_WR			BIT(31)
++
++#define MTK_WED_RADDR_ELEM_TBL_WDATA 			0xe24
++#define MTK_WED_RADDR_ELEM_TBL_RDATA 			0xe28
++
++#define MTK_WED_PN_CHECK_CFG 				0xe30
++#define MTK_WED_PN_CHECK_SE_ID				GENMASK(11, 0)
++#define MTK_WED_PN_CHECK_RD_RDY				BIT(28)
++#define MTK_WED_PN_CHECK_WR_RDY				BIT(29)
++#define MTK_WED_PN_CHECK_RD				BIT(30)
++#define MTK_WED_PN_CHECK_WR				BIT(31)
++
++#define MTK_WED_PN_CHECK_WDATA_M 			0xe38
++#define MTK_WED_PN_CHECK_IS_FIRST			BIT(17)
++
++#define MTK_WED_RRO_MSDU_PG_RING_CFG(_n)		(0xe44 + (_n) * 0x8)
++
++#define MTK_WED_RRO_MSDU_PG_RING2_CFG			0xe58
++#define MTK_WED_RRO_MSDU_PG_DRV_CLR			BIT(26)
++#define MTK_WED_RRO_MSDU_PG_DRV_EN			BIT(31)
++
++#define MTK_WED_RRO_MSDU_PG_CTRL0(_n)			(0xe5c + (_n) * 0xc)
++#define MTK_WED_RRO_MSDU_PG_CTRL1(_n)			(0xe60 + (_n) * 0xc)
++#define MTK_WED_RRO_MSDU_PG_CTRL2(_n)			(0xe64 + (_n) * 0xc)
++
++#define MTK_WED_RRO_RX_D_RX(_n)				(0xe80 + (_n) * 0x10)
++
++#define MTK_WED_RRO_RX_MAGIC_CNT			BIT(13)
++
++#define MTK_WED_RRO_RX_D_CFG(_n)			(0xea0 + (_n) * 0x4)
++#define MTK_WED_RRO_RX_D_DRV_CLR			BIT(26)
++#define MTK_WED_RRO_RX_D_DRV_EN				BIT(31)
++
++#define MTK_WED_RRO_PG_BM_RX_DMAM			0xeb0
++#define MTK_WED_RRO_PG_BM_RX_SDL0			GENMASK(13, 0)
++
++#define MTK_WED_RRO_PG_BM_BASE				0xeb4
++#define MTK_WED_RRO_PG_BM_INIT_PTR			0xeb8
++#define MTK_WED_RRO_PG_BM_SW_TAIL_IDX			GENMASK(15, 0)
++#define MTK_WED_RRO_PG_BM_INIT_SW_TAIL_IDX		BIT(16)
++
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX			0xeec
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_EN		BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_CLR		BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX0_DONE_TRIG	GENMASK(6, 2)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_EN		BIT(8)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_CLR		BIT(9)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_RX1_DONE_TRIG	GENMASK(14, 10)
++
++#define MTK_WED_WPDMA_INT_CTRL_RRO_MSDU_PG		0xef4
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_EN		BIT(0)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_CLR		BIT(1)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG0_DONE_TRIG	GENMASK(6, 2)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_EN		BIT(8)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_CLR		BIT(9)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG1_DONE_TRIG	GENMASK(14, 10)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_EN		BIT(16)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR		BIT(17)
++#define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG	GENMASK(22, 18)
++
++#define MTK_WED_RX_IND_CMD_CNT0				0xf20
++#define MTK_WED_RX_IND_CMD_DBG_CNT_EN			BIT(31)
++
++#define MTK_WED_RX_IND_CMD_CNT(_n)			(0xf20 + (_n) * 0x4)
++#define MTK_WED_IND_CMD_MAGIC_CNT_FAIL_CNT		GENMASK(15, 0)
++
++#define MTK_WED_RX_ADDR_ELEM_CNT(_n)			(0xf48 + (_n) * 0x4)
++#define MTK_WED_ADDR_ELEM_SIG_FAIL_CNT			GENMASK(15, 0)
++#define MTK_WED_ADDR_ELEM_FIRST_SIG_FAIL_CNT		GENMASK(31, 16)
++#define MTK_WED_ADDR_ELEM_ACKSN_CNT			GENMASK(27, 0)
++
++#define MTK_WED_RX_MSDU_PG_CNT(_n)			(0xf5c + (_n) * 0x4)
++
++#define MTK_WED_RX_PN_CHK_CNT 				0xf70
++#define MTK_WED_PN_CHK_FAIL_CNT				GENMASK(15, 0)
++
+ #define MTK_WED_WOCPU_VIEW_MIOD_BASE		 	0x8000
+ #define MTK_WED_PCIE_INT_MASK				0x0
+ 
++#define MTK_WED_PAO_AMSDU_FIFO				0x1800
++#define MTK_WED_PAO_AMSDU_IS_PRIOR0_RING		BIT(10)
++
++#define MTK_WED_PAO_STA_INFO				0x01810
++#define MTK_WED_PAO_STA_INFO_DO_INIT			BIT(0)
++#define MTK_WED_PAO_STA_INFO_SET_INIT			BIT(1)
++
++#define MTK_WED_PAO_STA_INFO_INIT			0x01814
++#define MTK_WED_PAO_STA_WTBL_HDRT_MODE			BIT(0)
++#define MTK_WED_PAO_STA_RMVL				BIT(1)
++#define MTK_WED_PAO_STA_MAX_AMSDU_LEN			GENMASK(7, 2)
++#define MTK_WED_PAO_STA_MAX_AMSDU_NUM			GENMASK(11, 8)
++
++#define MTK_WED_PAO_HIFTXD_BASE_L(_n)			(0x1980 + (_n) * 0x4)
++
++#define MTK_WED_PAO_PSE					0x1910
++#define MTK_WED_PAO_PSE_RESET				BIT(16)
++
++#define MTK_WED_PAO_HIFTXD_CFG				0x1968
++#define MTK_WED_PAO_HIFTXD_SRC				GENMASK(16, 15)
++
++#define MTK_WED_PAO_MON_AMSDU_FIFO_DMAD			0x1a34
++
++#define MTK_WED_PAO_MON_AMSDU_ENG_DMAD(_n)		(0x1a80 + (_n) * 0x50)
++#define MTK_WED_PAO_MON_AMSDU_ENG_QFPL(_n)		(0x1a84 + (_n) * 0x50)
++#define MTK_WED_PAO_MON_AMSDU_ENG_QENI(_n)		(0x1a88 + (_n) * 0x50)
++#define MTK_WED_PAO_MON_AMSDU_ENG_QENO(_n)		(0x1a8c + (_n) * 0x50)
++#define MTK_WED_PAO_MON_AMSDU_ENG_MERG(_n)		(0x1a90 + (_n) * 0x50)
++
++#define MTK_WED_PAO_MON_AMSDU_ENG_CNT8(_n)		(0x1a94 + (_n) * 0x50)
++#define MTK_WED_PAO_AMSDU_ENG_MAX_QGPP_CNT		GENMASK(10, 0)
++#define MTK_WED_PAO_AMSDU_ENG_MAX_PL_CNT		GENMASK(27, 16)
++
++#define MTK_WED_PAO_MON_AMSDU_ENG_CNT9(_n)		(0x1a98 + (_n) * 0x50)
++#define MTK_WED_PAO_AMSDU_ENG_CUR_ENTRY			GENMASK(10, 0)
++#define MTK_WED_PAO_AMSDU_ENG_MAX_BUF_MERGED		GENMASK(20, 16)
++#define MTK_WED_PAO_AMSDU_ENG_MAX_MSDU_MERGED		GENMASK(28, 24)
++
++#define MTK_WED_PAO_MON_QMEM_STS1			0x1e04
++
++#define MTK_WED_PAO_MON_QMEM_CNT(_n)			(0x1e0c + (_n) * 0x4)
++#define MTK_WED_PAO_QMEM_FQ_CNT				GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_SP_QCNT			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID0_QCNT			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID1_QCNT			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID2_QCNT			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID3_QCNT			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID4_QCNT			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID5_QCNT			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID6_QCNT			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID7_QCNT			GENMASK(11, 0)
++
++#define MTK_WED_PAO_MON_QMEM_PTR(_n)			(0x1e20 + (_n) * 0x4)
++#define MTK_WED_PAO_QMEM_FQ_HEAD				GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_SP_QHEAD			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID0_QHEAD			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID1_QHEAD			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID2_QHEAD			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID3_QHEAD			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID4_QHEAD			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID5_QHEAD			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID6_QHEAD			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID7_QHEAD			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_FQ_TAIL			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_SP_QTAIL			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID0_QTAIL			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID1_QTAIL			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID2_QTAIL			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID3_QTAIL			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID4_QTAIL			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID5_QTAIL			GENMASK(11, 0)
++#define MTK_WED_PAO_QMEM_TID6_QTAIL			GENMASK(27, 16)
++#define MTK_WED_PAO_QMEM_TID7_QTAIL			GENMASK(11, 0)
++
++#define MTK_WED_PAO_MON_HIFTXD_FETCH_MSDU(_n)		(0x1ec4 + (_n) * 0x4)
++
++#define MTK_WED_PCIE_BASE			0x11280000
++
++#define MTK_WED_PCIE_BASE0			0x11300000
++#define MTK_WED_PCIE_BASE1			0x11310000
++#define MTK_WED_PCIE_BASE2			0x11290000
+ #endif
+diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
+index 58b5ce6..5e51790 100644
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -873,6 +873,13 @@ struct net_device_path {
+ 			u8 queue;
+ 			u16 wcid;
+ 			u8 bss;
++			u32 usr_info;
++			u8 tid;
++			u8 is_fixedrate;
++			u8 is_prior;
++			u8 is_sp;
++			u8 hf;
++			u8 amsdu_en;
+ 		} mtk_wdma;
+ 	};
+ };
+diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h
+index 27cf284..60336e0 100644
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -5,11 +5,14 @@
+ #include <linux/rcupdate.h>
+ #include <linux/regmap.h>
+ #include <linux/pci.h>
++#include <linux/skbuff.h>
++#include <linux/iopoll.h>
+ 
+ #define WED_WO_STA_REC			0x6
+ 
+ #define MTK_WED_TX_QUEUES		2
+ #define MTK_WED_RX_QUEUES		2
++#define MTK_WED_RX_PAGE_QUEUES         3
+ 
+ enum mtk_wed_wo_cmd {
+ 	MTK_WED_WO_CMD_WED_CFG,
+@@ -55,10 +58,13 @@ enum mtk_wed_bus_tye {
+ struct mtk_wed_hw;
+ struct mtk_wdma_desc;
+ 
++#define MTK_WED_RING_CONFIGURED		BIT(0)
++
+ struct mtk_wed_ring {
+ 	struct mtk_wdma_desc *desc;
+ 	dma_addr_t desc_phys;
+ 	int size;
++	u32 flags;
+ 
+ 	u32 reg_base;
+ 	void __iomem *wpdma;
+@@ -69,11 +75,18 @@ struct mtk_rxbm_desc {
+ 	__le32 token;
+ } __packed __aligned(4);
+ 
++struct dma_page_info {
++	void *addr;
++	dma_addr_t addr_phys;
++};
++
+ struct dma_buf {
+ 	int size;
+-	void **pages;
+-	struct mtk_wdma_desc *desc;
++	int pkt_nums;
++	void *desc;
++	int desc_size;
+ 	dma_addr_t desc_phys;
++	struct dma_page_info *pages;
+ };
+ 
+ struct dma_entry {
+@@ -97,6 +110,7 @@ struct mtk_wed_device {
+ 	struct device *dev;
+ 	struct mtk_wed_hw *hw;
+ 	bool init_done, running;
++	bool wdma_init_done;
+ 	int wdma_idx;
+ 	int irq;
+ 	u8 ver;
+@@ -108,7 +122,11 @@ struct mtk_wed_device {
+ 	struct mtk_wed_ring rx_ring[MTK_WED_RX_QUEUES];
+ 	struct mtk_wed_ring rx_wdma[MTK_WED_RX_QUEUES];
+ 
+-	struct dma_buf buf_ring;
++	struct mtk_wed_ring rx_rro_ring[MTK_WED_RX_QUEUES];
++	struct mtk_wed_ring rx_page_ring[MTK_WED_RX_PAGE_QUEUES];
++	struct mtk_wed_ring ind_cmd_ring;
++
++	struct dma_buf tx_buf_ring;
+ 
+ 	struct {
+ 		int size;
+@@ -117,6 +135,8 @@ struct mtk_wed_device {
+ 		dma_addr_t desc_phys;
+ 	} rx_buf_ring;
+ 
++	struct dma_buf rx_page_buf_ring;
++
+ 	struct {
+ 		struct mtk_wed_ring rro_ring;
+ 		void __iomem *rro_desc;
+@@ -131,8 +151,9 @@ struct mtk_wed_device {
+ 			struct platform_device *platform_dev;
+ 			struct pci_dev *pci_dev;
+ 		};
++		enum mtk_wed_bus_tye bus_type;
+ 		void __iomem *base;
+-		u32 bus_type;
++		void __iomem *regs;
+ 		u32 phy_base;
+ 
+ 		u32 wpdma_phys;
+@@ -142,9 +163,13 @@ struct mtk_wed_device {
+ 		u32 wpdma_txfree;
+ 		u32 wpdma_rx_glo;
+ 		u32 wpdma_rx;
++		u32 wpdma_rx_rro[MTK_WED_RX_QUEUES];
++		u32 wpdma_rx_pg;
+ 
+ 		u8 tx_tbit[MTK_WED_TX_QUEUES];
+ 		u8 rx_tbit[MTK_WED_RX_QUEUES];
++		u8 rro_rx_tbit[MTK_WED_RX_QUEUES];
++		u8 rx_pg_tbit[MTK_WED_RX_PAGE_QUEUES];
+ 		u8 txfree_tbit;
+ 
+ 		u16 token_start;
+@@ -154,12 +179,26 @@ struct mtk_wed_device {
+ 		unsigned int rx_size;
+ 
+ 		bool wcid_512;
+-
++		bool hwrro;
++		bool msi;
++
++		u8 max_amsdu_nums;
++		u32 max_amsdu_len;
++
++		struct {
++			u8 se_group_nums;
++			u16 win_size;
++			u16 particular_sid;
++			u32 ack_sn_addr;
++			dma_addr_t particular_se_phys;
++			dma_addr_t addr_elem_phys[1024];
++		} ind_cmd;
++
++		u32 chip_id;
+ 		u32 (*init_buf)(void *ptr, dma_addr_t phys, int token_id);
+ 		int (*offload_enable)(struct mtk_wed_device *wed);
+ 		void (*offload_disable)(struct mtk_wed_device *wed);
+-		u32 (*init_rx_buf)(struct mtk_wed_device *wed,
+-				   int pkt_num);
++		u32 (*init_rx_buf)(struct mtk_wed_device *wed, int size);
+ 		void (*release_rx_buf)(struct mtk_wed_device *wed);
+ 		void (*update_wo_rx_stats)(struct mtk_wed_device *wed,
+ 					   struct mtk_wed_wo_rx_stats *stats);
+@@ -180,6 +219,11 @@ struct mtk_wed_ops {
+ 				 void __iomem *regs);
+ 	int (*rx_ring_setup)(struct mtk_wed_device *dev, int ring,
+ 			     void __iomem *regs, bool reset);
++	int (*rro_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
++			     void __iomem *regs);
++	int (*msdu_pg_rx_ring_setup)(struct mtk_wed_device *dev, int ring,
++			     void __iomem *regs);
++	int (*ind_rx_ring_setup)(struct mtk_wed_device *dev, void __iomem *regs);
+ 	int (*msg_update)(struct mtk_wed_device *dev, int cmd_id,
+ 			  void *data, int len);
+ 	void (*detach)(struct mtk_wed_device *dev);
+@@ -196,6 +240,7 @@ struct mtk_wed_ops {
+ 	void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+ 	void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb,
+ 			  u32 reason, u32 hash);
++	void (*start_hwrro)(struct mtk_wed_device *dev, u32 irq_mask);
+ };
+ 
+ extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
+@@ -224,12 +269,21 @@ static inline bool
+ mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
+ {
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
++	if (dev->ver == 3 && !dev->wlan.hwrro)
++		return false;
++
+ 	return dev->ver != 1;
+ #else
+ 	return false;
+ #endif
+ }
+ 
++static inline bool
++mtk_wed_device_support_pao(struct mtk_wed_device *dev)
++{
++	return dev->ver == 3;
++}
++
+ #ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ #define mtk_wed_device_active(_dev) !!(_dev)->ops
+ #define mtk_wed_device_detach(_dev) (_dev)->ops->detach(_dev)
+@@ -243,6 +297,12 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
+ 	(_dev)->ops->txfree_ring_setup(_dev, _regs)
+ #define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) \
+ 	(_dev)->ops->rx_ring_setup(_dev, _ring, _regs, _reset)
++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) \
++	(_dev)->ops->rro_rx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs) \
++	(_dev)->ops->msdu_pg_rx_ring_setup(_dev, _ring, _regs)
++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs) \
++	(_dev)->ops->ind_rx_ring_setup(_dev, _regs)
+ #define mtk_wed_device_update_msg(_dev, _id, _msg, _len) \
+ 	(_dev)->ops->msg_update(_dev, _id, _msg, _len)
+ #define mtk_wed_device_reg_read(_dev, _reg) \
+@@ -257,6 +317,9 @@ mtk_wed_get_rx_capa(struct mtk_wed_device *dev)
+ 	(_dev)->ops->reset_dma(_dev)
+ #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \
+ 	(_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
++#define mtk_wed_device_start_hwrro(_dev, _mask) \
++	(_dev)->ops->start_hwrro(_dev, _mask)
++
+ #else
+ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ {
+@@ -268,6 +331,9 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ #define mtk_wed_device_tx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV
+ #define mtk_wed_device_txfree_ring_setup(_dev, _ring, _regs) -ENODEV
+ #define mtk_wed_device_rx_ring_setup(_dev, _ring, _regs, _reset) -ENODEV
++#define mtk_wed_device_rro_rx_ring_setup(_dev, _ring, _regs) -ENODEV
++#define mtk_wed_device_msdu_pg_rx_ring_setup(_dev, _ring, _regs)  -ENODEV
++#define mtk_wed_device_ind_rx_ring_setup(_dev, _regs)  -ENODEV
+ #define mtk_wed_device_reg_read(_dev, _reg) 0
+ #define mtk_wed_device_reg_write(_dev, _reg, _val) do {} while (0)
+ #define mtk_wed_device_irq_get(_dev, _mask) 0
+@@ -275,6 +341,7 @@ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+ #define mtk_wed_device_dma_reset(_dev) do {} while (0)
+ #define mtk_wed_device_setup_tc(_dev, _ndev, _type, _data) do {} while (0)
+ #define mtk_wed_device_ppe_check(_dev, _hash)  do {} while (0)
++#define mtk_wed_device_start_hwrro(_dev, _mask) do {} while (0)
+ #endif
+ 
+ #endif
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3022-mtk-wed-add-wed3-ser-support.patch b/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3022-mtk-wed-add-wed3-ser-support.patch
new file mode 100644
index 0000000..3837d8d
--- /dev/null
+++ b/autobuild_mac80211_release/mt7988_mt7996_mac80211/target/linux/mediatek/patches-5.4/999-3022-mtk-wed-add-wed3-ser-support.patch
@@ -0,0 +1,611 @@
+From 7304ce8edabcbc34433307b02de429c2d118abaa Mon Sep 17 00:00:00 2001
+From: mtk27745 <rex.lu@mediatek.com>
+Date: Tue, 23 May 2023 11:19:30 +0800
+Subject: [PATCH] mtk-wed-add-wed3-ser-support
+
+---
+ drivers/net/ethernet/mediatek/mtk_wed.c      | 236 +++++++++++++++++--
+ drivers/net/ethernet/mediatek/mtk_wed_regs.h |  73 +++++-
+ include/linux/soc/mediatek/mtk_wed.h         |   6 +-
+ 3 files changed, 291 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed.c b/drivers/net/ethernet/mediatek/mtk_wed.c
+index 6ed1c83..990888d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed.c
++++ b/drivers/net/ethernet/mediatek/mtk_wed.c
+@@ -99,11 +99,65 @@ mtk_wdma_rx_reset(struct mtk_wed_device *dev)
+ 	u32 status;
+ 	u32 mask = MTK_WDMA_GLO_CFG_RX_DMA_BUSY;
+ 	int busy, i;
++	u32 value;
+ 
+ 	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_RX_DMA_EN);
+ 	busy = readx_poll_timeout(mtk_wdma_read_reset, dev, status,
+-			       !(status & mask), 0, 10000);
++				  !(status & mask), 0, 10000);
+ 
++	if (dev->hw->version == 3) {
++		wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++		wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_PREF_TX_CFG);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_PREF_RX_CFG);
++
++		wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++		wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_WRBK_TX_CFG);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_WRBK_RX_CFG);
++
++		/* Prefetch FIFO */
++		wdma_w32(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
++			 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
++			 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
++		wdma_clr(dev, MTK_WDMA_PREF_RX_FIFO_CFG,
++			 MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR |
++			 MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR);
++
++		/* Core FIFO */
++		value = (MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR);
++
++		wdma_w32(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, value);
++		wdma_clr(dev, MTK_WDMA_XDMA_RX_FIFO_CFG, value);
++
++		/* Writeback FIFO */
++		wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++		wdma_w32(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++
++		wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(0), MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_WRBK_RX_FIFO_CFG(1), MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR);
++
++		/* Prefetch ring status */
++		wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR);
++		/* Writeback ring status */
++		wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR);
++	}
+ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_RX);
+ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+ 
+@@ -121,13 +175,62 @@ mtk_wdma_tx_reset(struct mtk_wed_device *dev)
+ {
+ 	u32 status;
+ 	u32 mask = MTK_WDMA_GLO_CFG_TX_DMA_BUSY;
+-	int i;
++	int busy, i;
++	u32 value;
+ 
+ 	wdma_clr(dev, MTK_WDMA_GLO_CFG, MTK_WDMA_GLO_CFG_TX_DMA_EN);
+ 	if (readx_poll_timeout(mtk_wdma_read_reset, dev, status,
+ 			       !(status & mask), 0, 10000))
+ 		WARN_ON_ONCE(1);
+ 
++	if (dev->hw->version == 3) {
++		wdma_clr(dev, MTK_WDMA_PREF_TX_CFG, MTK_WDMA_PREF_TX_CFG_PREF_EN);
++		wdma_clr(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_PREF_TX_CFG_PREF_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_PREF_TX_CFG);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_PREF_RX_CFG_PREF_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_PREF_RX_CFG);
++
++		wdma_clr(dev, MTK_WDMA_WRBK_TX_CFG, MTK_WDMA_WRBK_TX_CFG_WRBK_EN);
++		wdma_clr(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_WRBK_TX_CFG);
++		busy = read_poll_timeout(wdma_r32, status,
++					 !(status & MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY), 0, 10000,
++					 false, dev, MTK_WDMA_WRBK_RX_CFG);
++
++		/* Prefetch FIFO */
++		wdma_w32(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
++			 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
++			 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
++		wdma_clr(dev, MTK_WDMA_PREF_TX_FIFO_CFG,
++			 MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR |
++			 MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR);
++		/* Core FIFO */
++		value = (MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR |
++			 MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR);
++
++		wdma_w32(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, value);
++		wdma_clr(dev, MTK_WDMA_XDMA_TX_FIFO_CFG, value);
++		/* Writeback FIFO */
++		wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++		wdma_w32(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++
++		wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(0), MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_WRBK_TX_FIFO_CFG(1), MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR);
++
++		/* Prefetch ring status */
++		wdma_w32(dev, MTK_WDMA_PREF_SIDX_CFG, MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_PREF_SIDX_CFG, MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR);
++		/* Writeback ring status */
++		wdma_w32(dev, MTK_WDMA_WRBK_SIDX_CFG, MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
++		wdma_clr(dev, MTK_WDMA_WRBK_SIDX_CFG, MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR);
++	}
+ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, MTK_WDMA_RESET_IDX_TX);
+ 	wdma_w32(dev, MTK_WDMA_RESET_IDX, 0);
+ 	for (i = 0; i < ARRAY_SIZE(dev->tx_wdma); i++)
+@@ -903,7 +1006,7 @@ mtk_wed_dma_enable(struct mtk_wed_device *dev)
+ 				MTK_WED_WPDMA_GLO_CFG_RX_DRV_UNS_VER_FORCE_4);
+ 
+ 			wdma_set(dev, MTK_WDMA_PREF_RX_CFG, MTK_WDMA_PREF_RX_CFG_PREF_EN);
+-			//wdma_w32(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
++			wdma_set(dev, MTK_WDMA_WRBK_RX_CFG, MTK_WDMA_WRBK_RX_CFG_WRBK_EN);
+ 			if (mtk_wed_get_rx_capa(dev)) {
+ 				wed_set(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
+ 					MTK_WED_WPDMA_RX_D_PREF_EN |
+@@ -1477,13 +1580,30 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 	mtk_wed_mcu_send_msg(wo, MODULE_ID_WO, MTK_WED_WO_CMD_CHANGE_STATE,
+ 			     &state, sizeof(state), true);
+ 
++	if (dev->wlan.hwrro) {
++		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_IND_CMD_EN);
++		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_HW_STS,
++				  MTK_WED_RX_IND_CMD_BUSY);
++		mtk_wed_reset(dev, MTK_WED_RESET_RRO_RX_TO_PG);
++	}
+ 	wed_clr(dev, MTK_WED_WPDMA_RX_D_GLO_CFG, MTK_WED_WPDMA_RX_D_RX_DRV_EN);
+ 	busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_GLO_CFG,
+ 				 MTK_WED_WPDMA_RX_D_RX_DRV_BUSY);
++	if (dev->hw->version == 3)
++		busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++					 MTK_WED_WPDMA_RX_D_PREF_BUSY);
+ 	if (busy) {
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_D_DRV);
+ 	} else {
++		if (dev->hw->version == 3) {
++			/*1.a. Disable Prefetch HW*/
++			wed_clr(dev, MTK_WED_WPDMA_RX_D_PREF_CFG, MTK_WED_WPDMA_RX_D_PREF_EN);
++			mtk_wed_poll_busy(dev, MTK_WED_WPDMA_RX_D_PREF_CFG,
++					  MTK_WED_WPDMA_RX_D_PREF_BUSY);
++			wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
++				MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL);
++		}
+ 		wed_w32(dev, MTK_WED_WPDMA_RX_D_RST_IDX,
+ 			MTK_WED_WPDMA_RX_D_RST_CRX_IDX |
+ 			MTK_WED_WPDMA_RX_D_RST_DRV_IDX);
+@@ -1511,6 +1631,24 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 		wed_w32(dev, MTK_WED_RROQM_RST_IDX, 0);
+ 	}
+ 
++	if (dev->wlan.hwrro) {
++		/* Disable RRO MSDU Page Drv */
++		wed_clr(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_EN);
++
++		/* Disable RRO Data Drv */
++		wed_clr(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_EN);
++
++		/* RRO MSDU Page Drv Reset */
++		wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_CLR);
++		mtk_wed_poll_busy(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG,
++				  MTK_WED_RRO_MSDU_PG_DRV_CLR);
++
++		/* RRO Data Drv Reset */
++		wed_w32(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_RX_D_DRV_CLR);
++		mtk_wed_poll_busy(dev, MTK_WED_RRO_RX_D_CFG(2),
++				  MTK_WED_RRO_RX_D_DRV_CLR);
++	}
++
+ 	/* reset route qm */
+ 	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_RX_ROUTE_QM_EN);
+ 	busy = mtk_wed_poll_busy(dev, MTK_WED_CTRL,
+@@ -1518,8 +1656,13 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 	if (busy) {
+ 		mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
+ 	} else {
+-		wed_set(dev, MTK_WED_RTQM_GLO_CFG,
+-			MTK_WED_RTQM_Q_RST);
++		if (dev->hw->version == 3) {
++			wed_set(dev, MTK_WED_RTQM_RST, BIT(0));
++			wed_clr(dev, MTK_WED_RTQM_RST, BIT(0));
++			mtk_wed_reset(dev, MTK_WED_RESET_RX_ROUTE_QM);
++		} else
++			wed_set(dev, MTK_WED_RTQM_GLO_CFG,
++				MTK_WED_RTQM_Q_RST);
+ 	}
+ 
+ 	/* reset tx wdma */
+@@ -1527,8 +1670,13 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 
+ 	/* reset tx wdma drv */
+ 	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_TX_DRV_EN);
+-	mtk_wed_poll_busy(dev, MTK_WED_CTRL,
+-			  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
++	if (dev->hw->version == 3)
++		mtk_wed_poll_busy(dev, MTK_WED_WPDMA_STATUS,
++				  MTK_WED_WPDMA_STATUS_TX_DRV);
++	else
++		mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++				  MTK_WED_CTRL_WDMA_INT_AGENT_BUSY);
++
+ 	mtk_wed_reset(dev, MTK_WED_RESET_WDMA_TX_DRV);
+ 
+ 	/* reset wed rx dma */
+@@ -1546,9 +1694,17 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 	/* reset rx bm */
+ 	wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_BM_EN);
+ 	mtk_wed_poll_busy(dev, MTK_WED_CTRL,
+-			   MTK_WED_CTRL_WED_RX_BM_BUSY);
++			  MTK_WED_CTRL_WED_RX_BM_BUSY);
+ 	mtk_wed_reset(dev, MTK_WED_RESET_RX_BM);
+ 
++	if (dev->wlan.hwrro) {
++		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_WED_RX_PG_BM_EN);
++		mtk_wed_poll_busy(dev, MTK_WED_CTRL,
++				  MTK_WED_CTRL_WED_RX_PG_BM_BUSY);
++		wed_set(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
++		wed_clr(dev, MTK_WED_RESET, MTK_WED_RESET_RX_PG_BM);
++	}
++
+ 	/* wo change to enable state */
+ 	state = WO_STATE_ENABLE;
+ 	mtk_wed_mcu_send_msg(wo, MODULE_ID_WO, MTK_WED_WO_CMD_CHANGE_STATE,
+@@ -1565,6 +1721,9 @@ mtk_wed_rx_reset(struct mtk_wed_device *dev)
+ 	}
+ 
+ 	mtk_wed_free_rx_buffer(dev);
++
++	if (dev->wlan.hwrro)
++		mtk_wed_rx_page_free_buffer(dev);
+ }
+ 
+ 
+@@ -1598,18 +1757,40 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+ 
+ 	/* 2. Reset WDMA Rx DMA/Driver_Engine */
+ 	busy = !!mtk_wdma_rx_reset(dev);
++	if (dev->hw->version == 3) {
++		val = wed_r32(dev, MTK_WED_WDMA_GLO_CFG);
++		val |= MTK_WED_WDMA_GLO_CFG_RX_DIS_FSM_AUTO_IDLE;
++		val &= ~MTK_WED_WDMA_GLO_CFG_RX_DRV_EN;
++		wed_w32(dev, MTK_WED_WDMA_GLO_CFG, val);
++	} else
++		wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+ 
+-	wed_clr(dev, MTK_WED_WDMA_GLO_CFG, MTK_WED_WDMA_GLO_CFG_RX_DRV_EN);
+ 	busy = !!(busy ||
+ 		  mtk_wed_poll_busy(dev, MTK_WED_WDMA_GLO_CFG,
+-					 MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY));
++				    MTK_WED_WDMA_GLO_CFG_RX_DRV_BUSY));
++	if (dev->hw->version == 3)
++		busy = !!(busy ||
++			  mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
++					    MTK_WED_WDMA_RX_PREF_BUSY));
+ 
+ 	if (busy) {
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_INT_AGENT);
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WDMA_RX_DRV);
+ 	} else {
++		if (dev->hw->version == 3) {
++			/*1.a. Disable Prefetch HW*/
++			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_EN);
++			mtk_wed_poll_busy(dev, MTK_WED_WDMA_RX_PREF_CFG,
++					  MTK_WED_WDMA_RX_PREF_BUSY);
++			wed_clr(dev, MTK_WED_WDMA_RX_PREF_CFG, MTK_WED_WDMA_RX_PREF_DDONE2_EN);
++
++			/*2. Reset dma index*/
++			wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
++				MTK_WED_WDMA_RESET_IDX_RX_ALL);
++		}
+ 		wed_w32(dev, MTK_WED_WDMA_RESET_IDX,
+-			MTK_WED_WDMA_RESET_IDX_RX | MTK_WED_WDMA_RESET_IDX_DRV);
++			MTK_WED_WDMA_RESET_IDX_RX |
++			MTK_WED_WDMA_RESET_IDX_DRV);
+ 		wed_w32(dev, MTK_WED_WDMA_RESET_IDX, 0);
+ 
+ 		wed_set(dev, MTK_WED_WDMA_GLO_CFG,
+@@ -1624,9 +1805,15 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+ 		MTK_WED_CTRL_WED_TX_FREE_AGENT_EN);
+ 
+ 	for (i = 0; i < 100; i++) {
+-		val = wed_r32(dev, MTK_WED_TX_BM_INTF);
+-		if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
+-			break;
++		if (dev->ver > MTK_WED_V1) {
++			val = wed_r32(dev, MTK_WED_TX_TKID_INTF);
++			if (FIELD_GET(MTK_WED_TX_TKID_INTF_TKFIFO_FDEP, val) == 0x40)
++				break;
++		} else {
++			val = wed_r32(dev, MTK_WED_TX_BM_INTF);
++			if (FIELD_GET(MTK_WED_TX_BM_INTF_TKFIFO_FDEP, val) == 0x40)
++				break;
++		}
+ 	}
+ 	mtk_wed_reset(dev, MTK_WED_RESET_TX_FREE_AGENT);
+ 
+@@ -1635,18 +1822,20 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+ 
+ 	/* 4. Reset WED WPDMA Tx Driver Engine */
+ 	busy = mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
+-				      MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY);
++				 MTK_WED_WPDMA_GLO_CFG_TX_DRV_BUSY);
+ 	wed_clr(dev, MTK_WED_WPDMA_GLO_CFG,
+ 		MTK_WED_WPDMA_GLO_CFG_TX_DRV_EN |
+ 		MTK_WED_WPDMA_GLO_CFG_RX_DRV_EN);
+ 
+ 	busy = !!(busy ||
+ 		  mtk_wed_poll_busy(dev, MTK_WED_WPDMA_GLO_CFG,
+-					 MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY));
++				    MTK_WED_WPDMA_GLO_CFG_RX_DRV_BUSY));
+ 	if (busy) {
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_INT_AGENT);
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_TX_DRV);
+ 		mtk_wed_reset(dev, MTK_WED_RESET_WPDMA_RX_DRV);
++		if (dev->hw->version == 3)
++			wed_w32(dev, MTK_WED_RX1_CTRL2, 0);
+ 	} else {
+ 		wed_w32(dev, MTK_WED_WPDMA_RESET_IDX,
+ 			MTK_WED_WPDMA_RESET_IDX_TX |
+@@ -1659,7 +1848,13 @@ mtk_wed_reset_dma(struct mtk_wed_device *dev)
+ 		}
+ 	}
+ 
+-	if (dev->ver > MTK_WED_V1) {
++	if (dev->hw->version == 3) {
++		/*reset wed pao*/
++		wed_clr(dev, MTK_WED_CTRL, MTK_WED_CTRL_TX_PAO_EN);
++		mtk_wed_reset(dev, MTK_WED_RESET_TX_PAO);
++	}
++
++	if (mtk_wed_get_rx_capa(dev)) {
+ 		dev->init_done = false;
+ 		mtk_wed_rx_reset(dev);
+ 	}
+@@ -1874,7 +2069,7 @@ mtk_wed_ppe_check(struct mtk_wed_device *dev, struct sk_buff *skb,
+ }
+ 
+ static void
+-mtk_wed_start_hwrro(struct mtk_wed_device *dev, u32 irq_mask)
++mtk_wed_start_hwrro(struct mtk_wed_device *dev, u32 irq_mask, bool reset)
+ {
+ 	int idx, ret;
+ 
+@@ -1884,6 +2079,11 @@ mtk_wed_start_hwrro(struct mtk_wed_device *dev, u32 irq_mask)
+ 	if (!mtk_wed_get_rx_capa(dev) || !dev->wlan.hwrro)
+ 		return;
+ 
++	if (reset) {
++		wed_set(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_EN);
++		return;
++	}
++	
+ 	wed_set(dev, MTK_WED_RRO_RX_D_CFG(2), MTK_WED_RRO_MSDU_PG_DRV_CLR);
+ 	wed_w32(dev, MTK_WED_RRO_MSDU_PG_RING2_CFG, MTK_WED_RRO_MSDU_PG_DRV_CLR);
+ 
+diff --git a/drivers/net/ethernet/mediatek/mtk_wed_regs.h b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+index 25be547..4379dc4 100644
+--- a/drivers/net/ethernet/mediatek/mtk_wed_regs.h
++++ b/drivers/net/ethernet/mediatek/mtk_wed_regs.h
+@@ -42,6 +42,8 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RESET					0x008
+ #define MTK_WED_RESET_TX_BM				BIT(0)
+ #define MTK_WED_RESET_RX_BM				BIT(1)
++#define MTK_WED_RESET_RX_PG_BM				BIT(2)
++#define MTK_WED_RESET_RRO_RX_TO_PG			BIT(3)
+ #define MTK_WED_RESET_TX_FREE_AGENT			BIT(4)
+ #define MTK_WED_RESET_WPDMA_TX_DRV			BIT(8)
+ #define MTK_WED_RESET_WPDMA_RX_DRV			BIT(9)
+@@ -64,7 +66,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_CTRL_WDMA_INT_AGENT_BUSY		BIT(3)
+ #define MTK_WED_CTRL_WED_RX_IND_CMD_EN			BIT(5)
+ #define MTK_WED_CTRL_WED_RX_PG_BM_EN			BIT(6)
+-#define MTK_WED_CTRL_WED_RX_PG_BM_BUSU			BIT(7)
++#define MTK_WED_CTRL_WED_RX_PG_BM_BUSY			BIT(7)
+ #define MTK_WED_CTRL_WED_TX_BM_EN			BIT(8)
+ #define MTK_WED_CTRL_WED_TX_BM_BUSY			BIT(9)
+ #define MTK_WED_CTRL_WED_TX_FREE_AGENT_EN		BIT(10)
+@@ -123,6 +125,10 @@ struct mtk_wdma_desc {
+ #define MTK_WED_STATUS					0x060
+ #define MTK_WED_STATUS_TX				GENMASK(15, 8)
+ 
++#define MTK_WED_WPDMA_STATUS				0x068
++#define MTK_WED_WPDMA_STATUS_TX_DRV			GENMASK(15, 8)
++
++
+ #define MTK_WED_TX_BM_CTRL				0x080
+ #define MTK_WED_TX_BM_CTRL_VLD_GRP_NUM			GENMASK(6, 0)
+ #define MTK_WED_TX_BM_CTRL_RSV_GRP_NUM			GENMASK(22, 16)
+@@ -167,6 +173,9 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WED_TX_TKID_CTRL_PAUSE			BIT(28)
+ 
++#define MTK_WED_TX_TKID_INTF				0x0dc
++#define MTK_WED_TX_TKID_INTF_TKFIFO_FDEP		GENMASK(25, 16)
++
+ #define MTK_WED_TX_TKID_DYN_THR				0x0e0
+ #define MTK_WED_TX_TKID_DYN_THR_LO			GENMASK(6, 0)
+ #define MTK_WED_TX_TKID_DYN_THR_HI			GENMASK(22, 16)
+@@ -203,10 +212,11 @@ struct mtk_wdma_desc {
+ #define MTK_WED_GLO_CFG_RX_2B_OFFSET			BIT(31)
+ 
+ #define MTK_WED_RESET_IDX				0x20c
+-#define MTK_WED_RESET_IDX_TX				GENMASK(3, 0)
+-#if defined(CONFIG_MEDIATEK_NETSYS_V2)
++#if defined(CONFIG_MEDIATEK_NETSYS_V2) || defined(CONFIG_MEDIATEK_NETSYS_V3)
++#define MTK_WED_RESET_IDX_TX				GENMASK(1, 0)
+ #define MTK_WED_RESET_IDX_RX				GENMASK(7, 6)
+ #else
++#define MTK_WED_RESET_IDX_TX				GENMASK(3, 0)
+ #define MTK_WED_RESET_IDX_RX				GENMASK(17, 16)
+ #endif
+ #define MTK_WED_RESET_WPDMA_IDX_RX			GENMASK(31, 30)
+@@ -221,6 +231,7 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RING_RX_DATA(_n)			(0x420 + (_n) * 0x10)
+ 
+ #define MTK_WED_SCR0					0x3c0
++#define MTK_WED_RX1_CTRL2				0x418
+ #define MTK_WED_WPDMA_INT_TRIGGER			0x504
+ #define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE		BIT(1)
+ #define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE		GENMASK(5, 4)
+@@ -336,6 +347,7 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WED_WPDMA_RX_D_RST_IDX			0x760
+ #define MTK_WED_WPDMA_RX_D_RST_CRX_IDX			GENMASK(17, 16)
++#define MTK_WED_WPDMA_RX_D_RST_DRV_IDX_ALL		BIT(20)
+ #define MTK_WED_WPDMA_RX_D_RST_DRV_IDX			GENMASK(25, 24)
+ 
+ #define MTK_WED_WPDMA_RX_GLO_CFG			0x76c
+@@ -352,6 +364,7 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WED_WPDMA_RX_D_PREF_CFG			0x7b4
+ #define MTK_WED_WPDMA_RX_D_PREF_EN			BIT(0)
++#define MTK_WED_WPDMA_RX_D_PREF_BUSY		BIT(1)
+ #define MTK_WED_WPDMA_RX_D_PREF_BURST_SIZE		GENMASK(12, 8)
+ #define MTK_WED_WPDMA_RX_D_PREF_LOW_THRES		GENMASK(21, 16)
+ 
+@@ -373,11 +386,13 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WED_WDMA_RX_PREF_CFG			0x950
+ #define MTK_WED_WDMA_RX_PREF_EN				BIT(0)
++#define MTK_WED_WDMA_RX_PREF_BUSY			BIT(1)
+ #define MTK_WED_WDMA_RX_PREF_BURST_SIZE			GENMASK(12, 8)
+ #define MTK_WED_WDMA_RX_PREF_LOW_THRES			GENMASK(21, 16)
+ #define MTK_WED_WDMA_RX_PREF_RX0_SIDX_CLR		BIT(24)
+ #define MTK_WED_WDMA_RX_PREF_RX1_SIDX_CLR		BIT(25)
+ #define MTK_WED_WDMA_RX_PREF_DDONE2_EN			BIT(26)
++#define MTK_WED_WDMA_RX_PREF_DDONE2_BUSY		BIT(27)
+ 
+ #define MTK_WED_WDMA_RX_PREF_FIFO_CFG			0x95C
+ #define MTK_WED_WDMA_RX_PREF_FIFO_RX0_CLR		BIT(0)
+@@ -406,6 +421,7 @@ struct mtk_wdma_desc {
+ 
+ #define MTK_WED_WDMA_RESET_IDX				0xa08
+ #define MTK_WED_WDMA_RESET_IDX_RX			GENMASK(17, 16)
++#define MTK_WED_WDMA_RESET_IDX_RX_ALL			BIT(20)
+ #define MTK_WED_WDMA_RESET_IDX_DRV			GENMASK(25, 24)
+ 
+ #define MTK_WED_WDMA_INT_CLR				0xa24
+@@ -474,21 +490,66 @@ struct mtk_wdma_desc {
+ #define MTK_WDMA_INT_MASK_RX_DELAY			BIT(30)
+ #define MTK_WDMA_INT_MASK_RX_COHERENT			BIT(31)
+ 
++#define MTK_WDMA_XDMA_TX_FIFO_CFG			0x238
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_PAR_FIFO_CLEAR		BIT(0)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_CMD_FIFO_CLEAR		BIT(4)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_DMAD_FIFO_CLEAR		BIT(8)
++#define MTK_WDMA_XDMA_TX_FIFO_CFG_TX_ARR_FIFO_CLEAR		BIT(12)
++
++#define MTK_WDMA_XDMA_RX_FIFO_CFG			0x23c
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_PAR_FIFO_CLEAR		BIT(0)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_CMD_FIFO_CLEAR		BIT(4)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_DMAD_FIFO_CLEAR		BIT(8)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_ARR_FIFO_CLEAR		BIT(12)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_LEN_FIFO_CLEAR		BIT(15)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_WID_FIFO_CLEAR		BIT(18)
++#define MTK_WDMA_XDMA_RX_FIFO_CFG_RX_BID_FIFO_CLEAR		BIT(21)
++
++
++
+ #define MTK_WDMA_INT_GRP1				0x250
+ #define MTK_WDMA_INT_GRP2				0x254
+ 
+ #define MTK_WDMA_PREF_TX_CFG				0x2d0
+ #define MTK_WDMA_PREF_TX_CFG_PREF_EN			BIT(0)
++#define MTK_WDMA_PREF_TX_CFG_PREF_BUSY			BIT(1)
+ 
+ #define MTK_WDMA_PREF_RX_CFG				0x2dc
+ #define MTK_WDMA_PREF_RX_CFG_PREF_EN			BIT(0)
++#define MTK_WDMA_PREF_RX_CFG_PREF_BUSY			BIT(1)
++
++#define MTK_WDMA_PREF_RX_FIFO_CFG			0x2e0
++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING0_CLEAR		BIT(0)
++#define MTK_WDMA_PREF_RX_FIFO_CFG_RING1_CLEAR		BIT(16)
++
++#define MTK_WDMA_PREF_TX_FIFO_CFG			0x2d4
++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING0_CLEAR		BIT(0)
++#define MTK_WDMA_PREF_TX_FIFO_CFG_RING1_CLEAR		BIT(16)
++
++#define MTK_WDMA_PREF_SIDX_CFG				0x2e4
++#define MTK_WDMA_PREF_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0)
++#define MTK_WDMA_PREF_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4)
+ 
+ #define MTK_WDMA_WRBK_TX_CFG				0x300
++#define MTK_WDMA_WRBK_TX_CFG_WRBK_BUSY			BIT(0)
+ #define MTK_WDMA_WRBK_TX_CFG_WRBK_EN			BIT(30)
+ 
++#define MTK_WDMA_WRBK_TX_FIFO_CFG(_n)			(0x304 + (_n) * 0x4)
++#define MTK_WDMA_WRBK_TX_FIFO_CFG_RING_CLEAR		BIT(0)
++
++
+ #define MTK_WDMA_WRBK_RX_CFG				0x344
++#define MTK_WDMA_WRBK_RX_CFG_WRBK_BUSY			BIT(0)
+ #define MTK_WDMA_WRBK_RX_CFG_WRBK_EN			BIT(30)
+ 
++#define MTK_WDMA_WRBK_RX_FIFO_CFG(_n)			(0x348 + (_n) * 0x4)
++#define MTK_WDMA_WRBK_RX_FIFO_CFG_RING_CLEAR		BIT(0)
++
++
++#define MTK_WDMA_WRBK_SIDX_CFG				0x388
++#define MTK_WDMA_WRBK_SIDX_CFG_TX_RING_CLEAR		GENMASK(3, 0)
++#define MTK_WDMA_WRBK_SIDX_CFG_RX_RING_CLEAR		GENMASK(5, 4)
++
+ #define MTK_PCIE_MIRROR_MAP(n)				((n) ? 0x4 : 0x0)
+ #define MTK_PCIE_MIRROR_MAP_EN				BIT(0)
+ #define MTK_PCIE_MIRROR_MAP_WED_ID			BIT(1)
+@@ -502,6 +563,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_RTQM_Q_DBG_BYPASS			BIT(5)
+ #define MTK_WED_RTQM_TXDMAD_FPORT			GENMASK(23, 20)
+ 
++#define MTK_WED_RTQM_RST				0xb04
++
++
+ #define MTK_WED_RTQM_IGRS0_I2HW_DMAD_CNT		0xb1c
+ #define MTK_WED_RTQM_IGRS0_I2H_DMAD_CNT(_n)		(0xb20 + (_n) * 0x4)
+ #define	MTK_WED_RTQM_IGRS0_I2HW_PKT_CNT			0xb28
+@@ -691,6 +755,9 @@ struct mtk_wdma_desc {
+ #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_CLR		BIT(17)
+ #define MTK_WED_WPDMA_INT_CTRL_RRO_PG2_DONE_TRIG	GENMASK(22, 18)
+ 
++#define MTK_WED_RRO_RX_HW_STS				0xf00
++#define MTK_WED_RX_IND_CMD_BUSY			GENMASK(31, 0)
++
+ #define MTK_WED_RX_IND_CMD_CNT0				0xf20
+ #define MTK_WED_RX_IND_CMD_DBG_CNT_EN			BIT(31)
+ 
+diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h
+index 2b389e8..bb02ba5 100644
+--- a/include/linux/soc/mediatek/mtk_wed.h
++++ b/include/linux/soc/mediatek/mtk_wed.h
+@@ -240,7 +240,7 @@ struct mtk_wed_ops {
+ 	void (*irq_set_mask)(struct mtk_wed_device *dev, u32 mask);
+ 	void (*ppe_check)(struct mtk_wed_device *dev, struct sk_buff *skb,
+ 			  u32 reason, u32 hash);
+-	void (*start_hwrro)(struct mtk_wed_device *dev, u32 irq_mask);
++	void (*start_hwrro)(struct mtk_wed_device *dev, u32 irq_mask, bool reset);
+ };
+ 
+ extern const struct mtk_wed_ops __rcu *mtk_soc_wed_ops;
+@@ -317,8 +317,8 @@ mtk_wed_device_support_pao(struct mtk_wed_device *dev)
+ 	(_dev)->ops->reset_dma(_dev)
+ #define mtk_wed_device_ppe_check(_dev, _skb, _reason, _hash) \
+ 	(_dev)->ops->ppe_check(_dev, _skb, _reason, _hash)
+-#define mtk_wed_device_start_hwrro(_dev, _mask) \
+-	(_dev)->ops->start_hwrro(_dev, _mask)
++#define mtk_wed_device_start_hwrro(_dev, _mask, _reset) \
++	(_dev)->ops->start_hwrro(_dev, _mask, _reset)
+ 
+ #else
+ static inline bool mtk_wed_device_active(struct mtk_wed_device *dev)
+-- 
+2.18.0
+
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0001-prefligt-add-cmd-to-speedup-build-image.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0001-prefligt-add-cmd-to-speedup-build-image.patch
new file mode 100755
index 0000000..377aefb
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0001-prefligt-add-cmd-to-speedup-build-image.patch
@@ -0,0 +1,39 @@
+diff --git a/include/kernel-build.mk b/include/kernel-build.mk
+index 9dfb19c..e2d97da 100644
+--- a/include/kernel-build.mk
++++ b/include/kernel-build.mk
+@@ -184,4 +184,10 @@ define BuildKernel
+ 
+   prereq: image-prereq
+ 
++  install-image:
++	@echo Only install image........
++	+$(MAKE) -C image compile install TARGET_BUILD=
++
++  clean-linux: FORCE
++	rm -rf $(LINUX_DIR)
+ endef
+diff --git a/rules.mk b/rules.mk
+index 8b2424f..1787d75 100644
+--- a/rules.mk
++++ b/rules.mk
+@@ -103,7 +103,7 @@ ifdef CONFIG_MIPS64_ABI
+   endif
+ endif
+ 
+-DEFAULT_SUBDIR_TARGETS:=clean download prepare compile update refresh prereq dist distcheck configure check check-depends
++DEFAULT_SUBDIR_TARGETS:=clean download prepare compile update refresh prereq dist distcheck configure check check-depends install-image clean-linux
+ 
+ define DefaultTargets
+ $(foreach t,$(DEFAULT_SUBDIR_TARGETS) $(1),
+diff --git a/target/linux/Makefile b/target/linux/Makefile
+index 3a70b80..e3fe1ca 100644
+--- a/target/linux/Makefile
++++ b/target/linux/Makefile
+@@ -9,5 +9,5 @@ include $(INCLUDE_DIR)/target.mk
+ 
+ export TARGET_BUILD=1
+ 
+-prereq clean download prepare compile install oldconfig menuconfig nconfig xconfig update refresh: FORCE
++prereq clean download prepare compile install oldconfig menuconfig nconfig xconfig update refresh install-image clean-linux: FORCE
+ 	@+$(NO_TRACE_MAKE) -C $(BOARD) $@
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0002-busybox-mtk-defconfig.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0002-busybox-mtk-defconfig.patch
new file mode 100644
index 0000000..66e174c
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0002-busybox-mtk-defconfig.patch
@@ -0,0 +1,83 @@
+diff --git a/package/utils/busybox/Config-defaults.in b/package/utils/busybox/Config-defaults.in
+index 168c73b..377c15f 100644
+--- a/package/utils/busybox/Config-defaults.in
++++ b/package/utils/busybox/Config-defaults.in
+@@ -1606,10 +1606,10 @@ config BUSYBOX_DEFAULT_LOSETUP
+ 	default n
+ config BUSYBOX_DEFAULT_LSPCI
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_LSUSB
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_MDEV
+ 	bool
+ 	default n
+@@ -2519,28 +2519,28 @@ config BUSYBOX_DEFAULT_UDPSVD
+ 	default n
+ config BUSYBOX_DEFAULT_TELNET
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TELNET_TTYPE
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TELNET_AUTOLOGIN
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TELNET_WIDTH
+ 	bool
+ 	default n
+ config BUSYBOX_DEFAULT_TELNETD
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TELNETD_STANDALONE
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TELNETD_INETD_WAIT
+ 	bool
+ 	default n
+ config BUSYBOX_DEFAULT_TFTP
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TFTP_PROGRESS_BAR
+ 	bool
+ 	default n
+@@ -2552,10 +2552,10 @@ config BUSYBOX_DEFAULT_TFTPD
+ 	default n
+ config BUSYBOX_DEFAULT_FEATURE_TFTP_GET
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TFTP_PUT
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_FEATURE_TFTP_BLOCKSIZE
+ 	bool
+ 	default n
+@@ -2585,7 +2585,7 @@ config BUSYBOX_DEFAULT_FEATURE_TUNCTL_UG
+ 	default n
+ config BUSYBOX_DEFAULT_VCONFIG
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_WGET
+ 	bool
+ 	default n
+@@ -2729,7 +2729,7 @@ config BUSYBOX_DEFAULT_LSOF
+ 	default n
+ config BUSYBOX_DEFAULT_MPSTAT
+ 	bool
+-	default n
++	default y
+ config BUSYBOX_DEFAULT_NMETER
+ 	bool
+ 	default n
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0003-fstool-mtk-samba-test.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0003-fstool-mtk-samba-test.patch
new file mode 100644
index 0000000..7e1eba0
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0003-fstool-mtk-samba-test.patch
@@ -0,0 +1,65 @@
+diff -urN a/package/system/fstools/patches/0100-automount.patch b/package/system/fstools/patches/0100-automount.patch
+--- a/package/system/fstools/patches/0100-automount.patch	1970-01-01 08:00:00.000000000 +0800
++++ b/package/system/fstools/patches/0100-automount.patch	2020-07-30 18:16:13.122072146 +0800
+@@ -0,0 +1,22 @@
++Index: fstools-2016-12-04-84b530a7/block.c
++===================================================================
++--- fstools-2016-12-04-84b530a7.orig/block.c	2017-08-17 16:10:43.236274000 +0800
+++++ fstools-2016-12-04-84b530a7/block.c	2017-08-17 16:11:02.423958000 +0800
++@@ -530,7 +530,7 @@
++ 		printf("\toption\tuuid\t'%s'\n", pr->uuid);
++ 	else
++ 		printf("\toption\tdevice\t'%s'\n", pr->dev);
++-	printf("\toption\tenabled\t'0'\n\n");
+++	printf("\toption\tenabled\t'1'\n\n");
++ 
++ 	return 0;
++ }
++@@ -1454,7 +1454,7 @@
++ 	cache_load(0);
++ 	printf("config 'global'\n");
++ 	printf("\toption\tanon_swap\t'0'\n");
++-	printf("\toption\tanon_mount\t'0'\n");
+++	printf("\toption\tanon_mount\t'1'\n");
++ 	printf("\toption\tauto_swap\t'1'\n");
++ 	printf("\toption\tauto_mount\t'1'\n");
++ 	printf("\toption\tdelay_root\t'5'\n");
+diff -urN a/package/system/fstools/patches/0102-mount-options.patch b/package/system/fstools/patches/0102-mount-options.patch
+--- a/package/system/fstools/patches/0102-mount-options.patch	1970-01-01 08:00:00.000000000 +0800
++++ b/package/system/fstools/patches/0102-mount-options.patch	2020-07-30 18:16:13.190070353 +0800
+@@ -0,0 +1,19 @@
++Index: fstools-2016-12-04-84b530a7/block.c
++===================================================================
++--- fstools-2016-12-04-84b530a7.orig/block.c	2017-10-31 18:34:40.867026783 +0800
+++++ fstools-2016-12-04-84b530a7/block.c	2017-10-31 18:39:16.417175783 +0800
++@@ -854,9 +854,13 @@
++ 	int i, err;
++ 	size_t mount_opts_len;
++ 	char *mount_opts = NULL, *ptr;
+++	char _data[128] = {0};
+++	if (strstr(fstype, "fat") || strstr(fstype, "ntfs")) {
+++		snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534");
+++	}
++ 
++ 	err = mount(source, target, fstype, m ? m->flags : 0,
++-	            (m && m->options) ? m->options : "");
+++	            (m && m->options) ? m->options : _data);
++ 
++ 	/* Requested file system type is not available in kernel,
++ 	   attempt to call mount helper. */
+diff -urN a/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch b/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch
+--- a/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch	1970-01-01 08:00:00.000000000 +0800
++++ b/package/system/fstools/patches/0103-mtk-ntfs-mount-by-ufsd.patch	2021-01-26 14:21:31.235330174 +0800
+@@ -0,0 +1,12 @@
++--- a/block.c	2017-11-07 11:13:11.502259230 +0800
+++++ b/block.c	2017-11-07 11:16:43.484684786 +0800
++@@ -859,6 +859,9 @@
++ 		snprintf(_data, sizeof(_data), "%s", "iocharset=utf8,uid=65534,gid=65534");
++ 	}
++ 
+++	if (strstr(fstype, "ntfs"))
+++		fstype= "ufsd";
+++
++ 	err = mount(source, target, fstype, m ? m->flags : 0,
++ 	            (m && m->options) ? m->options : _data);
++ 
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0600-mtd-utils-enable-install-test-load.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0600-mtd-utils-enable-install-test-load.patch
new file mode 100644
index 0000000..2287bf8
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0600-mtd-utils-enable-install-test-load.patch
@@ -0,0 +1,23 @@
+diff --git a/package/utils/mtd-utils/Makefile b/package/utils/mtd-utils/Makefile
+index 5a4b03d..5a9372d 100644
+--- a/package/utils/mtd-utils/Makefile
++++ b/package/utils/mtd-utils/Makefile
+@@ -57,7 +57,8 @@ endef
+ MAKE_FLAGS += LDLIBS+="$(LIBGCC_S)"
+ 
+ CONFIGURE_ARGS += \
+-	--disable-tests \
++	--enable-tests \
++	--enable-install-tests \
+ 	--without-crypto \
+ 	--without-xattr \
+ 	--without-zstd \
+@@ -78,6 +79,8 @@ define Package/nand-utils/install
+ 	$(INSTALL_DIR) $(1)/usr/sbin
+ 	$(INSTALL_BIN) \
+ 	$(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} $(1)/usr/sbin/
++	$(INSTALL_BIN) \
++	$(PKG_INSTALL_DIR)/usr/lib/mtd-utils/{flash_speed,flash_stress,nandbiterrs} $(1)/usr/sbin/
+ endef
+ 
+ $(eval $(call BuildPackage,ubi-utils))
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0900-sbc-create-related-tools-and-scripts.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0900-sbc-create-related-tools-and-scripts.patch
new file mode 100644
index 0000000..332ee64
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0900-sbc-create-related-tools-and-scripts.patch
@@ -0,0 +1,465 @@
+diff --git a/scripts/make-squashfs-hashed.sh b/scripts/make-squashfs-hashed.sh
+new file mode 100755
+index 0000000..a4b183e
+--- /dev/null
++++ b/scripts/make-squashfs-hashed.sh
+@@ -0,0 +1,23 @@
++#!/bin/bash
++#
++# 1. Using veritysetup to append hash image into squashfs
++# 2. Parsing output of veritysetup to generate uboot script
++#
++SQUASHFS_FILE_PATH=$1
++STAGING_DIR_HOST=$2
++TOPDIR=$3
++SUMMARY_FILE=$4
++
++FILE_SIZE=`stat -c "%s" ${SQUASHFS_FILE_PATH}`
++BLOCK_SIZE=4096
++
++DATA_BLOCKS=$((${FILE_SIZE} / ${BLOCK_SIZE}))
++[ $((${FILE_SIZE} % ${BLOCK_SIZE})) -ne 0 ] && DATA_BLOCKS=$((${DATA_BLOCKS} + 1))
++
++HASH_OFFSET=$((${DATA_BLOCKS} * ${BLOCK_SIZE}))
++
++${STAGING_DIR_HOST}/bin/veritysetup format \
++	--data-blocks=${DATA_BLOCKS} \
++	--hash-offset=${HASH_OFFSET} \
++	${SQUASHFS_FILE_PATH} ${SQUASHFS_FILE_PATH} \
++	> ${SUMMARY_FILE}
+diff --git a/scripts/prepare-dm-verity-uboot-script.sh b/scripts/prepare-dm-verity-uboot-script.sh
+new file mode 100755
+index 0000000..a66b921
+--- /dev/null
++++ b/scripts/prepare-dm-verity-uboot-script.sh
+@@ -0,0 +1,54 @@
++#!/bin/bash
++
++ROOT_DEVICE=$1
++EXTRA_ARGS=$2
++
++while read line; do
++	key=$(echo ${line} | cut -f1 -d':')
++	value=$(echo ${line} | cut -f2 -d':')
++
++	case "${key}" in
++	"UUID")
++		UUID=${value}
++		;;
++	"Data blocks")
++		DATA_BLOCKS=${value}
++		;;
++	"Data block size")
++		DATA_BLOCK_SIZE=${value}
++		;;
++	"Hash block size")
++		HASH_BLOCK_SIZE=${value}
++		;;
++	"Hash algorithm")
++		HASH_ALG=${value}
++		;;
++	"Salt")
++		SALT=${value}
++		;;
++	"Root hash")
++		ROOT_HASH=${value}
++		;;
++	esac
++done
++
++#
++# dm-mod.create=<name>,<uuid>,<minor>,<flags>,
++#               <start_sector> <num_sectors> <target_type> <target_args>
++# <target_type>=verity
++# <target_args>=<version> <data_dev> <hash_dev> <data_block_size> <hash_block_size>
++#               <num_data_blocks> <hash_start_block> <algorithm> <root_hash> <salt>
++#
++# <uuid>   ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
++# <minor>  ::= The device minor number | ""
++# <flags>  ::= "ro" | "rw"
++#
++# More detail in field you can ref.
++# Documentation/admin-guide/device-mapper/dm-init.rst
++# Documentation/admin-guide/device-mapper/verity.rst
++#
++
++BOOTARGS=$( printf '%s root=/dev/dm-0 dm-mod.create="dm-verity,,,ro,0 %s verity 1 %s %s %s %s %s %s %s %s %s"' \
++                   "${EXTRA_ARGS}" $((${DATA_BLOCKS} * 8)) ${ROOT_DEVICE} ${ROOT_DEVICE} ${DATA_BLOCK_SIZE} ${HASH_BLOCK_SIZE} ${DATA_BLOCKS} $((${DATA_BLOCKS} + 1)) ${HASH_ALG} ${ROOT_HASH} ${SALT} )
++
++echo setenv bootargs ${BOOTARGS}
+diff --git a/tools/ar-tool/Makefile b/tools/ar-tool/Makefile
+new file mode 100644
+index 0000000..2b22ac0
+--- /dev/null
++++ b/tools/ar-tool/Makefile
+@@ -0,0 +1,36 @@
++#
++# Copyright (C) 2011-2012 OpenWrt.org
++#
++# This is free software, licensed under the GNU General Public License v2.
++# See /LICENSE for more information.
++#
++
++include $(TOPDIR)/rules.mk
++
++PKG_NAME:=ar-tool
++PKG_VERSION:=1
++
++include $(INCLUDE_DIR)/host-build.mk
++
++define Host/Prepare
++	mkdir -p $(HOST_BUILD_DIR)
++	$(CP) ./src/* $(HOST_BUILD_DIR)/
++endef
++
++define Host/Compile
++	$(MAKE) -C $(HOST_BUILD_DIR)
++endef
++
++define Host/Configure
++endef
++
++define Host/Install
++	$(CP) $(HOST_BUILD_DIR)/ar-tool $(STAGING_DIR_HOST)/bin/
++endef
++
++define Host/Clean
++	rm -f $(HOST_BUILD_DIR)/ar-tool
++	rm -f $(STAGING_DIR_HOST)/bin/ar-tool
++endef
++
++$(eval $(call HostBuild))
+diff --git a/tools/ar-tool/src/Makefile b/tools/ar-tool/src/Makefile
+new file mode 100644
+index 0000000..26ab3cf
+--- /dev/null
++++ b/tools/ar-tool/src/Makefile
+@@ -0,0 +1,20 @@
++#
++# Copyright (C) 2019 MediaTek Inc.
++#
++# Author: Sam Shih <sam.shih@mediatek.com>
++#
++# SPDX-License-Identifier:     BSD-3-Clause
++# https://spdx.org/licenses
++#
++
++TARGET := ar-tool
++
++.PHONY: all clean
++
++all: ${TARGET}
++
++%: %.py Makefile
++	cp $< $@
++
++clean:
++	rm ${TARGET}
+diff --git a/tools/ar-tool/src/ar-tool.py b/tools/ar-tool/src/ar-tool.py
+new file mode 100755
+index 0000000..e33510b
+--- /dev/null
++++ b/tools/ar-tool/src/ar-tool.py
+@@ -0,0 +1,302 @@
++#!/usr/bin/python
++import os
++import sys
++from xml.dom import minidom
++import pdb
++import traceback
++import re
++
++
++class bl_ar_table_t:
++
++	def __init__(self, input_file):
++		self.input_file = input_file
++		self.ar_ver_list = []
++
++	def generate_ar_ver_code(self):
++		code = ""
++		code += "/* \n"
++		code +=	" * This file is auto-generated by ar-tool\n"
++		code +=	" * please do not modify this file manually\n"
++		code +=	" */\n"
++		code +=	"#include <plat/common/platform.h>\n"
++		code +=	"const uint32_t bl_ar_ver = %d;\n" % self.ar_ver_list[-1]
++		return code
++
++	def generate_ar_conf_code(self):
++		code = ""
++		code +=	"BL_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1]
++		return code
++
++	def check_and_set_ar_ver_list(self, ar_ver):
++		if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)):
++			self.ar_ver_list.append(ar_ver)
++			return True
++		else:
++			return False
++
++	def get_data_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
++		i = entry_id
++		datalist = xml_node.getElementsByTagName(name)
++		if not datalist:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d]:" % i)
++				print("    Chilld node '%s' not exist" % name)
++			return None
++		data = None
++		if len(datalist) != 1:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d]:" % i)
++				print("    Duplicate '%s' node exist" % name)
++			return None
++		datanode = datalist[0].firstChild
++		if not datanode:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++				print("    '%s' data not exist" % name)
++			return None
++		if datanode.nodeType != datanode.TEXT_NODE:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++				print("    '%s' data not exist" % name)
++			return None
++		return str(datanode.data)
++
++	def get_int_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
++		data = self.get_data_by_name_from_ar_entry(xml_node, entry_id, name, print_err)
++		if data:
++			data = data.strip()
++			if not data.isdigit():
++				if print_err is True:
++					print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++					print("    '%s' must be an integer" % name)
++				return None
++			return data
++		return None
++
++	def xml_debug_show(self, line, column):
++		f = open(self.input_file, "r")
++		if not f:
++			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
++			raise
++		xml_data = f.read()
++		xml_lines = xml_data.split("\n")
++		f.close()
++		print("input xml fail at line %d, column %d" % (line, column))
++		if line < 2:
++			show_lines = [xml_lines[line]]
++		elif line+2 >= len(xml_lines):
++			show_lines = [xml_lines[line]]
++		else:
++			show_lines = xml_lines[line-1:line+1]
++		for line in show_lines:
++			print(line)
++
++	def parse(self):
++		data = None
++		try:
++			f = open(self.input_file, "r")
++			if not f:
++				raise
++			f.close()
++		except:
++			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
++			return 1
++		try:
++			xmldoc = minidom.parse(self.input_file)
++			ar_entry_list = xmldoc.getElementsByTagName('bl_ar_entry')
++
++			for i in range(0, len(ar_entry_list)):
++				ar_entry = ar_entry_list[i]
++				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False)
++				if not data:
++					continue
++
++				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "BL_AR_VER")
++				if not data:
++					return 1
++				if data:
++					data = data.strip()
++					if self.check_and_set_ar_ver_list(int(data)) is False:
++						print("XML parse fail in bl_ar_entry[%d].BL_AR_VER:" % i)
++						print("    'BL_AR_VER' value duplicate or exceed range")
++						return 1
++			print("Get %d record in bl_ar_table" % len(self.ar_ver_list))
++		except:
++			sys.stderr.write("Unable to parse file '%s'\n" % self.input_file)
++			crash_info = traceback.format_exc()
++			m = re.search("ExpatError: mismatched tag: line (.+), column (.+)", crash_info)
++			if m:
++				line = int(m.group(1))
++				column = int(m.group(2))
++				self.xml_debug_show(line, column)
++				print(m.group(0))
++			else:
++				print(crash_info)
++			return 1
++		return 0
++
++
++class fw_ar_table_t:
++
++	def __init__(self, input_file):
++		self.input_file = input_file
++		self.ar_ver_list = []
++
++	def generate_ar_ver_code(self):
++		code = ""
++		code += "/* \n"
++		code +=	" * This file is auto-generated by ar-tool\n"
++		code +=	" * please do not modify this file manually\n"
++		code +=	" */\n"
++		code +=	"const uint32_t fw_ar_ver = %d;\n" % self.ar_ver_list[-1]
++		return code
++
++	def generate_ar_conf_code(self):
++		code = ""
++		code +=	"FW_AR_VER\t:=\t%d\n" % self.ar_ver_list[-1]
++		return code
++
++	def check_and_set_ar_ver_list(self, ar_ver):
++		if ((ar_ver not in self.ar_ver_list) and (ar_ver <= 64) and (ar_ver >= 0)):
++			self.ar_ver_list.append(ar_ver)
++			return True
++		else:
++			return False
++
++	def get_data_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
++		i = entry_id
++		datalist = xml_node.getElementsByTagName(name)
++		if not datalist:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d]:" % i)
++				print("    Chilld node '%s' not exist" % name)
++			return None
++		data = None
++		if len(datalist) != 1:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d]:" % i)
++				print("    Duplicate '%s' node exist" % name)
++			return None
++		datanode = datalist[0].firstChild
++		if not datanode:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++				print("    '%s' data not exist" % name)
++			return None
++		if datanode.nodeType != datanode.TEXT_NODE:
++			if print_err is True:
++				print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++				print("    '%s' data not exist" % name)
++			return None
++		return str(datanode.data)
++
++	def get_int_by_name_from_ar_entry(self, xml_node, entry_id, name, print_err=True):
++		data = self.get_data_by_name_from_ar_entry(xml_node, entry_id, name, print_err)
++		if data:
++			data = data.strip()
++			if not data.isdigit():
++				if print_err is True:
++					print("XML parse fail in ar_entry[%d].%s:" % (i, name))
++					print("    '%s' must be an integer" % name)
++				return None
++			return data
++		return None
++
++	def xml_debug_show(self, line, column):
++		f = open(self.input_file, "r")
++		if not f:
++			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
++			raise
++		xml_data = f.read()
++		xml_lines = xml_data.split("\n")
++		f.close()
++		print("input xml fail at line %d, column %d" % (line, column))
++		if line < 2:
++			show_lines = [xml_lines[line]]
++		elif line+2 >= len(xml_lines):
++			show_lines = [xml_lines[line]]
++		else:
++			show_lines = xml_lines[line-1:line+1]
++		for line in show_lines:
++			print(line)
++
++	def parse(self):
++		data = None
++		try:
++			f = open(self.input_file, "r")
++			if not f:
++				raise
++			f.close()
++		except:
++			sys.stderr.write("Unable to open file '%s'\n" % self.input_file)
++			return 1
++		try:
++			xmldoc = minidom.parse(self.input_file)
++			ar_entry_list = xmldoc.getElementsByTagName('fw_ar_entry')
++
++			for i in range(0, len(ar_entry_list)):
++				ar_entry = ar_entry_list[i]
++				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "USED", False)
++				if not data:
++					continue
++
++				data = self.get_int_by_name_from_ar_entry(ar_entry, i, "FW_AR_VER")
++				if not data:
++					return 1
++				if data:
++					data = data.strip()
++					if self.check_and_set_ar_ver_list(int(data)) is False:
++						print("XML parse fail in fw_ar_entry[%d].FW_AR_VER:" % i)
++						print("    'FW_AR_VER' value duplicate or exceed range")
++						return 1
++			print("Get %d record in fw_ar_table" % len(self.ar_ver_list))
++		except:
++			sys.stderr.write("Unable to parse file '%s'\n" % self.input_file)
++			crash_info = traceback.format_exc()
++			m = re.search("ExpatError: mismatched tag: line (.+), column (.+)", crash_info)
++			if m:
++				line = int(m.group(1))
++				column = int(m.group(2))
++				self.xml_debug_show(line, column)
++				print(m.group(0))
++			else:
++				print(crash_info)
++			return 1
++		return 0
++
++
++def main(argc, argv):
++	if argc != 5:
++		sys.stdout.write("ar-tool [bl_ar_table|fw_ar_table] [create_ar_ver|create_ar_conf] $(input_file) $(output_file)\n")
++		return 1
++	if argv[1] == "bl_ar_table":
++		ar_table = bl_ar_table_t(argv[3])
++	else:
++		ar_table = fw_ar_table_t(argv[3])
++	if ar_table.parse() != 0:
++		return 1
++	if argv[2] == "create_ar_ver":
++		code = ar_table.generate_ar_ver_code()
++		print("(%s) --> (%s)" % (argv[3], argv[4]))
++		#print(code)
++		f = open(argv[4], "w")
++		f.write(code)
++		f.close()
++		return 0
++	elif argv[2] == "create_ar_conf":
++		code = ar_table.generate_ar_conf_code()
++		print("(%s) --> (%s)" % (argv[3], argv[4]))
++		#print(code)
++		f = open(argv[4], "w")
++		f.write(code)
++		f.close()
++		return 0
++	else:
++		print("Unknow option '%s'" % argv[1])
++		return 1
++
++
++if __name__ == '__main__':
++    sys.exit(main(len(sys.argv), sys.argv))
++
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-ar-tool.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-ar-tool.patch
new file mode 100644
index 0000000..2541654
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-ar-tool.patch
@@ -0,0 +1,10 @@
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -21,6 +21,7 @@ ifneq ($(CONFIG_SDK)$(CONFIG_PACKAGE_kmo
+   BUILD_B43_TOOLS = y
+ endif
+ 
++tools-y += ar-tool
+ tools-y += autoconf autoconf-archive automake bc bison cmake dosfstools
+ tools-y += e2fsprogs fakeroot findutils firmware-utils flex gengetopt
+ tools-y += libressl libtool lzma m4 make-ext4fs missing-macros mkimage
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-cryptsetup.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-cryptsetup.patch
new file mode 100644
index 0000000..8d69a8a
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0901-sbc-build-host-tool-cryptsetup.patch
@@ -0,0 +1,382 @@
+diff --git a/feeds/packages/libs/libaio/Makefile b/feeds/packages/libs/libaio/Makefile
+index 463b965..a0a2719 100644
+--- a/feeds/packages/libs/libaio/Makefile
++++ b/feeds/packages/libs/libaio/Makefile
+@@ -23,7 +23,10 @@ PKG_BUILD_PARALLEL:=1
+ PKG_USE_MIPS16:=0
+ PKG_INSTALL:=1
+ 
++HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
++
+ include $(INCLUDE_DIR)/package.mk
++include $(INCLUDE_DIR)/host-build.mk
+ 
+ define Package/libaio
+   SECTION:=libs
+@@ -44,4 +47,13 @@ define Package/libaio/install
+ 	$(CP) $(PKG_INSTALL_DIR)/usr/lib/libaio.so.* $(1)/usr/lib/
+ endef
+ 
++define Host/Compile
++	$(MAKE) -C $(HOST_BUILD_DIR)
++endef
++
++define Host/Install
++	$(MAKE) -C $(HOST_BUILD_DIR) prefix=$(HOST_BUILD_PREFIX) install
++endef
++
+ $(eval $(call BuildPackage,libaio))
++$(eval $(call HostBuild))
+diff --git a/feeds/packages/utils/cryptsetup/Makefile b/feeds/packages/utils/cryptsetup/Makefile
+index bdb249f..8d98957 100644
+--- a/feeds/packages/utils/cryptsetup/Makefile
++++ b/feeds/packages/utils/cryptsetup/Makefile
+@@ -8,12 +8,12 @@
+ include $(TOPDIR)/rules.mk
+ 
+ PKG_NAME:=cryptsetup
+-PKG_VERSION:=2.5.0
+-PKG_RELEASE:=$(AUTORELEASE)
++PKG_VERSION:=2.3.4
++PKG_RELEASE:=2
+ 
+ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
+ PKG_SOURCE_URL:=@KERNEL/linux/utils/cryptsetup/v2.5
+-PKG_HASH:=9184a6ebbd9ce7eb211152e7f741a6c82f2d1cc0e24a84ec9c52939eee0f0542
++PKG_HASH:=9d16eebb96b53b514778e813019b8dd15fea9fec5aafde9fae5febf59df83773
+ 
+ PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
+ PKG_LICENSE:=GPL-2.0-or-later LGPL-2.1-or-later
+@@ -24,8 +24,12 @@ PKG_BUILD_PARALLEL:=1
+ 
+ PKG_BUILD_DEPENDS:=!USE_GLIBC:argp-standalone
+ 
++HOST_BUILD_DEPENDS:=lvm2/host libjson-c/host popt/host
++HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
++
+ include $(INCLUDE_DIR)/package.mk
+ include $(INCLUDE_DIR)/nls.mk
++include $(INCLUDE_DIR)/host-build.mk
+ 
+ define Package/cryptsetup
+   SECTION:=utils
+@@ -75,6 +79,13 @@ CONFIGURE_VARS += \
+ 
+ TARGET_LDFLAGS += -Wl,--gc-sections $(if $(INTL_FULL),-lintl)
+ 
++HOST_CONFIGURE_ARGS += \
++	--with-crypto-backend=openssl \
++	$(STAGING_DIR_HOST) \
++	--disable-kernel_crypto \
++	--disable-blkid \
++	--enable-libiconv-tiny
++
+ define Build/InstallDev
+ 	$(INSTALL_DIR) $(1)/usr/include
+ 	$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/include/libcryptsetup.h $(1)/usr/include
+@@ -98,5 +109,11 @@ define Package/cryptsetup-ssh/install
+ 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/cryptsetup-ssh $(1)/usr/sbin
+ endef
+ 
++define Host/Install
++	$(INSTALL_BIN) $(HOST_BUILD_DIR)/veritysetup $(STAGING_DIR_HOST)/bin/veritysetup
++	$(CP) $(HOST_BUILD_DIR)/.libs $(STAGING_DIR_HOST)/bin/
++endef
++
+ $(eval $(call BuildPackage,cryptsetup))
+ $(eval $(call BuildPackage,cryptsetup-ssh))
++$(eval $(call HostBuild))
+diff --git a/feeds/packages/utils/cryptsetup/patches/0001-dont-use-c89.patch b/feeds/packages/utils/cryptsetup/patches/0001-dont-use-c89.patch
+new file mode 100644
+index 0000000000..c5d4ee90d7
+--- /dev/null
++++ b/feeds/packages/utils/cryptsetup/patches/0001-dont-use-c89.patch
+@@ -0,0 +1,10 @@
++--- a/lib/crypto_backend/argon2/Makemodule.am
+++++ b/lib/crypto_backend/argon2/Makemodule.am
++@@ -1,6 +1,6 @@
++ noinst_LTLIBRARIES += libargon2.la
++
++-libargon2_la_CFLAGS = $(AM_CFLAGS) -std=c89 -pthread -O3
+++libargon2_la_CFLAGS = $(AM_CFLAGS) -pthread -O3
++ libargon2_la_CPPFLAGS = $(AM_CPPFLAGS) \
++ 	-I lib/crypto_backend/argon2 \
++ 	-I lib/crypto_backend/argon2/blake2
+diff --git a/feeds/packages/utils/cryptsetup/patches/0100-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch b/feeds/packages/utils/cryptsetup/patches/0100-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch
+new file mode 100644
+index 0000000..d418a91
+--- /dev/null
++++ b/feeds/packages/utils/cryptsetup/patches/0100-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch
+@@ -0,0 +1,33 @@
++--- a/configure
+++++ b/configure
++@@ -856,6 +856,7 @@ enable_rpath
++ with_libiconv_prefix
++ enable_keyring
++ enable_largefile
+++enable_libiconv_tiny
++ enable_nls
++ with_libintl_prefix
++ enable_fips
++@@ -1583,6 +1584,7 @@ Optional Features:
++   --disable-keyring       disable kernel keyring support and builtin kernel
++                           keyring token
++   --disable-largefile     omit support for large files
+++  --enable-libiconv-tiny  build with libiconv-tiny from OpenWrt
++   --disable-nls           do not use Native Language Support
++   --enable-fips           enable FIPS mode restrictions
++   --enable-pwquality      enable password quality checking using pwquality
++@@ -15038,6 +15040,14 @@ if test "x$enable_largefile" = "xno"; th
++   as_fn_error $? "Building with --disable-largefile is not supported, it can cause data corruption." "$LINENO" 5
++ fi
++ 
+++# Check whether --enable-libiconv-tiny was given.
+++if test "${enable_libiconv_tiny+set}" = set; then
+++  enableval=$enable_libiconv_tiny;
+++  if test ".$enableval" = .yes; then
+++    LIBS="$LIBS -liconv"
+++  fi
+++fi
+++
++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
++ $as_echo_n "checking for an ANSI C-conforming const... " >&6; }
++ if ${ac_cv_c_const+:} false; then :
+diff --git a/feeds/packages/utils/lvm2/Makefile b/feeds/packages/utils/lvm2/Makefile
+index af35899..24b485a 100644
+--- a/feeds/packages/utils/lvm2/Makefile
++++ b/feeds/packages/utils/lvm2/Makefile
+@@ -24,7 +24,11 @@ PKG_CPE_ID:=cpe:/a:heinz_mauelshagen:lvm
+ 
+ PKG_BUILD_PARALLEL:=1
+ 
++HOST_BUILD_DEPENDS:=libaio/host
++HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
++
+ include $(INCLUDE_DIR)/package.mk
++include $(INCLUDE_DIR)/host-build.mk
+ 
+ define Package/libdevmapper/Default
+   SECTION:=libs
+@@ -58,6 +62,15 @@ $(call Package/libdevmapper/description)
+ 
+ endef
+ 
++define Package/dmsetup
++  SECTION:=utils
++  CATEGORY:=Utilities
++  SUBMENU:=Disc
++  TITLE:=The Linux Kernel Device Mapper userspace setup utility
++  URL:=https://sourceware.org/dm/
++  DEPENDS:=+libdevmapper
++endef
++
+ define Package/lvm2/default
+   SECTION:=utils
+   CATEGORY:=Utilities
+@@ -70,7 +83,7 @@ endef
+ define Package/lvm2
+   $(call Package/lvm2/default)
+   VARIANT := normal
+-  DEPENDS += +libdevmapper
++  DEPENDS += +libdevmapper +dmsetup
+ endef
+ 
+ define Package/lvm2-selinux
+@@ -105,6 +118,16 @@ ifneq ($(shell /bin/sh -c "echo -n 'X'")
+ MAKE_SHELL = SHELL=/bin/bash
+ endif
+ 
++HOST_CONFIGURE_ARGS += \
++	--enable-write_install \
++	--enable-pkgconfig \
++	--disable-cmdlib \
++	--disable-dmeventd \
++	--disable-applib \
++	--disable-fsadm \
++	--disable-readline \
++	--disable-selinux
++
+ define Build/Compile
+ 	$(MAKE) -C $(PKG_BUILD_DIR) \
+ 		CC="$(TARGET_CC)" \
+@@ -130,10 +153,15 @@ endef
+ 
+ Package/libdevmapper-selinux/install = $(Package/libdevmapper/install)
+ 
++define Package/dmsetup/install
++	$(INSTALL_DIR) $(1)/sbin
++	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/dmsetup $(1)/sbin
++	ln -sf dmsetup $(1)/sbin/dmstats
++endef
++
+ define Package/lvm2/install
+ 	$(INSTALL_DIR) $(1)/sbin
+ 	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/lvm $(1)/sbin
+-	$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/dmsetup $(1)/sbin
+ 	$(INSTALL_DIR) $(1)/etc/lvm
+ 	$(SED) '/^[[:space:]]*\(#\|$$$$\)/d; /cache_dir/s@.*@	cache_dir = "/tmp/lvm/cache"@' $(PKG_INSTALL_DIR)/etc/lvm/lvm.conf
+ 	$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/lvm/lvm.conf $(1)/etc/lvm/
+@@ -146,7 +174,7 @@ define Package/lvm2/install
+ 	$(INSTALL_BIN) ./files/lvm2.init $(1)/etc/init.d/lvm2
+ 	$(INSTALL_DIR) $(1)/etc/hotplug.d/block
+ 	$(INSTALL_DATA) ./files/lvm2.hotplug $(1)/etc/hotplug.d/block/20-lvm2
+-	$(FIND) $(PKG_INSTALL_DIR)/usr/sbin/ -type l -exec $(CP) -a {} $(1)/sbin/ \;
++	$(FIND) $(PKG_INSTALL_DIR)/usr/sbin/ -type l ! -name dmstats -exec $(CP) -a {} $(1)/sbin/ \;
+ endef
+ 
+ Package/lvm2-selinux/install = $(Package/lvm2/install)
+@@ -155,9 +183,30 @@ define Package/lvm2/conffiles
+ /etc/lvm/lvm.conf
+ endef
+ 
++define Host/Prepare
++	$(HOST_UNPACK)
++	[ ! -d ./src/ ] || $(CP) ./src/* $(HOST_BUILD_DIR)
++
++	mv $(HOST_BUILD_DIR)/../$(PKG_NAME).$(PKG_VERSION)/* $(HOST_BUILD_DIR)/
++	rmdir $(HOST_BUILD_DIR)/../$(PKG_NAME).$(PKG_VERSION)
++
++	$(Host/Patch)
++endef
++
++define Host/Compile
++	$(call Host/Compile/Default,device-mapper)
++endef
++
++define Host/Install
++	$(call Host/Compile/Default,install_device-mapper)
++endef
++
++
+ Package/lvm2-selinux/conffiles = $(Package/lvm2/conffiles)
+ 
+ $(eval $(call BuildPackage,libdevmapper))
+ $(eval $(call BuildPackage,libdevmapper-selinux))
++$(eval $(call BuildPackage,dmsetup))
+ $(eval $(call BuildPackage,lvm2))
+ $(eval $(call BuildPackage,lvm2-selinux))
++$(eval $(call HostBuild))
+diff -Nurp a/feeds/packages/utils/lvm2/patches/100-change-linker-search-dir-order.patch b/feeds/packages/utils/lvm2/patches/100-change-linker-search-dir-order.patch
+--- a/feeds/packages/utils/lvm2/patches/100-change-linker-search-dir-order.patch
++++ b/feeds/packages/utils/lvm2/patches/100-change-linker-search-dir-order.patch
+@@ -0,0 +1,39 @@
++--- a/libdm/dm-tools/Makefile.in
+++++ b/libdm/dm-tools/Makefile.in
++@@ -52,18 +52,18 @@ include $(top_builddir)/libdm/make.tmpl
++ 
++ CFLAGS_dmsetup.o += $(UDEV_CFLAGS) $(EXTRA_EXEC_CFLAGS)
++ CFLAGS_dmfilemapd.o += $(EXTRA_EXEC_CFLAGS)
++-LIBDM_LIBS = -L$(interfacebuilddir) -ldevmapper
+++LIBDM_LIBS = -ldevmapper
++ LIBDM_SHARED = $(interfacebuilddir)/libdevmapper.so
++ LIBDM_STATIC = $(interfacebuilddir)/libdevmapper.a
++ 
++ dmsetup: dmsetup.o $(LIBDM_SHARED)
++ 	@echo "    [CC] $@"
++-	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
+++	$(Q) $(CC) $(CFLAGS) -L$(interfacebuilddir) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
++ 	      -o $@ $< $(LIBDM_LIBS) $(LIBS)
++ 
++ dmsetup.static: dmsetup.o $(LIBDM_STATIC)
++ 	@echo "    [CC] $@"
++-	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static \
+++	$(Q) $(CC) $(CFLAGS) -L$(interfacebuilddir) $(LDFLAGS) -static \
++ 	      -o $@ $< $(LIBDM_LIBS) $(LIBS) $(STATIC_LIBS)
++ 
++ install_dmsetup_dynamic: dmsetup
++@@ -79,12 +79,12 @@ install_dmsetup_static: dmsetup.static
++ 
++ dmfilemapd: dmfilemapd.o $(LIBDM_SHARED)
++ 	@echo "    [CC] $@"
++-	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
+++	$(Q) $(CC) $(CFLAGS) -L$(interfacebuilddir) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) $(ELDFLAGS) \
++ 	      -o $@ $< $(LIBDM_LIBS) $(LIBS)
++ 
++ dmfilemapd.static: dmfilemapd.o $(LIBDM_STATIC)
++ 	@echo "    [CC] $@"
++-	$(Q) $(CC) $(CFLAGS) $(LDFLAGS) -static \
+++	$(Q) $(CC) $(CFLAGS) -L$(interfacebuilddir) $(LDFLAGS) -static \
++ 	      -o $@ $< $(LIBDM_LIBS) $(LIBS) $(STATIC_LIBS)
++ 
++ install_dmfilemapd_dynamic: dmfilemapd
+diff --git a/package/Makefile b/package/Makefile
+index 58c1ba2..a66851b 100644
+--- a/package/Makefile
++++ b/package/Makefile
+@@ -60,6 +60,7 @@ $(curdir)/merge-index: $(curdir)/merge
+ ifndef SDK
+   $(curdir)/compile: $(curdir)/system/opkg/host/compile
+ endif
++$(curdir)/compile: $(curdir)/cryptsetup/host/compile
+
+ $(curdir)/install: $(TMP_DIR)/.build $(curdir)/merge $(if $(CONFIG_TARGET_PER_DEVICE_ROOTFS),$(curdir)/merge-index)
+        - find $(STAGING_DIR_ROOT) -type d | $(XARGS) chmod 0755
+diff --git a/package/libs/popt/Makefile b/package/libs/popt/Makefile
+index 34ae4d7..2075542 100644
+--- a/package/libs/popt/Makefile
++++ b/package/libs/popt/Makefile
+@@ -19,15 +19,17 @@ PKG_SOURCE_URL:= \
+ PKG_HASH:=e728ed296fe9f069a0e005003c3d6b2dde3d9cad453422a10d6558616d304cc8
+ PKG_LICENSE:=MIT
+
+-PKG_FIXUP:=autoreconf
+-PKG_REMOVE_FILES:=autogen.sh aclocal.m4
+-
+ PKG_INSTALL:=1
+ PKG_BUILD_PARALLEL:=1
+
++HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
++
+ include $(INCLUDE_DIR)/package.mk
++include $(INCLUDE_DIR)/host-build.mk
+
+ TARGET_CFLAGS += $(FPIC)
++HOST_CONFIGURE_ARGS += --enable-libiconv-tiny
++HOST_BUILD_DEPENDS := libiconv/host
+
+ define Package/libpopt
+   SECTION:=libs
+@@ -54,4 +56,4 @@ define Package/libpopt/install
+ endef
+
+ $(eval $(call BuildPackage,libpopt))
+-
++$(eval $(call HostBuild))
+diff -urN a/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch b/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch
+--- a/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch	1970-01-01 08:00:00.000000000 +0800
++++ b/package/libs/popt/patches/0001-add-configure-arg-to-link-with-tiny-libiconv-in-host-build.patch	2021-01-06 13:46:47.514721593 +0800
+@@ -0,0 +1,34 @@
++--- a/configure
+++++ b/configure
++@@ -945,6 +945,7 @@ enable_libtool_lock
++ enable_largefile
++ enable_ld_version_script
++ enable_build_gcov
+++enable_libiconv_tiny
++ enable_nls
++ enable_rpath
++ with_libiconv_prefix
++@@ -1604,6 +1605,7 @@ Optional Features:
++                           enable/disable use of linker version script.
++                           (default is system dependent)
++   --enable-build-gcov     build POPT instrumented for gcov
+++  --enable-libiconv-tiny  build with libiconv-tiny from OpenWrt
++   --disable-nls           do not use Native Language Support
++   --disable-rpath         do not hardcode runtime library paths
++ 
++@@ -13334,6 +13336,15 @@ if test "${enable_build_gcov+set}" = set
++ fi
++ 
++ 
+++# Check whether --enable-libiconv-tiny was given.
+++if test "${enable_libiconv_tiny+set}" = set; then
+++  enableval=$enable_libiconv_tiny;
+++  if test ".$enableval" = .yes; then
+++    LIBS="$LIBS -liconv"
+++  fi
+++fi
+++
+++
++ { $as_echo "$as_me:$LINENO: checking for setreuid" >&5
++ $as_echo_n "checking for setreuid... " >&6; }
++ if test "${ac_cv_func_setreuid+set}" = set; then
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0902-sbc-secure-boot-and-anti-rollback-support.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0902-sbc-secure-boot-and-anti-rollback-support.patch
new file mode 100644
index 0000000..d4110ac
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0902-sbc-secure-boot-and-anti-rollback-support.patch
@@ -0,0 +1,156 @@
+--- a/include/image.mk
++++ b/include/image.mk
+@@ -227,8 +227,7 @@ $(eval $(foreach S,$(NAND_BLOCKSIZE),$(c
+ define Image/mkfs/squashfs-common
+ 	$(STAGING_DIR_HOST)/bin/mksquashfs4 $(call mkfs_target_dir,$(1)) $@ \
+ 		-nopad -noappend -root-owned \
+-		-comp $(SQUASHFSCOMP) $(SQUASHFSOPT) \
+-		-processors 1
++		-comp $(SQUASHFSCOMP) $(SQUASHFSOPT)
+ endef
+ 
+ ifeq ($(CONFIG_TARGET_ROOTFS_SECURITY_LABELS),y)
+@@ -441,6 +440,9 @@ else
+   DEVICE_CHECK_PROFILE = $(CONFIG_TARGET_$(if $(CONFIG_TARGET_MULTI_PROFILE),DEVICE_)$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_$(1))
+ endif
+ 
++DEVICE_CHECK_FIT_KEY = $(if $(wildcard $(FIT_KEY_DIR)/$(FIT_KEY_NAME).key),install-images,install-disabled)
++DEVICE_CHECK_FIT_DIR = $(if $(FIT_KEY_DIR),$(DEVICE_CHECK_FIT_KEY),install-images)
++
+ DEVICE_EXTRA_PACKAGES = $(call qstrip,$(CONFIG_TARGET_DEVICE_PACKAGES_$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_DEVICE_$(1)))
+ 
+ define merge_packages
+@@ -463,7 +465,7 @@ endef
+ define Device/Check
+   $(Device/Check/Common)
+   KDIR_KERNEL_IMAGE := $(KDIR)/$(1)$$(KERNEL_SUFFIX)
+-  _TARGET := $$(if $$(_PROFILE_SET),install-images,install-disabled)
++  _TARGET := $$(if $$(_PROFILE_SET),$$(DEVICE_CHECK_FIT_DIR),install-disabled)
+   ifndef IB
+     _COMPILE_TARGET := $$(if $(CONFIG_IB)$$(_PROFILE_SET),compile,compile-disabled)
+   endif
+@@ -527,6 +527,21 @@ define Device/Build/compile
+ 
+ endef
+ 
++define Device/Build/per-device-fs
++  ROOTFS/$(1)/$(3) := \
++	$(KDIR)/root.$(1)$$(strip \
++		$$(if $$(FS_OPTIONS/$(1)),+fs=$$(call param_mangle,$$(FS_OPTIONS/$(1)))) \
++	)$$(strip \
++		$(if $(TARGET_PER_DEVICE_ROOTFS),+pkg=$$(ROOTFS_ID/$(3))) \
++	)
++  ifndef IB
++    $$(ROOTFS/$(1)/$(3)): $(if $(TARGET_PER_DEVICE_ROOTFS),target-dir-$$(ROOTFS_ID/$(3)))
++  endif
++
++  $$(KDIR_KERNEL_IMAGE): $$(ROOTFS/$(1)/$(3))
++
++endef
++
+ ifndef IB
+ define Device/Build/dtb
+   ifndef BUILD_DTS_$(1)
+@@ -571,15 +586,6 @@ define Device/Build/image
+ 	  $(BIN_DIR)/$(call IMAGE_NAME,$(1),$(2))$$(GZ_SUFFIX))
+   $(eval $(call Device/Export,$(KDIR)/tmp/$(call IMAGE_NAME,$(1),$(2)),$(1)))
+ 
+-  ROOTFS/$(1)/$(3) := \
+-	$(KDIR)/root.$(1)$$(strip \
+-		$$(if $$(FS_OPTIONS/$(1)),+fs=$$(call param_mangle,$$(FS_OPTIONS/$(1)))) \
+-	)$$(strip \
+-		$(if $(TARGET_PER_DEVICE_ROOTFS),+pkg=$$(ROOTFS_ID/$(3))) \
+-	)
+-  ifndef IB
+-    $$(ROOTFS/$(1)/$(3)): $(if $(TARGET_PER_DEVICE_ROOTFS),target-dir-$$(ROOTFS_ID/$(3)))
+-  endif
+   $(KDIR)/tmp/$(call IMAGE_NAME,$(1),$(2)): $$(KDIR_KERNEL_IMAGE) $$(ROOTFS/$(1)/$(3))
+ 	@rm -f $$@
+ 	[ -f $$(word 1,$$^) -a -f $$(word 2,$$^) ]
+@@ -640,6 +646,10 @@ define Device/Build/artifact
+ endef
+ 
+ define Device/Build
++  $$(eval $$(foreach image,$$(IMAGES), \
++    $$(foreach fs,$$(filter $(TARGET_FILESYSTEMS),$$(FILESYSTEMS)), \
++      $$(call Device/Build/per-device-fs,$$(fs),$$(image),$(1)))))
++
+   $(if $(CONFIG_TARGET_ROOTFS_INITRAMFS),$(call Device/Build/initramfs,$(1)))
+   $(call Device/Build/kernel,$(1))
+ 
+--- a/include/image-commands.mk
++++ b/include/image-commands.mk
+@@ -87,7 +87,7 @@ define Build/append-ubi
+ 		$(if $(UBOOTENV_IN_UBI),--uboot-env) \
+ 		$(if $(KERNEL_IN_UBI),--kernel $(IMAGE_KERNEL)) \
+ 		$(foreach part,$(UBINIZE_PARTS),--part $(part)) \
+-		$(IMAGE_ROOTFS) \
++		$(call param_get_default,rootfs,$(1),$(IMAGE_ROOTFS)) \
+ 		$@.tmp \
+ 		-p $(BLOCKSIZE:%k=%KiB) -m $(PAGESIZE) \
+ 		$(if $(SUBPAGESIZE),-s $(SUBPAGESIZE)) \
+--- a/target/linux/mediatek/image/Makefile
++++ b/target/linux/mediatek/image/Makefile
+@@ -16,6 +16,53 @@ define Build/sysupgrade-emmc
+ 		$(IMAGE_ROOTFS)
+ endef
+ 
++# build squashfs-hashed
++define Build/squashfs-hashed
++	$(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed
++	$(TOPDIR)/scripts/make-squashfs-hashed.sh \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed \
++		$(STAGING_DIR_HOST) \
++		$(TOPDIR) \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary
++	fdt-patch-dm-verify $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary \
++		$(KDIR)/image-$(firstword $(DEVICE_DTS)).dtb $(KDIR)/image-sb-$(firstword $(DEVICE_DTS)).dtb \
++		$(HASHED_BOOT_DEVICE)
++endef
++
++# build fw-ar-ver
++get_fw_ar_ver = \
++  $(if $(wildcard $(2)),$(shell rm -rf $(2))) \
++  $(if $(wildcard $(1)),$(info $(shell $(STAGING_DIR_HOST)/bin/ar-tool fw_ar_table create_ar_conf $(1) $(2)))) \
++  $(if $(wildcard $(2)),$(eval include $(2))) \
++  $(if $(FW_AR_VER),$(info FW_AR_VER = $(FW_AR_VER)))
++
++define Build/fw-ar-ver
++	$(call get_fw_ar_ver,$(ANTI_ROLLBACK_TABLE),$(AUTO_AR_CONF))
++endef
++
++# build signed fit
++define Build/fit-sign
++	$(TOPDIR)/scripts/mkits.sh \
++		-D $(DEVICE_NAME) \
++		-o $@.its \
++		-k $@ \
++		$(if $(word 2,$(1)),-d $(word 2,$(1))) -C $(word 1,$(1)) \
++		-a $(KERNEL_LOADADDR) \
++		-e $(if $(KERNEL_ENTRY),$(KERNEL_ENTRY),$(KERNEL_LOADADDR)) \
++		-c $(if $(DEVICE_DTS_CONFIG),$(DEVICE_DTS_CONFIG),"config-1") \
++		-A $(LINUX_KARCH) \
++		-v $(LINUX_VERSION) \
++		$(if $(FIT_KEY_NAME),-S $(FIT_KEY_NAME)) \
++		$(if $(FW_AR_VER),-r $(FW_AR_VER)) \
++		$(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),-R $(ROOTFS/squashfs/$(DEVICE_NAME)))
++	PATH=$(LINUX_DIR)/scripts/dtc:$(PATH) mkimage \
++		-f $@.its \
++		$(if $(FIT_KEY_DIR),-k $(FIT_KEY_DIR)) \
++		-r \
++		$@.new
++	@mv $@.new $@
++endef
++
+ # default all platform image(fit) build 
+ define Device/Default
+   PROFILES = Default $$(DEVICE_NAME)
+@@ -29,6 +79,8 @@ define Device/Default
+   IMAGES := sysupgrade.bin
+   IMAGE/sysupgrade.bin := append-kernel | pad-to 128k | append-rootfs | \
+ 	pad-rootfs | append-metadata
++  FIT_KEY_DIR :=
++  FIT_KEY_NAME :=
+ endef
+ 
+ include $(SUBTARGET).mk
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0903-sbc-make-dm-mod-dax-built-in.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0903-sbc-make-dm-mod-dax-built-in.patch
new file mode 100644
index 0000000..4bf7bc9
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0903-sbc-make-dm-mod-dax-built-in.patch
@@ -0,0 +1,31 @@
+--- a/package/kernel/linux/modules/block.mk
++++ b/package/kernel/linux/modules/block.mk
+@@ -208,8 +208,7 @@ $(eval $(call KernelPackage,block2mtd))
+ define KernelPackage/dax
+   SUBMENU:=$(BLOCK_MENU)
+   TITLE:=DAX: direct access to differentiated memory
+-  KCONFIG:=CONFIG_DAX
+-  FILES:=$(LINUX_DIR)/drivers/dax/dax.ko
++  KCONFIG:=CONFIG_DAX=y
+ endef
+ 
+ $(eval $(call KernelPackage,dax))
+@@ -234,16 +233,15 @@ define KernelPackage/dm
+ 	CONFIG_DM_SNAPSHOT=n \
+ 	CONFIG_DM_LOG_USERSPACE=n \
+ 	CONFIG_MD=y \
+-	CONFIG_BLK_DEV_DM \
++	CONFIG_BLK_DEV_DM=y \
+ 	CONFIG_DM_CRYPT \
+ 	CONFIG_DM_MIRROR
+   FILES:= \
+-    $(LINUX_DIR)/drivers/md/dm-mod.ko \
+     $(LINUX_DIR)/drivers/md/dm-crypt.ko \
+     $(LINUX_DIR)/drivers/md/dm-log.ko \
+     $(LINUX_DIR)/drivers/md/dm-mirror.ko \
+     $(LINUX_DIR)/drivers/md/dm-region-hash.ko
+-  AUTOLOAD:=$(call AutoLoad,30,dm-mod dm-log dm-region-hash dm-mirror dm-crypt)
++  AUTOLOAD:=$(call AutoLoad,30,dm-log dm-region-hash dm-mirror dm-crypt)
+ endef
+ 
+ define KernelPackage/dm/description
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0904-sbc-remove-dm-device-before-sysupgrade.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0904-sbc-remove-dm-device-before-sysupgrade.patch
new file mode 100644
index 0000000..b89ada9
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0904-sbc-remove-dm-device-before-sysupgrade.patch
@@ -0,0 +1,44 @@
+--- a/package/base-files/files/lib/upgrade/nand.sh
++++ b/package/base-files/files/lib/upgrade/nand.sh
+@@ -460,6 +460,11 @@ ubi_do_upgrade() {
+ 	local dual_boot=$(cat /sys/module/boot_param/parameters/dual_boot 2>/dev/null)
+ 	local file_type=$(identify $1)
+ 
++	if [ -b /dev/dm-0 ]; then
++		v "Detach all device mapper devices"
++		dmsetup remove_all
++	fi
++
+ 	if [ x"${dual_boot}" != xY ]; then
+ 		nand_do_upgrade "$1"
+ 		return
+--- a/target/linux/mediatek/base-files/lib/upgrade/mmc.sh
++++ b/target/linux/mediatek/base-files/lib/upgrade/mmc.sh
+@@ -217,6 +217,11 @@ mtk_mmc_do_upgrade_dual_boot() {
+ mtk_mmc_do_upgrade() {
+ 	local dual_boot=$(cat /sys/module/boot_param/parameters/dual_boot 2>/dev/null)
+ 
++	if [ -b /dev/dm-0 ]; then
++		v "Detach all device mapper devices"
++		dmsetup remove_all
++	fi
++
+ 	if [ x"${dual_boot}" = xY ]; then
+ 		mtk_mmc_do_upgrade_dual_boot "$1"
+ 	else
+--- a/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh
++++ b/target/linux/mediatek/mt7981/base-files/lib/upgrade/platform.sh
+@@ -1,4 +1,4 @@
+-RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv'
++RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv dmsetup'
+ RAMFS_COPY_DATA="/etc/fw_env.config /var/lock/fw_printenv.lock"
+ platform_do_upgrade() {
+ 	local board=$(board_name)
+--- a/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh
++++ b/target/linux/mediatek/mt7986/base-files/lib/upgrade/platform.sh
+@@ -1,4 +1,4 @@
+-RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv'
++RAMFS_COPY_BIN='mkfs.f2fs blkid blockdev fw_printenv fw_setenv dmsetup'
+ RAMFS_COPY_DATA="/etc/fw_env.config /var/lock/fw_printenv.lock"
+ 
+ platform_do_upgrade() {
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0905-fix-boot-up-failed.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0905-fix-boot-up-failed.patch
new file mode 100644
index 0000000..b95cff1
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0905-fix-boot-up-failed.patch
@@ -0,0 +1,22 @@
+Index: lede/target/linux/mediatek/image/Makefile
+===================================================================
+--- lede.orig/target/linux/mediatek/image/Makefile	2023-03-14 10:22:26.601486141 +0800
++++ lede/target/linux/mediatek/image/Makefile	2023-03-14 10:25:01.346275356 +0800
+@@ -18,13 +18,13 @@
+ 
+ # build squashfs-hashed
+ define Build/squashfs-hashed
+-	$(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed
++	$(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS))
+ 	$(TOPDIR)/scripts/make-squashfs-hashed.sh \
+-		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS)) \
+ 		$(STAGING_DIR_HOST) \
+ 		$(TOPDIR) \
+-		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary
+-	fdt-patch-dm-verify $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary-$(firstword $(DEVICE_DTS))
++	fdt-patch-dm-verify $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary-$(firstword $(DEVICE_DTS)) \
+ 		$(KDIR)/image-$(firstword $(DEVICE_DTS)).dtb $(KDIR)/image-sb-$(firstword $(DEVICE_DTS)).dtb \
+ 		$(HASHED_BOOT_DEVICE)
+ endef
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0906-fsek-build-host-tool-openssl3.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0906-fsek-build-host-tool-openssl3.patch
new file mode 100644
index 0000000..1305332
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0906-fsek-build-host-tool-openssl3.patch
@@ -0,0 +1,62 @@
+diff --git a/tools/Makefile b/tools/Makefile
+index dc07de7..ccd60a5 100644
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -37,6 +37,7 @@ tools-$(CONFIG_TARGET_mxs) += elftosb sdimage
+ tools-$(CONFIG_TARGET_tegra) += cbootimage cbootimage-configs
+ tools-$(CONFIG_USES_MINOR) += kernel2minor
+ tools-$(CONFIG_USE_SPARSE) += sparse
++tools-y += openssl
+ 
+ # builddir dependencies
+ $(curdir)/autoconf/compile := $(curdir)/m4/compile
+diff --git a/tools/openssl/Makefile b/tools/openssl/Makefile
+new file mode 100644
+index 0000000..087b33f
+--- /dev/null
++++ b/tools/openssl/Makefile
+@@ -0,0 +1,44 @@
++#
++# Copyright (C) 2006-2016 OpenWrt.org
++#
++# This is free software, licensed under the GNU General Public License v2.
++# See /LICENSE for more information.
++#
++
++include $(TOPDIR)/rules.mk
++
++PKG_NAME:=openssl
++PKG_VERSION:=3.0.7
++PKG_RELEASE:=3
++
++PKG_BUILD_PARALLEL:=1
++
++PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
++PKG_SOURCE_URL:= \
++	http://www.openssl.org/source/ \
++	http://ftp.fi.muni.cz/pub/openssl/source/ \
++	ftp://ftp.pca.dfn.de/pub/tools/net/openssl/source/ \
++
++PKG_HASH:=83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
++
++PKG_LICENSE:=OpenSSL
++PKG_LICENSE_FILES:=LICENSE
++PKG_MAINTAINER:=Eneas U de Queiroz <cotequeiroz@gmail.com>
++PKG_CPE_ID:=cpe:/a:openssl:openssl
++
++include $(INCLUDE_DIR)/host-build.mk
++
++define Host/Configure
++	(cd $(HOST_BUILD_DIR); \
++	./Configure -no-shared)
++endef
++
++define Host/Install
++	$(CP) $(HOST_BUILD_DIR)/apps/openssl $(STAGING_DIR_HOST)/bin/openssl-$(PKG_RELEASE)
++	mkdir -p $(STAGING_DIR_HOST)/lib/openssl-$(PKG_RELEASE)
++	$(CP) $(HOST_BUILD_DIR)/libssl.a $(STAGING_DIR_HOST)/lib/openssl-$(PKG_RELEASE)
++	$(CP) $(HOST_BUILD_DIR)/libcrypto.a $(STAGING_DIR_HOST)/lib/openssl-$(PKG_RELEASE)
++	ln -sf $(HOST_BUILD_DIR)/include $(STAGING_DIR_HOST)/include/openssl-$(PKG_RELEASE)
++endef
++
++$(eval $(call HostBuild))
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tool-aesgcm.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tool-aesgcm.patch
new file mode 100644
index 0000000..665f417
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tool-aesgcm.patch
@@ -0,0 +1,443 @@
+diff --git a/tools/Makefile b/tools/Makefile
+index ccd60a5..e487530 100644
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -38,6 +38,7 @@ tools-$(CONFIG_TARGET_tegra) += cbootimage cbootimage-configs
+ tools-$(CONFIG_USES_MINOR) += kernel2minor
+ tools-$(CONFIG_USE_SPARSE) += sparse
+ tools-y += openssl
++tools-y += aesgcm
+ 
+ # builddir dependencies
+ $(curdir)/autoconf/compile := $(curdir)/m4/compile
+@@ -76,6 +77,7 @@ $(curdir)/squashfs/compile := $(curdir)/lzma-old/compile
+ $(curdir)/squashfskit4/compile := $(curdir)/xz/compile $(curdir)/zlib/compile
+ $(curdir)/zlib/compile := $(curdir)/cmake/compile
+ $(curdir)/zstd/compile := $(curdir)/cmake/compile
++$(curdir)/aesgcm/compile := $(curdir)/openssl/compile
+ 
+ ifneq ($(HOST_OS),Linux)
+   $(curdir)/squashfskit4/compile += $(curdir)/coreutils/compile
+diff --git a/tools/aesgcm/Makefile b/tools/aesgcm/Makefile
+new file mode 100644
+index 0000000..f7ffc53
+--- /dev/null
++++ b/tools/aesgcm/Makefile
+@@ -0,0 +1,29 @@
++#
++# Copyright (C) 2022 MediaTek Inc. All rights reserved.
++#
++# This is free software, licensed under the GNU General Public License v2.
++# See /LICENSE for more information.
++#
++include $(TOPDIR)/rules.mk
++
++PKG_NAME:=aesgcm
++PKG_VERSION:=1.0
++
++include $(INCLUDE_DIR)/host-build.mk
++
++define Host/Compile
++	$(MAKE) -C $(HOST_BUILD_DIR) \
++	OPENSSL_INCS_LOCATION=-I$(STAGING_DIR_HOST)/include/openssl-3 \
++	OPENSSL_LIBS_LOCATION=-L$(STAGING_DIR_HOST)/lib/openssl-3
++endef
++
++define Host/Prepare
++	mkdir -p $(HOST_BUILD_DIR)
++	$(CP) -a ./src/* $(HOST_BUILD_DIR)/
++endef
++
++define Host/Install
++	$(CP) $(HOST_BUILD_DIR)/aesgcm $(STAGING_DIR_HOST)/bin/
++endef
++
++$(eval $(call HostBuild))
+diff --git a/tools/aesgcm/src/Makefile b/tools/aesgcm/src/Makefile
+new file mode 100644
+index 0000000..18d2e7b
+--- /dev/null
++++ b/tools/aesgcm/src/Makefile
+@@ -0,0 +1,12 @@
++CFLAGS = $(OPENSSL_INCS_LOCATION)
++LDFLAGS = $(OPENSSL_LIBS_LOCATION) -lssl -lcrypto -ldl -lpthread
++
++all: aesgcm
++
++aesgcm: aesgcm.o
++
++aesgcm:
++	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
++
++clean:
++	$(RM) aesgcm
+diff --git a/tools/aesgcm/src/aesgcm.c b/tools/aesgcm/src/aesgcm.c
+new file mode 100644
+index 0000000..05aa373
+--- /dev/null
++++ b/tools/aesgcm/src/aesgcm.c
+@@ -0,0 +1,364 @@
++/*
++ * Copyright 2022 Mediatek Inc. All Rights Reserved.
++ *
++ * Licensed under the Apache License 2.0 (the "License").  You may not use
++ * this file except in compliance with the License.  You can obtain a copy
++ * in the file LICENSE in the source distribution or at
++ * https://www.openssl.org/source/license.html
++ */
++
++/*
++ * Simple AES GCM authenticated encryption with additional data (AEAD)
++ * demonstration program.
++ */
++
++#include <stdlib.h>
++#include <stddef.h>
++#include <string.h>
++#include <unistd.h>
++#include <openssl/err.h>
++#include <openssl/bio.h>
++#include <openssl/evp.h>
++#include <openssl/core_names.h>
++
++#define MAX_TEXT_LENGTH		4096
++#define MAX_AAD_LENGTH		256
++#define MAX_TAG_LENGTH		32
++
++#define ERR_ENC			1
++#define ERR_DEC			2
++#define ERR_UNK_MOD		3
++
++typedef enum {
++	UNK,
++	ENCRYPT,
++	DECRYPT
++} OPERATION;
++
++/*
++ * A library context and property query can be used to select & filter
++ * algorithm implementations. If they are NULL then the default library
++ * context and properties are used.
++ */
++OSSL_LIB_CTX *libctx = NULL;
++const char *propq = NULL;
++
++int aes_gcm_encrypt(uint8_t *key, uint8_t *iv, long iv_len, uint8_t *aad,
++		    long aad_len, uint8_t *pt, long pt_len, BIO *out)
++{
++	int ret = 0;
++	EVP_CIPHER_CTX *ctx;
++	EVP_CIPHER *cipher = NULL;
++	int outlen, tmplen;
++	uint8_t *outbuf;
++	uint8_t *outtag[16];
++	OSSL_PARAM params[2] = {
++		OSSL_PARAM_END, OSSL_PARAM_END
++	};
++
++	outbuf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
++	if (!outbuf)
++		return 0;
++
++	/* Create a context for the encrypt operation */
++	if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
++		goto err;
++
++	/* Fetch the cipher implementation */
++	if ((cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq)) == NULL)
++		goto err;
++
++	/* Set IV length if default 96 bits is not appropriate */
++	params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
++						&iv_len);
++	/*
++	 * Initialise an encrypt operation with the cipher/mode, key, IV and
++	 * IV length parameter.
++	 * For demonstration purposes the IV is being set here. In a compliant
++	 * application the IV would be generated internally so the iv passed in
++	 * would be NULL.
++	 */
++	if (!EVP_EncryptInit_ex2(ctx, cipher, key, iv, params))
++		goto err;
++
++	/* Zero or more calls to specify any AAD */
++	if (!EVP_EncryptUpdate(ctx, NULL, &outlen, aad, aad_len))
++		goto err;
++
++	/* Encrypt plaintext */
++	if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, pt, pt_len))
++		goto err;
++
++	/* Finalise: note get no output for GCM */
++	if (!EVP_EncryptFinal_ex(ctx, outbuf, &tmplen))
++		goto err;
++
++	/* Get tag */
++	params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
++						      outtag, 16);
++
++	if (!EVP_CIPHER_CTX_get_params(ctx, params))
++		goto err;
++
++	/* Output IV */
++	if (BIO_write(out, iv, iv_len) <= 0)
++		goto err;
++
++	/* Output tag */
++	if (BIO_write(out, outtag, 16) <= 0)
++		goto err;
++
++	/* Output encrypted block */
++	if (BIO_write(out, outbuf, outlen) <= 0)
++		goto err;
++
++	ret = 1;
++err:
++	if (!ret)
++		ERR_print_errors_fp(stderr);
++
++	free(outbuf);
++	EVP_CIPHER_free(cipher);
++	EVP_CIPHER_CTX_free(ctx);
++
++	return ret;
++}
++
++int aes_gcm_decrypt(uint8_t *key, uint8_t *iv, long iv_len,
++		    uint8_t *aad, long aad_len, uint8_t *ct, long ct_len,
++		    uint8_t *tag, long tag_len, BIO *out)
++{
++	int ret = 0;
++	EVP_CIPHER_CTX *ctx;
++	EVP_CIPHER *cipher = NULL;
++	int outlen, rv;
++	uint8_t *outbuf;
++	OSSL_PARAM params[2] = {
++		OSSL_PARAM_END, OSSL_PARAM_END
++	};
++
++	outbuf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
++	if (!outbuf)
++		return 0;
++
++	if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
++		goto err;
++
++	/* Fetch the cipher implementation */
++	if ((cipher = EVP_CIPHER_fetch(libctx, "AES-256-GCM", propq)) == NULL)
++		goto err;
++
++	/* Set IV length if default 96 bits is not appropriate */
++	params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN,
++						&iv_len);
++
++	/*
++	 * Initialise an encrypt operation with the cipher/mode, key, IV and
++	 * IV length parameter.
++	 */
++	if (!EVP_DecryptInit_ex2(ctx, cipher, key, iv, params))
++		goto err;
++
++	/* Zero or more calls to specify any AAD */
++	if (!EVP_DecryptUpdate(ctx, NULL, &outlen, aad, aad_len))
++		goto err;
++
++	/* Decrypt plaintext */
++	if (!EVP_DecryptUpdate(ctx, outbuf, &outlen, ct, ct_len))
++		goto err;
++
++	/* Output decrypted block */
++	if (BIO_write(out, outbuf, outlen) <= 0)
++		goto err;
++
++	/* Set expected tag value. */
++	params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
++						      (void *)tag, tag_len);
++
++	if (!EVP_CIPHER_CTX_set_params(ctx, params))
++		goto err;
++
++	/* Finalise: note get no output for GCM */
++	rv = EVP_DecryptFinal_ex(ctx, outbuf, &outlen);
++	/*
++	 * Print out return value. If this is not successful authentication
++	 * failed and plaintext is not trustworthy.
++	 */
++	printf("Tag Verify %s\n", rv > 0 ? "Successful!" : "Failed!");
++
++	ret = rv > 0 ? 1 : 0;
++err:
++	if (!ret)
++		ERR_print_errors_fp(stderr);
++
++	free(outbuf);
++	EVP_CIPHER_free(cipher);
++	EVP_CIPHER_CTX_free(ctx);
++
++	return ret;
++}
++
++void usage(void)
++{
++	printf(
++		"simple aes-256-gcm tool: \n"
++		"Operations:\n"
++		"-e 		- encrypt\n"
++		"-d 		- decrypt\n"
++		"Common requirement parameters:\n"
++		"-i infile	- input file\n"
++		"-o outfile	- out file\n"
++		"-k key(hex) 	- key in hex\n"
++		"-n iv(hex)	- initial vector in hex\n"
++		"-a aad(hex)	- additional authentication data in hex\n"
++		"Decryption requirement paremters:\n"
++		"-t intagfile 	- tag file as input\n");
++}
++
++int main(int argc, char **argv)
++{
++	int ret = 0, opt, oper = UNK;
++	long key_len = 0, iv_len = 0, aad_len = 0, tag_len = 0, in_len = 0;
++	BIO *in = NULL, *out = NULL, *in_tag = NULL;
++	uint8_t *in_buf;
++	uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
++	uint8_t iv[EVP_MAX_IV_LENGTH] = {0};
++	uint8_t aad[MAX_AAD_LENGTH] = {0};
++	uint8_t tag[MAX_TAG_LENGTH] = {0};
++
++	in_buf = calloc(MAX_TEXT_LENGTH, sizeof(uint8_t));
++	if (!in_buf)
++		return -ENOMEM;
++
++	while ((opt = getopt(argc, argv, "a:dei:g:k:n:o:t:")) > 0) {
++		switch(opt) {
++		case 'a':
++			ret = OPENSSL_hexstr2buf_ex(aad, MAX_AAD_LENGTH,
++						    &aad_len, optarg, '\0');
++			if (!ret) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to read aad\n");
++				goto end;
++			}
++			break;
++		case 'd':
++			if (oper) {
++				ret = -EINVAL;
++				fprintf(stderr, "Duplicate operations\n");
++				goto end;
++			}
++			oper = DECRYPT;
++			break;
++		case 'e':
++			if (oper) {
++				ret = -EINVAL;
++				fprintf(stderr, "Duplicate operations\n");
++				goto end;
++			}
++			oper = ENCRYPT;
++			break;
++		case 'i':
++			in = BIO_new_file(optarg, "rb");
++			if (!in) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to open input file\n");
++				goto end;
++			}
++
++			in_len = BIO_read(in, in_buf, MAX_TEXT_LENGTH);
++			if (in_len <= 0) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to read input file\n");
++				goto end;
++			}
++			break;
++		case 'k':
++			ret = OPENSSL_hexstr2buf_ex(key, EVP_MAX_KEY_LENGTH,
++				    		    &key_len, optarg, '\0');
++			if (!ret) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to read key\n");
++				goto end;
++			}
++			break;
++			case 'n':
++			ret = OPENSSL_hexstr2buf_ex(iv, EVP_MAX_IV_LENGTH,
++				    		    &iv_len, optarg, '\0');
++			if (!ret) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to read iv\n");
++				goto end;
++			}
++			break;
++		case 'o':
++			out = BIO_new_file(optarg, "w");
++			if (!out) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to open output file\n");
++				goto end;
++			}
++			break;
++		case 't':
++			in_tag = BIO_new_file(optarg, "rb");
++			if (!in_tag) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to open tag file\n");
++				goto end;
++			}
++
++			tag_len = BIO_read(in_tag, tag, MAX_TAG_LENGTH);
++			if (tag_len <= 0) {
++				ret = -EINVAL;
++				fprintf(stderr, "Failed to read tag file\n");
++				goto end;
++			}
++			break;
++		default:
++			break;
++		}
++
++	}
++
++	if (!key_len || !iv_len || !aad_len || !in_len || !out) {
++		ret = -EINVAL;
++		goto end;
++	}
++
++	if (oper == ENCRYPT) {
++		ret = aes_gcm_encrypt(key, iv, iv_len, aad, aad_len,
++				      in_buf, in_len, out);
++		if (!ret) {
++			ret = -ERR_ENC;
++			goto end;
++		}
++	} else if (oper == DECRYPT) {
++		if (!tag_len) {
++			ret = -EINVAL;
++			goto end;
++		}
++
++		ret = aes_gcm_decrypt(key, iv, iv_len, aad, aad_len,
++				      in_buf, in_len, tag, tag_len, out);
++		if (!ret) {
++			ret = -ERR_DEC;
++			goto end;
++		}
++	} else {
++		ret = -ERR_UNK_MOD;
++		goto end;
++	}
++
++end:
++	free(in_buf);
++	if (in)
++		BIO_free(in);
++	if (out)
++		BIO_free(out);
++	if (in_tag)
++		BIO_free(in_tag);
++
++	if (ret == -EINVAL)
++		usage();
++
++	return ret;
++}
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tools.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tools.patch
new file mode 100644
index 0000000..9f2cdd2
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0907-fsek-build-host-tools.patch
@@ -0,0 +1,53 @@
+diff --git a/package/Makefile b/package/Makefile
+index 392d773..15af01b 100644
+--- a/package/Makefile
++++ b/package/Makefile
+@@ -62,6 +62,7 @@ ifndef SDK
+   $(curdir)/compile: $(curdir)/system/opkg/host/compile
+ endif
+ $(curdir)/compile: $(curdir)/cryptsetup/host/compile
++$(curdir)/compile: $(curdir)/dtc/host/compile
+ 
+ $(curdir)/install: $(TMP_DIR)/.build $(curdir)/merge $(if $(CONFIG_TARGET_PER_DEVICE_ROOTFS),$(curdir)/merge-index)
+ 	- find $(STAGING_DIR_ROOT) -type d | $(XARGS) chmod 0755
+diff --git a/package/utils/dtc/Makefile b/package/utils/dtc/Makefile
+index deec4e3..88ad84c 100644
+--- a/package/utils/dtc/Makefile
++++ b/package/utils/dtc/Makefile
+@@ -16,7 +16,10 @@ PKG_INSTALL:=1
+ PKG_MAINTAINER:=Yousong Zhou <yszhou4tech@gmail.com>
+ 
+ include $(INCLUDE_DIR)/package.mk
++include $(INCLUDE_DIR)/host-build.mk
+ 
++HOST_BUILD_PREFIX:=$(STAGING_DIR_HOST)
++HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/$(PKG_NAME)-$(PKG_VERSION)
+ 
+ define Package/dtc
+   SECTION:=utils
+@@ -87,6 +90,13 @@ define Build/InstallDev
+ 	$(CP) $(PKG_INSTALL_DIR)/lib/* $(1)/usr/lib
+ endef
+ 
++define Host/Install
++	$(CP) $(HOST_BUILD_DIR)/libfdt/libfdt*.so* $(STAGING_DIR_HOST)/lib/
++	$(CP) $(HOST_BUILD_DIR)/fdtget $(STAGING_DIR_HOST)/bin/
++	$(CP) $(HOST_BUILD_DIR)/fdtput $(STAGING_DIR_HOST)/bin/
++endef
++
+ $(eval $(call BuildPackage,dtc))
+ $(eval $(call BuildPackage,fdt-utils))
+ $(eval $(call BuildPackage,libfdt))
++$(eval $(call HostBuild))
+diff --git a/feeds/packages/utils/cryptsetup/Makefile b/feeds/packages/utils/cryptsetup/Makefile
+index 6d5264d..25553df 100644
+--- a/feeds/packages/utils/cryptsetup/Makefile
++++ b/feeds/packages/utils/cryptsetup/Makefile
+@@ -113,6 +113,7 @@ endef
+ 
+ define Host/Install
+ 	$(INSTALL_BIN) $(HOST_BUILD_DIR)/veritysetup $(STAGING_DIR_HOST)/bin/veritysetup
++	$(INSTALL_BIN) $(HOST_BUILD_DIR)/cryptsetup $(STAGING_DIR_HOST)/bin/cryptsetup
+ 	$(CP) $(HOST_BUILD_DIR)/.libs $(STAGING_DIR_HOST)/bin/
+ endef
+ 
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0908-fsek-build-libuboot-envtools.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0908-fsek-build-libuboot-envtools.patch
new file mode 100644
index 0000000..1863f6e
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0908-fsek-build-libuboot-envtools.patch
@@ -0,0 +1,15 @@
+diff --git a/package/boot/uboot-envtools/Makefile b/package/boot/uboot-envtools/Makefile
+index a9eccec..0728b88 100644
+--- a/package/boot/uboot-envtools/Makefile
++++ b/package/boot/uboot-envtools/Makefile
+@@ -76,4 +76,10 @@ define Package/uboot-envtools/install
+ 	)
+ endef
+ 
++define Build/InstallDev
++	ln -sf $(PKG_BUILD_DIR)/include $(STAGING_DIR)/usr/include/u-boot
++	$(CP) $(PKG_BUILD_DIR)/tools/env/lib.a  $(STAGING_DIR)/usr/lib/libuboot-envtools.a
++	$(CP) $(PKG_BUILD_DIR)/tools/env/fw_env.h $(STAGING_DIR)/usr/include/fw_env.h
++endef
++
+ $(eval $(call BuildPackage,uboot-envtools))
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0909-fsek-setup-host-scripts.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0909-fsek-setup-host-scripts.patch
new file mode 100644
index 0000000..27f455e
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0909-fsek-setup-host-scripts.patch
@@ -0,0 +1,349 @@
+diff --git a/scripts/enc-rfsk.sh b/scripts/enc-rfsk.sh
+new file mode 100755
+index 0000000..d396127
+--- /dev/null
++++ b/scripts/enc-rfsk.sh
+@@ -0,0 +1,194 @@
++#!/bin/bash
++# use roe-key to encrypt rootfs-key, and generate fit-secret
++usage() {
++	printf "Usage: %s -s build_dir -d key_dir -f fit" "$(basename "$0")"
++	printf "encrypt rootfs key: [-k roe_key] "
++	printf "generate FIT-secret: [-k roe_key -c config_name]\n"
++	printf "\n\t-c ==> config name with signature node"
++	printf "\n\t-d ==> key_directory"
++	printf "\n\t-f ==> fit image file"
++	printf "\n\t-k ==> roe key"
++	printf "\n\t-s ==> build directory"
++	exit 1
++}
++
++hex2bin() {
++	$PERL -e 'print pack "H*", <STDIN>'
++}
++
++bin2hex() {
++	od -An -t x1 -w128 | sed "s/ //g"
++}
++
++hkdf() {
++	local key=$1
++	local info=$2
++	local salt=$3
++	local k_derived=$(${OPENSSL} kdf -keylen 32 -kdfopt digest:SHA2-256 \
++		-kdfopt hexkey:$(cat ${key} | bin2hex) \
++		-kdfopt hexsalt:$salt \
++		-kdfopt info:$info HKDF | sed "s/://g")
++	echo $k_derived
++}
++
++gen_aes_key() {
++	out=$1
++	$OPENSSL rand -out $out 32
++}
++
++aes_cbc_enc() {
++	local in=$1
++	local out=$2
++	local key=$3
++	local iv=$(${OPENSSL} rand -hex 16)
++	$OPENSSL enc -e -aes-256-cbc -in $in -out ${out}.tmp -K $key -iv $iv
++
++	echo -n $iv | hex2bin > $out
++	cat ${out}.tmp >> $out
++	rm ${out}.tmp
++}
++
++aes_gcm_enc() {
++	local in=$1
++	local out=$2
++	local key=$3
++	local aad=$4
++	local iv=$(${OPENSSL} rand -hex 12)
++	$AESGCM -e -i $in -o $out -k $key -n $iv -a $aad
++}
++
++# encrypt data with AES
++# encrypted-data-format:
++# -----------------------------------------------
++# | salt | iv | k-tempx.enc | iv | tag | in.enc |
++# -----------------------------------------------
++enc_data() {
++	local k_temp=$1
++	local in=$2
++	local out=$3
++	local info=$4
++	local salt=$(${OPENSSL} rand -hex 16)
++
++	echo -n $salt | hex2bin > $out
++
++	# encrypt k-tempx
++	aes_cbc_enc $k_temp ${out}.tmp $(hkdf ${ROE_KEY}.key ${info} ${salt})
++
++	cat ${out}.tmp >> $out
++
++	aad=$(cat ${out} | bin2hex)
++	# encrypt in
++	aes_gcm_enc $in ${out}.tmp $(cat $k_temp | bin2hex) $aad
++
++	cat ${out}.tmp >> $out
++	rm ${out}.tmp
++}
++
++# generate FIT-secret and insert back into FIT
++gen_fit_secret() {
++	echo "Generating fit-secret"
++
++	if [ ! -f "${FIT}" ]; then
++		printf "%s not found\n" "${FIT}"
++		exit 1
++	fi
++
++	SECRETS_DIR=$BUILD_DIR/fit-secrets
++	if [ ! -d "${SECRETS_DIR}" ]; then
++		mkdir -p $SECRETS_DIR
++	fi
++
++	LD_LIBRARY_PATH=${LIBFDT_PATH} \
++	$FDTGET $FIT /configurations/$CONFIG/signature value -t bi > \
++		$SECRETS_DIR/$FIT_NAME-signature.tmp || exit 1
++
++	echo -n $(cat ${SECRETS_DIR}/${FIT_NAME}-signature.tmp) | xargs printf "%02x" | \
++		hex2bin > $SECRETS_DIR/$FIT_NAME-signature.raw
++
++	$OPENSSL dgst -sha256 -binary -out $SECRETS_DIR/$FIT_NAME-signature.hash \
++		$SECRETS_DIR/$FIT_NAME-signature.raw || exit 1
++
++	gen_aes_key $KEY_DIR/${TEMP2_KEY_NAME}.key
++
++	enc_data $KEY_DIR/${TEMP2_KEY_NAME}.key \
++		 $SECRETS_DIR/$FIT_NAME-signature.hash \
++		 $SECRETS_DIR/$FIT_NAME-secret.enc \
++		 fit-secret
++
++	LD_LIBRARY_PATH=${LIBFDT_PATH} \
++	$FDTPUT -c -p $FIT /fit-secrets/$CONFIG || exit 1
++	LD_LIBRARY_PATH=${LIBFDT_PATH} \
++	$FDTPUT $FIT /fit-secrets/$CONFIG algo -t s "sha256"
++	LD_LIBRARY_PATH=${LIBFDT_PATH} \
++	$FDTPUT $FIT /fit-secrets/$CONFIG data -t x \
++		$(cat ${SECRETS_DIR}/${FIT_NAME}-secret.enc | bin2hex | \
++		sed 's/ //g; s/.\{8\}/0x& /g; s/.$//g')
++}
++
++# encrypt rootfs key
++enc_rfsk() {
++	echo "Encrypting rootfs key"
++
++	gen_aes_key $KEY_DIR/${ROOTFS_KEY_NAME}.key
++	gen_aes_key $KEY_DIR/${TEMP1_KEY_NAME}.key
++
++	enc_data $KEY_DIR/${TEMP1_KEY_NAME}.key \
++		 $KEY_DIR/${ROOTFS_KEY_NAME}.key \
++		 $BUILD_DIR/${FIT_NAME}-rfsk.enc \
++		 k-rootfs
++}
++
++while getopts "c:d:f:k:s:" OPTION
++do
++	case $OPTION in
++	c ) CONFIG=$OPTARG;;
++	d ) KEY_DIR=$OPTARG;;
++	f ) FIT=$OPTARG;;
++	k ) ROE_KEY=$OPTARG;;
++	s ) BUILD_DIR=$OPTARG;;
++	* ) echo "Invalid option passed to '$0' (options:$*)"
++	usage;;
++	esac
++done
++
++if [ ! -d "${KEY_DIR}" ]; then
++	echo "key directory not found"
++	usage;
++fi
++
++if [ ! -d "${BUILD_DIR}" ]; then
++	echo "build directory not found"
++	usage;
++fi
++
++if [ -z "${FIT}" ]; then
++	echo "FIT name is empty"
++	usage;
++fi
++
++if [ -z "${BIN}" ]; then
++	echo "bin directory not found"
++	exit 1
++fi
++
++if [ ! -f "${ROE_KEY}".key ]; then
++	echo "roe-key not found"
++	usage;
++fi
++
++OPENSSL=$BIN/openssl-3
++AESGCM=$BIN/aesgcm
++PERL=$BIN/perl
++FDTGET=$BIN/fdtget
++FDTPUT=$BIN/fdtput
++
++FIT_NAME=$(basename ${FIT} | sed "s/\.[^.]*$//g")
++ROOTFS_KEY_NAME=${FIT_NAME}-rootfs-key
++TEMP1_KEY_NAME=${FIT_NAME}-temp1-key
++TEMP2_KEY_NAME=${FIT_NAME}-temp2-key
++
++if [ ! -z "${CONFIG}" ]; then
++	gen_fit_secret;
++else
++	enc_rfsk;
++fi
+diff --git a/scripts/fdt-patch-dm-crypt.sh b/scripts/fdt-patch-dm-crypt.sh
+new file mode 100755
+index 0000000..9ad87c7
+--- /dev/null
++++ b/scripts/fdt-patch-dm-crypt.sh
+@@ -0,0 +1,26 @@
++# !/bin/bash
++FDTGET=$BIN/fdtget
++FDTPUT=$BIN/fdtput
++PERL=$BIN/perl
++
++FDT=$1
++SUMMARY=$2
++BOOTARGS=$(LD_LIBRARY_PATH=${LIBFDT_PATH} \
++		${FDTGET} ${FDT} /chosen bootargs | sed "s/\"*$/;/g")
++DEVICE_PATH=$(echo -n ${BOOTARGS} | grep -o "root=/dev/dm-[0-9]\+" | \
++		grep -o "/dev/dm-[0-9]\+")
++DEVICE_NUM=$(echo -n ${DEVICE_PATH} | grep -o "[0-9]\+")
++BOOTARGS=$(echo -n ${BOOTARGS} | \
++		sed "s/root=\/dev\/dm-${DEVICE_NUM}/root=\/dev\/dm-$((DEVICE_NUM+1))/g")
++DATABLOCKS_NUM=$(cat ${SUMMARY} | grep "Data blocks:" | grep -o "[0-9]\+")
++CIPHER="aes-xts-plain64"
++KEY=$(${PERL} -E 'say "0" x 64')
++IV_OFFSET=0
++OFFSET=0
++
++NEW_BOOTARGS=$( printf '%sdm-crypt,,,ro,0 %d crypt %s %s %d %s %d"' \
++		"${BOOTARGS}" $((DATABLOCKS_NUM*8)) ${CIPHER} ${KEY} \
++		${IV_OFFSET} ${DEVICE_PATH} ${OFFSET} )
++
++LD_LIBRARY_PATH=${LIBFDT_PATH}
++$FDTPUT $FDT /chosen bootargs -t s "${NEW_BOOTARGS}" || exit 1
+diff --git a/scripts/make-squashfs-encrypted.sh b/scripts/make-squashfs-encrypted.sh
+new file mode 100755
+index 0000000..9bb522b
+--- /dev/null
++++ b/scripts/make-squashfs-encrypted.sh
+@@ -0,0 +1,37 @@
++# !/bin/bash
++ROOTFS=$1
++ENCRYPTED_ROOTFS=$2
++ROOTFS_KEY_DIR=$3
++TARGET_DEVICE=$4
++
++if [ -z "${BIN}" ]; then
++	echo "bin directory not found"
++	exit 1
++fi
++
++CRYPTSETUP=$BIN/cryptsetup
++DEVICE_NAME=$(basename ${TARGET_DEVICE} | sed "s/\.[^.]*$//g")
++ROOTFS_KEY=${ROOTFS_KEY_DIR}/${DEVICE_NAME}-rootfs-key.key
++if [ ! -f "${ROOTFS}" ] || [ -z "${ENCRYPTED_ROOTFS}" ] ||
++   [ ! -f "${ROOTFS_KEY}" ] || [ -z "${DEVICE_NAME}" ]; then
++	exit 1
++fi
++
++FILE_SIZE=`stat -c "%s" ${ROOTFS}`
++BLOCK_SIZE=4096
++DATA_BLOCKS=$((${FILE_SIZE} / ${BLOCK_SIZE}))
++[ $((${FILE_SIZE} % ${BLOCK_SIZE})) -ne 0 ] && DATA_BLOCKS=$((${DATA_BLOCKS} + 1))
++
++# create container
++dd if=/dev/zero of=$ENCRYPTED_ROOTFS bs=4096 count=$DATA_BLOCKS
++
++# mapping encrypted device
++sudo $CRYPTSETUP open --type=plain --cipher=aes-xts-plain64 --key-size=256 \
++	--key-file=$ROOTFS_KEY $ENCRYPTED_ROOTFS ${DEVICE_NAME}
++
++# encrypt squashfs
++sudo dd if=$ROOTFS of=/dev/mapper/${DEVICE_NAME}
++
++# close mapping device
++sudo $CRYPTSETUP close /dev/mapper/${DEVICE_NAME}
++
+diff --git a/scripts/mkits.sh b/scripts/mkits.sh
+index 1c7f292..26bcf70 100755
+--- a/scripts/mkits.sh
++++ b/scripts/mkits.sh
+@@ -17,7 +17,7 @@
+ usage() {
+ 	printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")"
+ 	printf " -v version -k kernel [-D name -n address -d dtb] -o its_file"
+-	printf " [-s script] [-S key_name_hint] [-r ar_ver] [-R rootfs]"
++	printf " [-s script] [-S key_name_hint] [-r ar_ver] [-R rootfs] [-m rfsk]"
+ 
+ 	printf "\n\t-A ==> set architecture to 'arch'"
+ 	printf "\n\t-C ==> set compression type 'comp'"
+@@ -33,13 +33,14 @@ usage() {
+ 	printf "\n\t-s ==> include u-boot script 'script'"
+ 	printf "\n\t-S ==> add signature at configurations and assign its key_name_hint by 'key_name_hint'"
+ 	printf "\n\t-r ==> set anti-rollback version to 'fw_ar_ver' (dec)"
+-	printf "\n\t-R ==> specify rootfs file for embedding hash\n"
++	printf "\n\t-R ==> specify rootfs file for embedding hash"
++	printf "\n\t-m ==> include encrypted rootfs key'\n"
+ 	exit 1
+ }
+ 
+ FDTNUM=1
+ 
+-while getopts ":A:a:c:C:D:d:e:k:n:o:v:s:S:r:R:" OPTION
++while getopts ":A:a:c:C:D:d:e:k:n:o:v:s:S:r:R:m:" OPTION
+ do
+ 	case $OPTION in
+ 		A ) ARCH=$OPTARG;;
+@@ -57,6 +58,7 @@ do
+ 		S ) KEY_NAME_HINT=$OPTARG;;
+ 		r ) AR_VER=$OPTARG;;
+ 		R ) ROOTFS_FILE=$OPTARG;;
++		m ) ROOTFS_KEY=$OPTARG;;
+ 		* ) echo "Invalid option passed to '$0' (options:$*)"
+ 		usage;;
+ 	esac
+@@ -91,6 +93,19 @@ if [ -n "${DTB}" ]; then
+ 	FDT_PROP="fdt = \"fdt-$FDTNUM\";"
+ fi
+ 
++# Conditionally create encrypted rootfs-key information
++if [ -n "${ROOTFS_KEY}" ]; then
++	RFSK_NODE="
++			rfsk = <$(cat ${ROOTFS_KEY} | od -An -t x1 -w256 | sed 's/ //g; s/.\{8\}/0x& /g; s/.$//g')>;";
++
++	FIT_SECRET_NODE="
++	fit-secrets {
++		${CONFIG} {
++		};
++	};
++"
++fi
++
+ # Conditionally create rootfs hash information
+ if [ -f "${ROOTFS_FILE}" ]; then
+ 	ROOTFS_SIZE=$(stat -c %s ${ROOTFS_FILE})
+@@ -200,12 +215,15 @@ ${ROOTFS}
+ 		${CONFIG} {
+ 			description = \"OpenWrt\";
+ ${FW_AR_VER}
++${RFSK_NODE}
+ ${LOADABLES}
+ 			kernel = \"kernel-1\";
+ 			${FDT_PROP}
+ ${SIGNATURE}
+ 		};
+ 	};
++
++${FIT_SECRET_NODE}
+ };"
+ 
+ # Write .its file to disk
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0910-fsek-encrypt-rfsk-and-patch-dm-crypt.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0910-fsek-encrypt-rfsk-and-patch-dm-crypt.patch
new file mode 100644
index 0000000..7aca849
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0910-fsek-encrypt-rfsk-and-patch-dm-crypt.patch
@@ -0,0 +1,100 @@
+diff --git a/include/image.mk b/include/image.mk
+index 92d343c..f93fb01 100644
+--- a/include/image.mk
++++ b/include/image.mk
+@@ -440,6 +440,8 @@ else
+   DEVICE_CHECK_PROFILE = $(CONFIG_TARGET_$(if $(CONFIG_TARGET_MULTI_PROFILE),DEVICE_)$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_$(1))
+ endif
+ 
++ROOTFS_ENCRYPT = $(if $(ROE_KEY_DIR),$(wildcard $(ROE_KEY_DIR)/$(ROE_KEY_NAME).key),)
++
+ DEVICE_CHECK_FIT_KEY = $(if $(wildcard $(FIT_KEY_DIR)/$(FIT_KEY_NAME).key),install-images,install-disabled)
+ DEVICE_CHECK_FIT_DIR = $(if $(FIT_KEY_DIR),$(DEVICE_CHECK_FIT_KEY),install-images)
+ 
+diff --git a/target/linux/mediatek/image/Makefile b/target/linux/mediatek/image/Makefile
+index 20e5977..52c266e 100644
+--- a/target/linux/mediatek/image/Makefile
++++ b/target/linux/mediatek/image/Makefile
+@@ -16,6 +16,14 @@ define Build/sysupgrade-emmc
+ 		$(IMAGE_ROOTFS)
+ endef
+ 
++define Build/fdt-patch-dm-crypt
++	BIN=$(STAGING_DIR_HOST)/bin \
++	LIBFDT_PATH=$(STAGING_DIR_HOST)/lib \
++	$(TOPDIR)/scripts/fdt-patch-dm-crypt.sh \
++		$(KDIR)/image-sb-$(firstword $(DEVICE_DTS)).dtb \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary-$(firstword $(DEVICE_DTS))
++endef
++
+ # build squashfs-hashed
+ define Build/squashfs-hashed
+ 	$(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS))
+@@ -27,6 +35,7 @@ define Build/squashfs-hashed
+ 	fdt-patch-dm-verify $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary-$(firstword $(DEVICE_DTS)) \
+ 		$(KDIR)/image-$(firstword $(DEVICE_DTS)).dtb $(KDIR)/image-sb-$(firstword $(DEVICE_DTS)).dtb \
+ 		$(HASHED_BOOT_DEVICE)
++	$(if $(ROOTFS_ENCRYPT),$(call Build/fdt-patch-dm-crypt))
+ endef
+ 
+ # build fw-ar-ver
+@@ -40,6 +49,30 @@ define Build/fw-ar-ver
+ 	$(call get_fw_ar_ver,$(ANTI_ROLLBACK_TABLE),$(AUTO_AR_CONF))
+ endef
+ 
++define Build/rfsk-encrypt
++	BIN=$(STAGING_DIR_HOST)/bin \
++	$(TOPDIR)/scripts/enc-rfsk.sh \
++		-d $(ROE_KEY_DIR) \
++		-f $@ \
++		-k $(ROE_KEY_DIR)/$(ROE_KEY_NAME) \
++		-s $(dir $@)
++endef
++
++define Build/fit-secret
++	BIN=$(STAGING_DIR_HOST)/bin \
++	LIBFDT_PATH=$(STAGING_DIR_HOST)/lib \
++	$(TOPDIR)/scripts/enc-rfsk.sh \
++		-c "config-1" \
++		-d $(ROE_KEY_DIR) \
++		-f $@ \
++		-k $(ROE_KEY_DIR)/$(ROE_KEY_NAME) \
++		-s $(dir $@)
++endef
++
++define Build/rootfs-encrypt
++	$(if $(ROOTFS_ENCRYPT),$(call Build/rfsk-encrypt))
++endef
++
+ # build signed fit
+ define Build/fit-sign
+ 	$(TOPDIR)/scripts/mkits.sh \
+@@ -54,13 +87,18 @@ define Build/fit-sign
+ 		-v $(LINUX_VERSION) \
+ 		$(if $(FIT_KEY_NAME),-S $(FIT_KEY_NAME)) \
+ 		$(if $(FW_AR_VER),-r $(FW_AR_VER)) \
+-		$(if $(CONFIG_TARGET_ROOTFS_SQUASHFS),-R $(ROOTFS/squashfs/$(DEVICE_NAME)))
++		$(if $(CONFIG_TARGET_ROOTFS_SQUASHFS), \
++			$(if $(ROOTFS_ENCRYPT), \
++				-R $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS)), \
++				-R $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)))) \
++		$(if $(ROOTFS_ENCRYPT),-m $(addsuffix -rfsk.enc,$(basename $@)))
+ 	PATH=$(LINUX_DIR)/scripts/dtc:$(PATH) mkimage \
+ 		-f $@.its \
+ 		$(if $(FIT_KEY_DIR),-k $(FIT_KEY_DIR)) \
+ 		-r \
+ 		$@.new
+ 	@mv $@.new $@
++	$(if $(ROOTFS_ENCRYPT),$(call Build/fit-secret))
+ endef
+ 
+ # default all platform image(fit) build 
+@@ -78,6 +116,8 @@ define Device/Default
+ 	pad-rootfs | append-metadata
+   FIT_KEY_DIR :=
+   FIT_KEY_NAME :=
++  ROE_KEY_DIR :=
++  ROE_KEY_NAME :=
+ endef
+ 
+ include $(SUBTARGET).mk
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0911-fsek-encrypt-rootfs.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0911-fsek-encrypt-rootfs.patch
new file mode 100644
index 0000000..3c39aef
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0911-fsek-encrypt-rootfs.patch
@@ -0,0 +1,35 @@
+diff --git a/target/linux/mediatek/image/Makefile b/target/linux/mediatek/image/Makefile
+index 52c266e..36deb6f 100644
+--- a/target/linux/mediatek/image/Makefile
++++ b/target/linux/mediatek/image/Makefile
+@@ -24,9 +24,21 @@ define Build/fdt-patch-dm-crypt
+ 		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-summary-$(firstword $(DEVICE_DTS))
+ endef
+ 
++define Build/squashfs-encrypt
++	BIN=$(STAGING_DIR_HOST)/bin \
++	$(TOPDIR)/scripts/make-squashfs-encrypted.sh \
++	$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) \
++	$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-encrypted-$(firstword $(DEVICE_DTS)) \
++	$(ROE_KEY_DIR) \
++	$@
++endef
++
+ # build squashfs-hashed
+ define Build/squashfs-hashed
+-	$(CP) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME)) $(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS))
++	$(CP) $(if $(ROOTFS_ENCRYPT), \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-encrypted-$(firstword $(DEVICE_DTS)), \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))) \
++		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS))
+ 	$(TOPDIR)/scripts/make-squashfs-hashed.sh \
+ 		$(ROOTFS/$(FILESYSTEMS)/$(DEVICE_NAME))-hashed-$(firstword $(DEVICE_DTS)) \
+ 		$(STAGING_DIR_HOST) \
+@@ -71,6 +83,7 @@ endef
+ 
+ define Build/rootfs-encrypt
+ 	$(if $(ROOTFS_ENCRYPT),$(call Build/rfsk-encrypt))
++	$(if $(ROOTFS_ENCRYPT),$(call Build/squashfs-encrypt))
+ endef
+ 
+ # build signed fit
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0912-sbc-upgrade-mkimage-to-2022-07.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0912-sbc-upgrade-mkimage-to-2022-07.patch
new file mode 100644
index 0000000..59b3cbe
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0912-sbc-upgrade-mkimage-to-2022-07.patch
@@ -0,0 +1,239 @@
+--- a/tools/libressl/Makefile
++++ b/tools/libressl/Makefile
+@@ -8,8 +8,8 @@
+ include $(TOPDIR)/rules.mk
+ 
+ PKG_NAME:=libressl
+-PKG_VERSION:=3.4.3
+-PKG_HASH:=ff88bffe354818b3ccf545e3cafe454c5031c7a77217074f533271d63c37f08d
++PKG_VERSION:=3.5.3
++PKG_HASH:=3ab5e5eaef69ce20c6b170ee64d785b42235f48f2e62b095fca5d7b6672b8b28
+ PKG_RELEASE:=1
+ 
+ PKG_CPE_ID:=cpe:/a:openbsd:libressl
+@@ -24,11 +24,14 @@ HOST_BUILD_PARALLEL:=1
+ include $(INCLUDE_DIR)/host-build.mk
+ 
+ HOSTCC := $(HOSTCC_NOCACHE)
+-HOST_CONFIGURE_ARGS += --enable-static --disable-shared --disable-tests
++
+ HOST_CFLAGS += $(HOST_FPIC)
+ 
+-ifeq ($(GNU_HOST_NAME),x86_64-linux-gnux32)
+-HOST_CONFIGURE_ARGS += --disable-asm
+-endif
++HOST_CONFIGURE_ARGS += \
++	--enable-static \
++	--disable-shared \
++	--disable-asm \
++	--with-pic \
++	--disable-tests
+ 
+ $(eval $(call HostBuild))
+--- a/tools/Makefile
++++ b/tools/Makefile
+@@ -59,7 +59,7 @@ $(curdir)/libtool/compile := $(curdir)/m
+ $(curdir)/lzma-old/compile := $(curdir)/zlib/compile
+ $(curdir)/make-ext4fs/compile := $(curdir)/zlib/compile
+ $(curdir)/missing-macros/compile := $(curdir)/autoconf/compile
+-$(curdir)/mkimage/compile += $(curdir)/libressl/compile
++$(curdir)/mkimage/compile += $(curdir)/bison/compile $(curdir)/libressl/compile
+ $(curdir)/mklibs/compile := $(curdir)/libtool/compile
+ $(curdir)/mm-macros/compile := $(curdir)/libtool/compile
+ $(curdir)/mpc/compile := $(curdir)/mpfr/compile $(curdir)/gmp/compile
+--- a/tools/mkimage/Makefile
++++ b/tools/mkimage/Makefile
+@@ -7,36 +7,36 @@
+ include $(TOPDIR)/rules.mk
+ 
+ PKG_NAME:=mkimage
+-PKG_VERSION:=2021.01
++PKG_VERSION:=2022.07
+ 
+ PKG_SOURCE:=u-boot-$(PKG_VERSION).tar.bz2
+ PKG_SOURCE_URL:= \
+ 	https://mirror.cyberbits.eu/u-boot \
+ 	https://ftp.denx.de/pub/u-boot \
+ 	ftp://ftp.denx.de/pub/u-boot
+-PKG_HASH:=b407e1510a74e863b8b5cb42a24625344f0e0c2fc7582d8c866bd899367d0454
++PKG_HASH:=92b08eb49c24da14c1adbf70a71ae8f37cc53eeb4230e859ad8b6733d13dcf5e
+ 
+ HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/u-boot-$(PKG_VERSION)
+ 
+ include $(INCLUDE_DIR)/host-build.mk
+ 
+-define Host/Prepare
+-	$(Host/Prepare/Default)
+-	mkdir -p $(HOST_BUILD_DIR)/include/config
+-	touch $(HOST_BUILD_DIR)/include/config/auto.conf
+-	mkdir -p $(HOST_BUILD_DIR)/include/generated/
+-	touch $(HOST_BUILD_DIR)/include/generated/autoconf.h
++define Host/Configure
++	$(MAKE) -C $(HOST_BUILD_DIR) \
++		HOSTCFLAGS="$(HOST_CFLAGS)" \
++		HOSTLDFLAGS="$(HOST_LDFLAGS)" \
++		PKG_CONFIG_EXTRAARGS="--static" \
++		V=$(if $(findstring c,$(OPENWRT_VERBOSE)),1) \
++		tools-only_config
++
++	sed -i 's/CONFIG_TOOLS_MKEFICAPSULE=y/# CONFIG_TOOLS_MKEFICAPSULE is not set/' $(HOST_BUILD_DIR)/.config
+ endef
+ 
+ define Host/Compile
+ 	$(MAKE) -C $(HOST_BUILD_DIR) \
+ 		HOSTCFLAGS="$(HOST_CFLAGS)" \
+ 		HOSTLDFLAGS="$(HOST_LDFLAGS)" \
+-		no-dot-config-targets=tools-only \
+-		CONFIG_MKIMAGE_DTC_PATH=dtc \
+-		CONFIG_FIT=y \
+-		CONFIG_FIT_SIGNATURE=y \
+-		CONFIG_FIT_SIGNATURE_MAX_SIZE=0x10000000 \
++		PKG_CONFIG_EXTRAARGS="--static" \
++		V=$(if $(findstring c,$(OPENWRT_VERBOSE)),1) \
+ 		tools-only
+ endef
+ 
+--- a/tools/mkimage/patches/030-allow-to-use-different-magic.patch
++++ b/tools/mkimage/patches/030-allow-to-use-different-magic.patch
+@@ -2,7 +2,7 @@ This patch makes it possible to set a cu
+ 
+ --- a/tools/mkimage.c
+ +++ b/tools/mkimage.c
+-@@ -21,6 +21,7 @@ static struct image_tool_params params =
++@@ -25,6 +25,7 @@ static struct image_tool_params params =
+  	.arch = IH_ARCH_PPC,
+  	.type = IH_TYPE_KERNEL,
+  	.comp = IH_COMP_GZIP,
+@@ -10,8 +10,8 @@ This patch makes it possible to set a cu
+  	.dtc = MKIMAGE_DEFAULT_DTC_OPTIONS,
+  	.imagename = "",
+  	.imagename2 = "",
+-@@ -82,11 +83,12 @@ static void usage(const char *msg)
+- 			 "          -l ==> list image header information\n",
++@@ -88,11 +89,12 @@ static void usage(const char *msg)
++ 			 "          -q ==> quiet\n",
+  		params.cmdname);
+  	fprintf(stderr,
+ -		"       %s [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image\n"
+@@ -24,16 +24,16 @@ This patch makes it possible to set a cu
+  		"          -a ==> set load address to 'addr' (hex)\n"
+  		"          -e ==> set entry point to 'ep' (hex)\n"
+  		"          -n ==> set image name to 'name'\n"
+-@@ -150,7 +152,7 @@ static void process_args(int argc, char
++@@ -163,7 +165,7 @@ static void process_args(int argc, char
+  	int opt;
+  
+  	while ((opt = getopt(argc, argv,
+--		   "a:A:b:B:c:C:d:D:e:Ef:Fk:i:K:ln:N:p:O:rR:qstT:vVx")) != -1) {
+-+		   "a:A:b:B:c:C:d:D:e:Ef:Fk:i:K:lM:n:N:p:O:rR:qstT:vVx")) != -1) {
++-		   "a:A:b:B:c:C:d:D:e:Ef:FG:k:i:K:ln:N:p:o:O:rR:qstT:vVx")) != -1) {
+++		   "a:A:b:B:c:C:d:D:e:Ef:FG:k:i:K:lM:n:N:p:o:O:rR:qstT:vVx")) != -1) {
+  		switch (opt) {
+  		case 'a':
+  			params.addr = strtoull(optarg, &ptr, 16);
+-@@ -237,6 +239,14 @@ static void process_args(int argc, char
++@@ -254,6 +256,14 @@ static void process_args(int argc, char
+  		case 'l':
+  			params.lflag = 1;
+  			break;
+@@ -61,7 +61,7 @@ This patch makes it possible to set a cu
+  	image_set_load(hdr, addr);
+ --- a/tools/imagetool.h
+ +++ b/tools/imagetool.h
+-@@ -56,6 +56,7 @@ struct image_tool_params {
++@@ -59,6 +59,7 @@ struct image_tool_params {
+  	int arch;
+  	int type;
+  	int comp;
+--- a/tools/mkimage/patches/050-Add-compatibility-with-non-Linux-hosts.patch
++++ b/tools/mkimage/patches/050-Add-compatibility-with-non-Linux-hosts.patch
+@@ -15,11 +15,9 @@ __u64 is not available on FreeBSD, remov
+ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ ---
+  include/image.h             | 2 ++
+- include/imx8image.h         | 5 +++++
+  include/linux/posix_types.h | 2 ++
+  include/linux/types.h       | 4 +++-
+- lib/rsa/rsa-sign.c          | 2 +-
+- 5 files changed, 13 insertions(+), 2 deletions(-)
++ 3 files changed, 7 insertions(+), 1 deletion(-)
+ 
+ --- a/include/image.h
+ +++ b/include/image.h
+--- a/tools/mkimage/patches/090-macos-arm64-builing-fix.patch
++++ /dev/null
+@@ -1,47 +0,0 @@
+-This patch fixes compilation issues on MacOS arm64.
+-Based on discussion 
+-https://github.com/u-boot/u-boot/commit/3b142045e8a7f0ab17b6099e9226296af45967d0
+-
+-diff --git a/Makefile b/Makefile
+-index b4f1cbc..551041f 100644
+---- a/Makefile
+-+++ b/Makefile
+-@@ -324,11 +324,6 @@ HOSTCC       = $(call os_x_before, 10, 5, "cc", "gcc")
+- KBUILD_HOSTCFLAGS  += $(call os_x_before, 10, 4, "-traditional-cpp")
+- KBUILD_HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress")
+- 
+--# since Lion (10.7) ASLR is on by default, but we use linker generated lists
+--# in some host tools which is a problem then ... so disable ASLR for these
+--# tools
+--KBUILD_HOSTLDFLAGS += $(call os_x_before, 10, 7, "", "-Xlinker -no_pie")
+--
+- # macOS Mojave (10.14.X) 
+- # Undefined symbols for architecture x86_64: "_PyArg_ParseTuple"
+- KBUILD_HOSTLDFLAGS += $(call os_x_after, 10, 14, "-lpython -dynamclib", "")
+-diff --git a/tools/imagetool.h b/tools/imagetool.h
+-index 8726792..d1b72ef 100644
+---- a/tools/imagetool.h
+-+++ b/tools/imagetool.h
+-@@ -270,17 +270,20 @@ int rockchip_copy_image(int fd, struct image_tool_params *mparams);
+-  *  b) we need a API call to get the respective section symbols */
+- #if defined(__MACH__)
+- #include <mach-o/getsect.h>
+-+#include <mach-o/dyld.h>
+- 
+- #define INIT_SECTION(name)  do {					\
+- 		unsigned long name ## _len;				\
+--		char *__cat(pstart_, name) = getsectdata("__TEXT",	\
+-+		char *__cat(pstart_, name) = getsectdata("__DATA",	\
+- 			#name, &__cat(name, _len));			\
+-+			__cat(pstart_, name) +=				\
+-+				_dyld_get_image_vmaddr_slide(0);	\
+- 		char *__cat(pstop_, name) = __cat(pstart_, name) +	\
+- 			__cat(name, _len);				\
+- 		__cat(__start_, name) = (void *)__cat(pstart_, name);	\
+- 		__cat(__stop_, name) = (void *)__cat(pstop_, name);	\
+- 	} while (0)
+--#define SECTION(name)   __attribute__((section("__TEXT, " #name)))
+-+#define SECTION(name)   __attribute__((section("__DATA, " #name)))
+- 
+- struct image_type_params **__start_image_type, **__stop_image_type;
+- #else
+--- /dev/null
++++ b/tools/mkimage/patches/095-tools-disable-TOOLS_FIT_FULL_CHECK.patch
+@@ -0,0 +1,24 @@
++From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
++From: Huangbin Zhan <zhanhb88@gmail.com>
++Date: Fri, 18 Feb 2022 14:19:23 +0800
++Subject: [PATCH] tools: disable TOOLS_FIT_FULL_CHECK
++
++	U-Boot disallows unit addresses by default. Disable TOOLS_FIT_FULL_CHECK
++	to allow at symbol in node names.
++
++https://github.com/openwrt/openwrt/commits/master/scripts/mkits.sh
++https://github.com/u-boot/u-boot/commit/3f04db891a353f4b127ed57279279f851c6b4917
++---
++ tools/Kconfig | 2 +-
++ 1 file changed, 1 insertion(+), 1 deletion(-)
++
++--- a/tools/Kconfig
+++++ b/tools/Kconfig
++@@ -31,7 +31,7 @@ config TOOLS_FIT
++ 	  Enable FIT support in the tools builds.
++
++ config TOOLS_FIT_FULL_CHECK
++-	def_bool y
+++	bool "Do a full check of the FIT"
++ 	help
++ 	  Do a full check of the FIT before using it in the tools builds
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0913-sbc-add-offline-sign-support.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0913-sbc-add-offline-sign-support.patch
new file mode 100644
index 0000000..191eb53
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0913-sbc-add-offline-sign-support.patch
@@ -0,0 +1,428 @@
+--- a/include/image.mk
++++ b/include/image.mk
+@@ -443,7 +443,10 @@ endif
+ ROOTFS_ENCRYPT = $(if $(ROE_KEY_DIR),$(wildcard $(ROE_KEY_DIR)/$(ROE_KEY_NAME).key),)
+ 
+ DEVICE_CHECK_FIT_KEY = $(if $(wildcard $(FIT_KEY_DIR)/$(FIT_KEY_NAME).key),install-images,install-disabled)
+-DEVICE_CHECK_FIT_DIR = $(if $(FIT_KEY_DIR),$(DEVICE_CHECK_FIT_KEY),install-images)
++DEVICE_CHECK_FIT_DIR = $(if $(FIT_KEY_DIR),$(DEVICE_CHECK_FIT_KEY),install-disabled)
++
++DEVICE_CHECK_OFFLINE_FIT_KEY = $(if $(filter offline%,$(FIT_KEY_NAME)),install-images,$(DEVICE_CHECK_FIT_DIR))
++DEVICE_CHECK_FIT_KEY_NAME = $(if $(FIT_KEY_NAME),$(DEVICE_CHECK_OFFLINE_FIT_KEY),install-images)
+ 
+ DEVICE_EXTRA_PACKAGES = $(call qstrip,$(CONFIG_TARGET_DEVICE_PACKAGES_$(call target_conf,$(BOARD)$(if $(SUBTARGET),_$(SUBTARGET)))_DEVICE_$(1)))
+ 
+@@ -467,7 +470,7 @@ endef
+ define Device/Check
+   $(Device/Check/Common)
+   KDIR_KERNEL_IMAGE := $(KDIR)/$(1)$$(KERNEL_SUFFIX)
+-  _TARGET := $$(if $$(_PROFILE_SET),$$(DEVICE_CHECK_FIT_DIR),install-disabled)
++  _TARGET := $$(if $$(_PROFILE_SET),$$(DEVICE_CHECK_FIT_KEY_NAME),install-disabled)
+   ifndef IB
+     _COMPILE_TARGET := $$(if $(CONFIG_IB)$$(_PROFILE_SET),compile,compile-disabled)
+   endif
+--- a/scripts/mkits.sh
++++ b/scripts/mkits.sh
+@@ -164,11 +164,17 @@ fi
+ 
+ # Conditionally create signature information
+ if [ -n "${KEY_NAME_HINT}" ]; then
++	if [[ "${KEY_NAME_HINT}" == "offline,"* ]]; then
++		KEY_NAME_HINT=$(echo -n "${KEY_NAME_HINT}" | sed "s/^.*[,]//g")
++		SIGN_OFFLINE="
++				sign-offline = <1>;"
++	fi
+ 	SIGNATURE="\
+ 			signature {
+ 				algo = \"sha1,rsa2048\";
+ 				key-name-hint = \"${KEY_NAME_HINT}\";
+ ${SIGN_IMAGES}
++${SIGN_OFFLINE}
+ 			};\
+ "
+ fi
+--- /dev/null
++++ b/tools/mkimage/patches/901-mtk-mkimage-add-offline-sign-support.patch
+@@ -0,0 +1,382 @@
++--- a/tools/image-host.c
+++++ b/tools/image-host.c
++@@ -168,6 +168,9 @@ static int fit_image_setup_sig(struct im
++ {
++ 	const char *node_name;
++ 	const char *padding_name;
+++	const char *offline;
+++	const char offline_suffix[] = ",offline";
+++	char *offline_algo_name = NULL;
++ 
++ 	node_name = fit_get_name(fit, noffset, NULL);
++ 	if (!algo_name) {
++@@ -188,7 +191,21 @@ static int fit_image_setup_sig(struct im
++ 	info->node_offset = noffset;
++ 	info->name = strdup(algo_name);
++ 	info->checksum = image_get_checksum_algo(algo_name);
++-	info->crypto = image_get_crypto_algo(algo_name);
+++
+++	offline = fdt_getprop(fit, noffset, "sign-offline", NULL);
+++	if (offline) {
+++		offline_algo_name = calloc(strlen(algo_name) + strlen(offline_suffix) + 1, sizeof(char));
+++		if (!offline_algo_name)
+++			return -ENOMEM;
+++
+++		strcpy(offline_algo_name, algo_name);
+++		strcat(offline_algo_name, offline_suffix);
+++
+++		info->crypto = image_get_crypto_algo(offline_algo_name);
+++	} else {
+++		info->crypto = image_get_crypto_algo(algo_name);
+++	}
+++
++ 	info->padding = image_get_padding_algo(padding_name);
++ 	info->require_keys = require_keys;
++ 	info->engine_id = engine_id;
++--- a/tools/image-sig-host.c
+++++ b/tools/image-sig-host.c
++@@ -11,6 +11,7 @@
++ #include <u-boot/ecdsa.h>
++ #include <u-boot/rsa.h>
++ #include <u-boot/hash-checksum.h>
+++#include "signoffline.h"
++ 
++ struct checksum_algo checksum_algos[] = {
++ 	{
++@@ -76,6 +77,27 @@ struct crypto_algo crypto_algos[] = {
++ 		.add_verify_data = ecdsa_add_verify_data,
++ 		.verify = ecdsa_verify,
++ 	},
+++	{
+++		.name = "rsa2048,offline",
+++		.key_len = RSA2048_BYTES,
+++		.sign = offline_sign,
+++		.add_verify_data = rsa_add_verify_data,
+++		.verify = offline_verify,
+++	},
+++	{
+++		.name = "rsa3072,offline",
+++		.key_len = RSA3072_BYTES,
+++		.sign = offline_sign,
+++		.add_verify_data = rsa_add_verify_data,
+++		.verify = offline_verify,
+++	},
+++	{
+++		.name = "rsa4096,offline",
+++		.key_len = RSA4096_BYTES,
+++		.sign = offline_sign,
+++		.add_verify_data = rsa_add_verify_data,
+++		.verify = offline_verify,
+++	},
++ };
++ 
++ struct padding_algo padding_algos[] = {
++--- a/tools/Makefile
+++++ b/tools/Makefile
++@@ -89,6 +89,8 @@ RSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(
++ 					rsa-sign.o rsa-verify.o \
++ 					rsa-mod-exp.o)
++ 
+++SIGNOFFLINE_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := signoffline.o
+++
++ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.o)
++ 
++ AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
++@@ -149,7 +151,8 @@ dumpimage-mkimage-objs := aisimage.o \
++ 			mtk_image.o \
++ 			$(ECDSA_OBJS-y) \
++ 			$(RSA_OBJS-y) \
++-			$(AES_OBJS-y)
+++			$(AES_OBJS-y) \
+++			$(SIGNOFFLINE_OBJS-y)
++ 
++ dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o
++ mkimage-objs   := $(dumpimage-mkimage-objs) mkimage.o
++--- /dev/null
+++++ b/tools/signoffline.h
++@@ -0,0 +1,18 @@
+++// SPDX-License-Identifier: GPL-2.0+
+++/*
+++ * Copyright (C) 2022 MediaTek Incorporation. All Rights Reserved.
+++ *
+++ */
+++
+++#ifndef SIGNOFFLINE_H_
+++#define SIGNOFFLINE_H_
+++
+++int offline_sign(struct image_sign_info *info,
+++		 const struct image_region region[], int region_count,
+++		 uint8_t **sigp, uint *sig_len);
+++
+++int offline_verify(struct image_sign_info *info,
+++		   const struct image_region region[], int region_count,
+++		   uint8_t *sig, uint sig_len);
+++
+++#endif /* SIGNOFFLINE_H_ */
++--- /dev/null
+++++ b/tools/signoffline.c
++@@ -0,0 +1,264 @@
+++// SPDX-License-Identifier: GPL-2.0+
+++/*
+++ * Copyright (C) 2022 MediaTek Incorporation. All Rights Reserved.
+++ *
+++ */
+++#include <stdio.h>
+++#include <stdint.h>
+++#include <image.h>
+++#include <linux/libfdt.h>
+++#include <malloc.h>
+++#include <u-boot/sha512.h>
+++#include "mkimage.h"
+++
+++#define OFFSIGN_MSG_FILE		".msg"
+++#define OFFSIGN_SIG_FILE		".sig"
+++#define OFFSIGN_MAX_TMPFILE_LEN		256
+++#define OFFSIGN_MAX_CMDLINE_LEN		3 * OFFSIGN_MAX_TMPFILE_LEN + 128 + 1
+++#define OPENSSL_PKEYUTL_CMD		"openssl pkeyutl"
+++#define OPENSSL_PKEYUTL_OPER		"-sign"
+++#define OPENSSL_PKEYUTL_PADDING		"-pkeyopt rsa_padding_mode"
+++#define OPENSSL_PKEYUTL_SALT		"-pkeyopt rsa_pss_saltlen:32"
+++
+++static char img_prefix[OFFSIGN_MAX_TMPFILE_LEN];
+++
+++static int get_fit_identifier(const void *fit, char *img_prefix)
+++{
+++	int ret = 0;
+++	int noffset = 0;
+++	int img_noffset = 0;
+++	int len = 0;
+++	const char *prop = NULL;
+++	char *p = NULL, *end_p = NULL;
+++
+++	img_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+++	if (img_noffset < 0) {
+++		fprintf(stderr, "Can't find image parent node: %s\n", FIT_IMAGES_PATH);
+++		return img_noffset;
+++	}
+++
+++	noffset = fdt_subnode_offset(fit, img_noffset, "fdt-1");
+++	if (noffset < 0) {
+++		fprintf(stderr, "Can't find fdt-1 node in %s\n", FIT_IMAGES_PATH);
+++		return noffset;
+++	}
+++
+++	prop = fdt_getprop(fit, noffset, FIT_DESC_PROP, &len);
+++	if (!prop) {
+++		fprintf(stderr, "Can't find %s property in node: fdt-1\n", FIT_DESC_PROP);
+++		return -EINVAL;
+++	}
+++
+++	/* find FIT image name as tmpfile prefix */
+++	p = strstr(prop, "OpenWrt ");
+++	if (!p)
+++		return -EINVAL;
+++
+++	p = strchr(p, ' ');
+++	if (!p)
+++		return -EINVAL;
+++	p += 1;
+++
+++	end_p = strchr(p, ' ');
+++	if (!end_p)
+++		return -EINVAL;
+++
+++	if ((end_p - p) >= OFFSIGN_MAX_TMPFILE_LEN)
+++		return -EINVAL;
+++
+++	strncpy(img_prefix, p, end_p - p);
+++
+++	return ret;
+++}
+++
+++static int prepare_offline_sign(struct image_sign_info *info,
+++				const struct image_region region[],
+++				int region_count)
+++{
+++	int ret = 0;
+++	size_t len = 0;
+++	FILE *f = NULL;
+++	char msg_file[OFFSIGN_MAX_TMPFILE_LEN] = {0};
+++	uint8_t checksum[SHA512_SUM_LEN] = {0};
+++
+++	ret = get_fit_identifier(info->fit, img_prefix);
+++	if (ret)
+++		return ret;
+++
+++	len = snprintf(msg_file, sizeof(msg_file),
+++		       "%s/%s%s", info->keydir, img_prefix, OFFSIGN_MSG_FILE);
+++	if (len < 0)
+++		return -EINVAL;
+++
+++	/* calculate digest */
+++	ret = info->checksum->calculate(info->checksum->name, region,
+++					region_count, checksum);
+++	if (ret) {
+++		fprintf(stderr, "Failed to calculate checksum of regions\n");
+++		return ret;
+++	}
+++
+++	/* write message to be signed to msg_file */
+++	f = fopen(msg_file, "w");
+++	if (!f) {
+++		fprintf(stderr, "Failed to open %s\n", msg_file);
+++		return -EINVAL;
+++	}
+++
+++	len = fwrite(checksum, sizeof(uint8_t),
+++		     info->checksum->checksum_len, f);
+++	if (len < 0) {
+++		fprintf(stderr, "Failed to write to %s\n", msg_file);
+++		ret = -EINVAL;
+++	}
+++
+++	fclose(f);
+++
+++	return ret;
+++}
+++
+++static int sign_offline(struct image_sign_info *info,
+++			const struct image_region region[],
+++			int region_count,
+++			uint8_t **sigp, uint *sig_len)
+++{
+++	int ret = 0;
+++	size_t len = 0;
+++	char cmd[OFFSIGN_MAX_CMDLINE_LEN] = {0};
+++	char padding[8] = {0};
+++
+++	/* check padding */
+++	if (info->padding && !strcmp(info->padding->name, "pss"))
+++		strncpy(padding, "pss", sizeof(padding));
+++	else
+++		strncpy(padding, "pkcs1", sizeof(padding));
+++
+++	len = snprintf(cmd, sizeof(cmd),
+++		"%s %s -in %s/%s%s -inkey %s/%s.key -out %s/%s%s -pkeyopt digest:%s %s:%s",
+++								OPENSSL_PKEYUTL_CMD,
+++								OPENSSL_PKEYUTL_OPER,
+++								info->keydir,
+++								img_prefix,
+++								OFFSIGN_MSG_FILE,
+++								info->keydir,
+++								info->keyname,
+++								info->keydir,
+++								img_prefix,
+++								OFFSIGN_SIG_FILE,
+++								info->checksum->name,
+++								OPENSSL_PKEYUTL_PADDING,
+++								padding);
+++	if (len < 0)
+++		return -EINVAL;
+++
+++	if (!strcmp(padding, "pss")) {
+++		len = snprintf(cmd + len, sizeof(cmd), "%s", OPENSSL_PKEYUTL_SALT);
+++		if (len < 0)
+++			return -EINVAL;
+++	}
+++
+++	printf("%s\n", cmd);
+++
+++	/* execute openssl command */
+++	if (system(cmd) == -1) {
+++		fprintf(stderr,"%s: failed to sign FIT\n", OPENSSL_PKEYUTL_CMD);
+++		return -EINVAL;
+++	}
+++
+++	return ret;
+++}
+++
+++static int post_offline_sign(struct image_sign_info *info,
+++			     uint8_t **sigp, uint *sig_len)
+++{
+++	int ret = 0;
+++	FILE *f = NULL;
+++	size_t len = 0;
+++	void *sig = NULL;
+++	char sig_file[OFFSIGN_MAX_TMPFILE_LEN] = {0};
+++
+++	len = snprintf(sig_file, sizeof(sig_file),
+++		       "%s/%s%s", info->keydir, img_prefix, OFFSIGN_SIG_FILE);
+++	if (len < 0)
+++		return -EINVAL;
+++
+++	/* read signature from sig_file */
+++	f = fopen(sig_file, "rb");
+++	if (!f) {
+++		fprintf(stderr, "Failed to open %s\n", sig_file);
+++		return -EINVAL;
+++	}
+++
+++	sig = calloc(info->crypto->key_len, sizeof(uint8_t));
+++	if (!sig) {
+++		ret = -ENOMEM;
+++		goto post_offline_sign_err;
+++	}
+++
+++	len = fread(sig, sizeof(uint8_t), info->crypto->key_len, f);
+++	if (len < 0) {
+++		fprintf(stderr, "Failed to read from %s\n", sig_file);
+++		ret = -EINVAL;
+++		goto post_offline_sign_err;
+++	}
+++
+++	if (len != info->crypto->key_len) {
+++		fprintf(stderr, "Signature length is invalid\n");
+++		ret = -EINVAL;
+++		goto post_offline_sign_err;
+++	}
+++
+++	fclose(f);
+++
+++	*sigp = sig;
+++	*sig_len= info->crypto->key_len;
+++
+++	return 0;
+++
+++post_offline_sign_err:
+++	fclose(f);
+++	if (sig)
+++		free(sig);
+++
+++	return ret;
+++}
+++
+++int offline_sign(struct image_sign_info *info,
+++		 const struct image_region region[], int region_count,
+++		 uint8_t **sigp, uint *sig_len)
+++{
+++	int ret = 0;
+++
+++	printf("%s:\n", __func__);
+++
+++	ret = prepare_offline_sign(info, region, region_count);
+++	if (ret) {
+++		fprintf(stderr, "prepare_offline_sign() failed\n");
+++		return -EINVAL;
+++	}
+++
+++	ret = sign_offline(info, region, region_count, sigp, sig_len);
+++	if (ret) {
+++		fprintf(stderr, "sign_offline() failed\n");
+++		return -EINVAL;
+++	}
+++
+++	ret = post_offline_sign(info, sigp, sig_len);
+++	if (ret) {
+++		fprintf(stderr, "post_offline_sign() failed\n");
+++		return -EINVAL;
+++	}
+++
+++	return ret;
+++}
+++
+++int offline_verify(struct image_sign_info *info,
+++		   const struct image_region region[], int region_count,
+++		   uint8_t *sig, uint sig_len)
+++{
+++	int ret = 0;
+++
+++	printf("%s:\n", __func__);
+++
+++	return ret;
+++}
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0914-sbc-add-key-algo-option.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0914-sbc-add-key-algo-option.patch
new file mode 100644
index 0000000..9694e8a
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0914-sbc-add-key-algo-option.patch
@@ -0,0 +1,65 @@
+--- a/scripts/mkits.sh
++++ b/scripts/mkits.sh
+@@ -17,7 +17,7 @@
+ usage() {
+ 	printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")"
+ 	printf " -v version -k kernel [-D name -n address -d dtb] -o its_file"
+-	printf " [-s script] [-S key_name_hint] [-r ar_ver] [-R rootfs] [-m rfsk]"
++	printf " [-s script] [-S key_name_hint] [-b key_alg] [-r ar_ver] [-R rootfs] [-m rfsk]"
+ 
+ 	printf "\n\t-A ==> set architecture to 'arch'"
+ 	printf "\n\t-C ==> set compression type 'comp'"
+@@ -32,6 +32,7 @@ usage() {
+ 	printf "\n\t-o ==> create output file 'its_file'"
+ 	printf "\n\t-s ==> include u-boot script 'script'"
+ 	printf "\n\t-S ==> add signature at configurations and assign its key_name_hint by 'key_name_hint'"
++	printf "\n\t-b ==> set key algorithm"
+ 	printf "\n\t-r ==> set anti-rollback version to 'fw_ar_ver' (dec)"
+ 	printf "\n\t-R ==> specify rootfs file for embedding hash"
+ 	printf "\n\t-m ==> include encrypted rootfs key'\n"
+@@ -40,11 +41,12 @@ usage() {
+ 
+ FDTNUM=1
+ 
+-while getopts ":A:a:c:C:D:d:e:k:n:o:v:s:S:r:R:m:" OPTION
++while getopts ":A:a:b:c:C:D:d:e:k:n:o:v:s:S:r:R:m:" OPTION
+ do
+ 	case $OPTION in
+ 		A ) ARCH=$OPTARG;;
+ 		a ) LOAD_ADDR=$OPTARG;;
++		b ) KEY_ALG=$OPTARG;;
+ 		c ) CONFIG=$OPTARG;;
+ 		C ) COMPRESS=$OPTARG;;
+ 		D ) DEVICE=$OPTARG;;
+@@ -169,9 +171,12 @@ if [ -n "${KEY_NAME_HINT}" ]; then
+ 		SIGN_OFFLINE="
+ 				sign-offline = <1>;"
+ 	fi
++	if [ -z "${KEY_ALG}" ]; then
++		KEY_ALG="sha256,rsa2048"
++	fi
+ 	SIGNATURE="\
+ 			signature {
+-				algo = \"sha1,rsa2048\";
++				algo = \"${KEY_ALG}\";
+ 				key-name-hint = \"${KEY_NAME_HINT}\";
+ ${SIGN_IMAGES}
+ ${SIGN_OFFLINE}
+--- a/target/linux/mediatek/image/Makefile
++++ b/target/linux/mediatek/image/Makefile
+@@ -99,6 +99,7 @@ define Build/fit-sign
+ 		-A $(LINUX_KARCH) \
+ 		-v $(LINUX_VERSION) \
+ 		$(if $(FIT_KEY_NAME),-S $(FIT_KEY_NAME)) \
++		$(if $(FIT_KEY_ALG),-b $(FIT_KEY_ALG)) \
+ 		$(if $(FW_AR_VER),-r $(FW_AR_VER)) \
+ 		$(if $(CONFIG_TARGET_ROOTFS_SQUASHFS), \
+ 			$(if $(ROOTFS_ENCRYPT), \
+@@ -129,6 +130,7 @@ define Device/Default
+ 	pad-rootfs | append-metadata
+   FIT_KEY_DIR :=
+   FIT_KEY_NAME :=
++  FIT_KEY_ALG :=
+   ROE_KEY_DIR :=
+   ROE_KEY_NAME :=
+ endef
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0915-append-opteenode-in-kernel-dtb.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0915-append-opteenode-in-kernel-dtb.patch
new file mode 100644
index 0000000..c07f99a
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/0915-append-opteenode-in-kernel-dtb.patch
@@ -0,0 +1,27 @@
+--- a/target/linux/mediatek/image/Makefile
++++ b/target/linux/mediatek/image/Makefile
+@@ -86,6 +86,24 @@ define Build/rootfs-encrypt
+ 	$(if $(ROOTFS_ENCRYPT),$(call Build/squashfs-encrypt))
+ endef
+ 
++define append-opteenode
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) "/reserved-memory/secmon@43000000" -tx reg 0 0x43000000 0 0x11f0000
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) /firmware -cp
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) /firmware/optee -cp
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) /firmware/optee -ts compatible "linaro,optee-tz"
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) /firmware/optee -ts method "smc"
++	LD_LIBRARY_PATH=$(STAGING_DIR_HOST)/lib \
++	fdtput $(1) /firmware/optee -ts status "okay"
++endef
++
++define Build/append-opteenode
++	$(if $(CONFIG_PACKAGE_optee-mediatek),$(call append-opteenode, $(1)))
++endef
+ # build signed fit
+ define Build/fit-sign
+ 	$(TOPDIR)/scripts/mkits.sh \
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8000-uboot-mediatek-makefile.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8000-uboot-mediatek-makefile.patch
new file mode 100644
index 0000000..1727ad6
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8000-uboot-mediatek-makefile.patch
@@ -0,0 +1,43 @@
+diff --git a/package/boot/uboot-mediatek/Makefile b/package/boot/uboot-mediatek/Makefile
+index c46b906..63610fb 100644
+--- a/package/boot/uboot-mediatek/Makefile
++++ b/package/boot/uboot-mediatek/Makefile
+@@ -1,8 +1,12 @@
+ include $(TOPDIR)/rules.mk
+ include $(INCLUDE_DIR)/kernel.mk
+ 
+-PKG_VERSION:=2020.10
+-PKG_HASH:=0d481bbdc05c0ee74908ec2f56a6daa53166cc6a78a0e4fac2ac5d025770a622
++PKG_NAME:=u-boot-mtk
++PKG_VERSION:=2021.01
++PKG_RELEASE:=1
++PKG_SOURCE_PROTO:=git
++PKG_SOURCE_URL:=https://gerrit.mediatek.inc/gateway/bootloader/Uboot-upstream
++PKG_SOURCE_VERSION:=icb-rebb-main
+ 
+ include $(INCLUDE_DIR)/u-boot.mk
+ include $(INCLUDE_DIR)/package.mk
+@@ -17,6 +21,7 @@ endef
+ define U-Boot/mt7622
+   NAME:=MT7622
+   BUILD_SUBTARGET:=mt7622
++  UBOOT_IMAGE:=u-boot.bin
+   UBOOT_CONFIG:=mt7622_rfb
+ endef
+ 
+@@ -39,7 +44,14 @@ define U-Boot/mt7629
+   UBOOT_CONFIG:=mt7629_rfb
+ endef
+ 
+-UBOOT_TARGETS := mt7629 mt7622 mt7623n_bpir2 mt7623a_unielec_u7623
++define U-Boot/mt7986
++  NAME:=MT7986
++  BUILD_SUBTARGET:=mt7986
++  UBOOT_IMAGE:=u-boot.bin
++  UBOOT_CONFIG:=mt7986_fpga
++endef
++
++UBOOT_TARGETS := mt7629 mt7622 mt7623n_bpir2 mt7623a_unielec_u7623 mt7986
+ 
+ UBOOT_MAKE_FLAGS += $(UBOOT_IMAGE)
+ 
diff --git a/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8001-uboot-mediatek-remove-old-nand-driver.patch b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8001-uboot-mediatek-remove-old-nand-driver.patch
new file mode 100644
index 0000000..8642159
--- /dev/null
+++ b/autobuild_mac80211_release/openwrt_patches-21.02/mtk_soc/8001-uboot-mediatek-remove-old-nand-driver.patch
@@ -0,0 +1,8809 @@
+diff --git a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch b/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch
+deleted file mode 100644
+index dc3ebaf..0000000
+--- a/package/boot/uboot-mediatek/patches/002-nand-add-spi-nand-driver.patch
++++ /dev/null
+@@ -1,8659 +0,0 @@
+-From de8b6cf615be20b25d0f3c817866de2c0d46a704 Mon Sep 17 00:00:00 2001
+-From: Sam Shih <sam.shih@mediatek.com>
+-Date: Mon, 20 Apr 2020 17:10:05 +0800
+-Subject: [PATCH 1/3] nand: add spi nand driver
+-
+-Add spi nand driver support for mt7622 based on nfi controller
+-
+-Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+----
+- drivers/mtd/Kconfig                           |    7 +
+- drivers/mtd/Makefile                          |    4 +
+- drivers/mtd/nand/raw/nand.c                   |    2 +
+- drivers/mtd/nandx/NOTICE                      |   52 +
+- drivers/mtd/nandx/Nandx.config                |   17 +
+- drivers/mtd/nandx/Nandx.mk                    |   91 ++
+- drivers/mtd/nandx/README                      |   31 +
+- drivers/mtd/nandx/core/Nandx.mk               |   38 +
+- drivers/mtd/nandx/core/core_io.c              |  735 +++++++++
+- drivers/mtd/nandx/core/core_io.h              |   39 +
+- drivers/mtd/nandx/core/nand/device_spi.c      |  200 +++
+- drivers/mtd/nandx/core/nand/device_spi.h      |  132 ++
+- drivers/mtd/nandx/core/nand/nand_spi.c        |  526 +++++++
+- drivers/mtd/nandx/core/nand/nand_spi.h        |   35 +
+- drivers/mtd/nandx/core/nand_base.c            |  304 ++++
+- drivers/mtd/nandx/core/nand_base.h            |   71 +
+- drivers/mtd/nandx/core/nand_chip.c            |  272 ++++
+- drivers/mtd/nandx/core/nand_chip.h            |  103 ++
+- drivers/mtd/nandx/core/nand_device.c          |  285 ++++
+- drivers/mtd/nandx/core/nand_device.h          |  608 ++++++++
+- drivers/mtd/nandx/core/nfi.h                  |   51 +
+- drivers/mtd/nandx/core/nfi/nfi_base.c         | 1357 +++++++++++++++++
+- drivers/mtd/nandx/core/nfi/nfi_base.h         |   95 ++
+- drivers/mtd/nandx/core/nfi/nfi_regs.h         |  114 ++
+- drivers/mtd/nandx/core/nfi/nfi_spi.c          |  689 +++++++++
+- drivers/mtd/nandx/core/nfi/nfi_spi.h          |   44 +
+- drivers/mtd/nandx/core/nfi/nfi_spi_regs.h     |   64 +
+- drivers/mtd/nandx/core/nfi/nfiecc.c           |  510 +++++++
+- drivers/mtd/nandx/core/nfi/nfiecc.h           |   90 ++
+- drivers/mtd/nandx/core/nfi/nfiecc_regs.h      |   51 +
+- drivers/mtd/nandx/driver/Nandx.mk             |   18 +
+- drivers/mtd/nandx/driver/bbt/bbt.c            |  408 +++++
+- drivers/mtd/nandx/driver/uboot/driver.c       |  574 +++++++
+- drivers/mtd/nandx/include/Nandx.mk            |   16 +
+- drivers/mtd/nandx/include/internal/bbt.h      |   62 +
+- .../mtd/nandx/include/internal/nandx_core.h   |  250 +++
+- .../mtd/nandx/include/internal/nandx_errno.h  |   40 +
+- .../mtd/nandx/include/internal/nandx_util.h   |  221 +++
+- drivers/mtd/nandx/include/uboot/nandx_os.h    |   78 +
+- include/configs/mt7622.h                      |   25 +
+- 40 files changed, 8309 insertions(+)
+- create mode 100644 drivers/mtd/nandx/NOTICE
+- create mode 100644 drivers/mtd/nandx/Nandx.config
+- create mode 100644 drivers/mtd/nandx/Nandx.mk
+- create mode 100644 drivers/mtd/nandx/README
+- create mode 100644 drivers/mtd/nandx/core/Nandx.mk
+- create mode 100644 drivers/mtd/nandx/core/core_io.c
+- create mode 100644 drivers/mtd/nandx/core/core_io.h
+- create mode 100644 drivers/mtd/nandx/core/nand/device_spi.c
+- create mode 100644 drivers/mtd/nandx/core/nand/device_spi.h
+- create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.c
+- create mode 100644 drivers/mtd/nandx/core/nand/nand_spi.h
+- create mode 100644 drivers/mtd/nandx/core/nand_base.c
+- create mode 100644 drivers/mtd/nandx/core/nand_base.h
+- create mode 100644 drivers/mtd/nandx/core/nand_chip.c
+- create mode 100644 drivers/mtd/nandx/core/nand_chip.h
+- create mode 100644 drivers/mtd/nandx/core/nand_device.c
+- create mode 100644 drivers/mtd/nandx/core/nand_device.h
+- create mode 100644 drivers/mtd/nandx/core/nfi.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.c
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_base.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_regs.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.c
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfi_spi_regs.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.c
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc.h
+- create mode 100644 drivers/mtd/nandx/core/nfi/nfiecc_regs.h
+- create mode 100644 drivers/mtd/nandx/driver/Nandx.mk
+- create mode 100644 drivers/mtd/nandx/driver/bbt/bbt.c
+- create mode 100644 drivers/mtd/nandx/driver/uboot/driver.c
+- create mode 100644 drivers/mtd/nandx/include/Nandx.mk
+- create mode 100644 drivers/mtd/nandx/include/internal/bbt.h
+- create mode 100644 drivers/mtd/nandx/include/internal/nandx_core.h
+- create mode 100644 drivers/mtd/nandx/include/internal/nandx_errno.h
+- create mode 100644 drivers/mtd/nandx/include/internal/nandx_util.h
+- create mode 100644 drivers/mtd/nandx/include/uboot/nandx_os.h
+-
+-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
+-+