From: John Crispin <john@openwrt.org>
Date: Fri, 1 Apr 2016 07:11:18 +0000 (+0000)
Subject: mediatek: update patches
X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=41ba4b04c84a80a441b30e167eab0d45d3b6c009;p=openwrt%2Fstaging%2Frobimarko.git

mediatek: update patches

add fixes for
* ethernet
* cpufreq
* nand
* a7-timer

Signed-off-by: John Crispin <blogic@openwrt.org>

SVN-Revision: 49098
---

diff --git a/target/linux/mediatek/config-4.4 b/target/linux/mediatek/config-4.4
index f9d5d06765..4ebfe4d4a5 100644
--- a/target/linux/mediatek/config-4.4
+++ b/target/linux/mediatek/config-4.4
@@ -27,6 +27,8 @@ CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
 CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
 CONFIG_ARM=y
 CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ARCH_TIMER=y
+CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y
 # CONFIG_ARM_ATAG_DTB_COMPAT is not set
 CONFIG_ARM_CPU_SUSPEND=y
 # CONFIG_ARM_CPU_TOPOLOGY is not set
@@ -35,6 +37,7 @@ CONFIG_ARM_HAS_SG_CHAIN=y
 CONFIG_ARM_L1_CACHE_SHIFT=6
 CONFIG_ARM_L1_CACHE_SHIFT_6=y
 # CONFIG_ARM_LPAE is not set
+CONFIG_ARM_MT7623_CPUFREQ=y
 CONFIG_ARM_PATCH_PHYS_VIRT=y
 # CONFIG_ARM_SMMU is not set
 CONFIG_ARM_THUMB=y
@@ -65,6 +68,7 @@ CONFIG_COMMON_CLK_MT2701=y
 # CONFIG_COMMON_CLK_MT8173 is not set
 CONFIG_COMPACTION=y
 CONFIG_COREDUMP=y
+# CONFIG_CPUFREQ_DT is not set
 CONFIG_CPU_32v6K=y
 CONFIG_CPU_32v7=y
 CONFIG_CPU_ABRT_EV7=y
@@ -74,11 +78,21 @@ CONFIG_CPU_CACHE_VIPT=y
 CONFIG_CPU_COPY_V6=y
 CONFIG_CPU_CP15=y
 CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
 CONFIG_CPU_HAS_ASID=y
 # CONFIG_CPU_ICACHE_DISABLE is not set
 CONFIG_CPU_PABRT_V7=y
 CONFIG_CPU_PM=y
 CONFIG_CPU_RMAP=y
+# CONFIG_CPU_THERMAL is not set
 CONFIG_CPU_TLB_V7=y
 CONFIG_CPU_V7=y
 CONFIG_CRC16=y
@@ -168,6 +182,7 @@ CONFIG_HAVE_ARCH_KGDB=y
 CONFIG_HAVE_ARCH_PFN_VALID=y
 CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
 CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_ARCH_TIMER=y
 # CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
 CONFIG_HAVE_BPF_JIT=y
 CONFIG_HAVE_CC_STACKPROTECTOR=y
@@ -375,6 +390,7 @@ CONFIG_PM_CLK=y
 CONFIG_PM_GENERIC_DOMAINS=y
 CONFIG_PM_GENERIC_DOMAINS_OF=y
 CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
+CONFIG_PM_OPP=y
 CONFIG_PM_SLEEP=y
 CONFIG_PM_SLEEP_SMP=y
 CONFIG_POWER_RESET=y
diff --git a/target/linux/mediatek/patches-4.4/0001-NET-multi-phy-support.patch b/target/linux/mediatek/patches-4.4/0001-NET-multi-phy-support.patch
index 239e434dbb..007c03c688 100644
--- a/target/linux/mediatek/patches-4.4/0001-NET-multi-phy-support.patch
+++ b/target/linux/mediatek/patches-4.4/0001-NET-multi-phy-support.patch
@@ -1,7 +1,7 @@
 From c30a296646a42302065ba452abe95b0b4b550883 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Sun, 27 Jul 2014 09:38:50 +0100
-Subject: [PATCH 01/66] NET: multi phy support
+Subject: [PATCH 01/78] NET: multi phy support
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
diff --git a/target/linux/mediatek/patches-4.4/0002-soc-mediatek-Separate-scpsys-driver-common-code.patch b/target/linux/mediatek/patches-4.4/0002-soc-mediatek-Separate-scpsys-driver-common-code.patch
index f99496ae62..b0e4d29931 100644
--- a/target/linux/mediatek/patches-4.4/0002-soc-mediatek-Separate-scpsys-driver-common-code.patch
+++ b/target/linux/mediatek/patches-4.4/0002-soc-mediatek-Separate-scpsys-driver-common-code.patch
@@ -1,7 +1,7 @@
 From 2c93328ed05061a50e3bd4111379dbcf6946d3ac Mon Sep 17 00:00:00 2001
 From: James Liao <jamesjj.liao@mediatek.com>
 Date: Wed, 30 Dec 2015 14:41:43 +0800
-Subject: [PATCH 02/66] soc: mediatek: Separate scpsys driver common code
+Subject: [PATCH 02/78] soc: mediatek: Separate scpsys driver common code
 
 Separate scpsys driver common code to mtk-scpsys.c, and move MT8173
 platform code to mtk-scpsys-mt8173.c.
diff --git a/target/linux/mediatek/patches-4.4/0003-soc-mediatek-Init-MT8173-scpsys-driver-earlier.patch b/target/linux/mediatek/patches-4.4/0003-soc-mediatek-Init-MT8173-scpsys-driver-earlier.patch
index 0fc766cada..7d77391cc5 100644
--- a/target/linux/mediatek/patches-4.4/0003-soc-mediatek-Init-MT8173-scpsys-driver-earlier.patch
+++ b/target/linux/mediatek/patches-4.4/0003-soc-mediatek-Init-MT8173-scpsys-driver-earlier.patch
@@ -1,7 +1,7 @@
 From c359272f86805259c5801385d60fdeea9d629cf9 Mon Sep 17 00:00:00 2001
 From: James Liao <jamesjj.liao@mediatek.com>
 Date: Wed, 30 Dec 2015 14:41:44 +0800
-Subject: [PATCH 03/66] soc: mediatek: Init MT8173 scpsys driver earlier
+Subject: [PATCH 03/78] soc: mediatek: Init MT8173 scpsys driver earlier
 
 Some power domain comsumers may init before module_init.
 So the power domain provider (scpsys) need to be initialized
diff --git a/target/linux/mediatek/patches-4.4/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch b/target/linux/mediatek/patches-4.4/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
index 1b45c44161..c0bb55bf9a 100644
--- a/target/linux/mediatek/patches-4.4/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
+++ b/target/linux/mediatek/patches-4.4/0004-soc-mediatek-Add-MT2701-power-dt-bindings.patch
@@ -1,7 +1,7 @@
 From f371844374fff273f817d6c43f679606417af59e Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Wed, 30 Dec 2015 14:41:45 +0800
-Subject: [PATCH 04/66] soc: mediatek: Add MT2701 power dt-bindings
+Subject: [PATCH 04/78] soc: mediatek: Add MT2701 power dt-bindings
 
 Add power dt-bindings for MT2701.
 
diff --git a/target/linux/mediatek/patches-4.4/0005-soc-mediatek-Add-MT2701-MT7623-scpsys-driver.patch b/target/linux/mediatek/patches-4.4/0005-soc-mediatek-Add-MT2701-MT7623-scpsys-driver.patch
index 505292e6a6..b2527b4772 100644
--- a/target/linux/mediatek/patches-4.4/0005-soc-mediatek-Add-MT2701-MT7623-scpsys-driver.patch
+++ b/target/linux/mediatek/patches-4.4/0005-soc-mediatek-Add-MT2701-MT7623-scpsys-driver.patch
@@ -1,7 +1,7 @@
 From c6711565985f359d7d3c05f01f081e4c216902de Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Wed, 30 Dec 2015 14:41:46 +0800
-Subject: [PATCH 05/66] soc: mediatek: Add MT2701/MT7623 scpsys driver
+Subject: [PATCH 05/78] soc: mediatek: Add MT2701/MT7623 scpsys driver
 
 Add scpsys driver for MT2701 and MT7623.
 
diff --git a/target/linux/mediatek/patches-4.4/0006-clk-mediatek-Refine-the-makefile-to-support-multiple.patch b/target/linux/mediatek/patches-4.4/0006-clk-mediatek-Refine-the-makefile-to-support-multiple.patch
index e02b7a546b..5ac02deea4 100644
--- a/target/linux/mediatek/patches-4.4/0006-clk-mediatek-Refine-the-makefile-to-support-multiple.patch
+++ b/target/linux/mediatek/patches-4.4/0006-clk-mediatek-Refine-the-makefile-to-support-multiple.patch
@@ -1,7 +1,7 @@
 From 0c39bcd17fa6ce723f56ad3756b4bb36c4690342 Mon Sep 17 00:00:00 2001
 From: James Liao <jamesjj.liao@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:17 +0800
-Subject: [PATCH 06/66] clk: mediatek: Refine the makefile to support multiple
+Subject: [PATCH 06/78] clk: mediatek: Refine the makefile to support multiple
  clock drivers
 
 Add a Kconfig to define clock configuration for each SoC, and
diff --git a/target/linux/mediatek/patches-4.4/0007-dt-bindings-ARM-Mediatek-Document-bindings-for-MT270.patch b/target/linux/mediatek/patches-4.4/0007-dt-bindings-ARM-Mediatek-Document-bindings-for-MT270.patch
index c562647e36..382699995b 100644
--- a/target/linux/mediatek/patches-4.4/0007-dt-bindings-ARM-Mediatek-Document-bindings-for-MT270.patch
+++ b/target/linux/mediatek/patches-4.4/0007-dt-bindings-ARM-Mediatek-Document-bindings-for-MT270.patch
@@ -1,7 +1,7 @@
 From d7e96f87f66c571e9f4171ecd89c656fbd2de89b Mon Sep 17 00:00:00 2001
 From: James Liao <jamesjj.liao@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:18 +0800
-Subject: [PATCH 07/66] dt-bindings: ARM: Mediatek: Document bindings for
+Subject: [PATCH 07/78] dt-bindings: ARM: Mediatek: Document bindings for
  MT2701
 
 This patch adds the binding documentation for apmixedsys, bdpsys,
diff --git a/target/linux/mediatek/patches-4.4/0008-clk-mediatek-Add-dt-bindings-for-MT2701-clocks.patch b/target/linux/mediatek/patches-4.4/0008-clk-mediatek-Add-dt-bindings-for-MT2701-clocks.patch
index 6e80e1adc4..f88cd18a1e 100644
--- a/target/linux/mediatek/patches-4.4/0008-clk-mediatek-Add-dt-bindings-for-MT2701-clocks.patch
+++ b/target/linux/mediatek/patches-4.4/0008-clk-mediatek-Add-dt-bindings-for-MT2701-clocks.patch
@@ -1,7 +1,7 @@
 From 2fcbc15da2f13164e0851b9c7fae290249f0b44d Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:19 +0800
-Subject: [PATCH 08/66] clk: mediatek: Add dt-bindings for MT2701 clocks
+Subject: [PATCH 08/78] clk: mediatek: Add dt-bindings for MT2701 clocks
 
 Add MT2701 clock dt-bindings, include topckgen, apmixedsys,
 infracfg, pericfg and subsystem clocks.
diff --git a/target/linux/mediatek/patches-4.4/0009-clk-mediatek-Add-MT2701-clock-support.patch b/target/linux/mediatek/patches-4.4/0009-clk-mediatek-Add-MT2701-clock-support.patch
index e2bb10dedd..7256eab494 100644
--- a/target/linux/mediatek/patches-4.4/0009-clk-mediatek-Add-MT2701-clock-support.patch
+++ b/target/linux/mediatek/patches-4.4/0009-clk-mediatek-Add-MT2701-clock-support.patch
@@ -1,7 +1,7 @@
 From f2c07eaa2df52f9acac9ffc3457d3d81079dd723 Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:20 +0800
-Subject: [PATCH 09/66] clk: mediatek: Add MT2701 clock support
+Subject: [PATCH 09/78] clk: mediatek: Add MT2701 clock support
 
 Add MT2701 clock support, include topckgen, apmixedsys,
 infracfg, pericfg and subsystem clocks.
diff --git a/target/linux/mediatek/patches-4.4/0010-reset-mediatek-mt2701-reset-controller-dt-binding-fi.patch b/target/linux/mediatek/patches-4.4/0010-reset-mediatek-mt2701-reset-controller-dt-binding-fi.patch
index bb4eb5b84f..4e3686bb62 100644
--- a/target/linux/mediatek/patches-4.4/0010-reset-mediatek-mt2701-reset-controller-dt-binding-fi.patch
+++ b/target/linux/mediatek/patches-4.4/0010-reset-mediatek-mt2701-reset-controller-dt-binding-fi.patch
@@ -1,7 +1,7 @@
 From 8d134cbe750b59d15c591622d81e2e9daa09f0c4 Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:21 +0800
-Subject: [PATCH 10/66] reset: mediatek: mt2701 reset controller dt-binding
+Subject: [PATCH 10/78] reset: mediatek: mt2701 reset controller dt-binding
  file
 
 Dt-binding file about reset controller is used to provide
diff --git a/target/linux/mediatek/patches-4.4/0011-reset-mediatek-mt2701-reset-driver.patch b/target/linux/mediatek/patches-4.4/0011-reset-mediatek-mt2701-reset-driver.patch
index fc1a35ac7d..7f4d066ca4 100644
--- a/target/linux/mediatek/patches-4.4/0011-reset-mediatek-mt2701-reset-driver.patch
+++ b/target/linux/mediatek/patches-4.4/0011-reset-mediatek-mt2701-reset-driver.patch
@@ -1,7 +1,7 @@
 From b86d3303db25a8296e4c3de46ee1470f60f71b0c Mon Sep 17 00:00:00 2001
 From: Shunli Wang <shunli.wang@mediatek.com>
 Date: Tue, 5 Jan 2016 14:30:22 +0800
-Subject: [PATCH 11/66] reset: mediatek: mt2701 reset driver
+Subject: [PATCH 11/78] reset: mediatek: mt2701 reset driver
 
 In infrasys and perifsys, there are many reset
 control bits for kinds of modules. These bits are
diff --git a/target/linux/mediatek/patches-4.4/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch b/target/linux/mediatek/patches-4.4/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
index 6c7632ee9f..1c52c15732 100644
--- a/target/linux/mediatek/patches-4.4/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
+++ b/target/linux/mediatek/patches-4.4/0012-ARM-mediatek-Add-MT2701-config-options-for-mediatek-.patch
@@ -1,7 +1,7 @@
 From 3b5df542d52b13a1b20d25311fa4c4029a3b83af Mon Sep 17 00:00:00 2001
 From: Erin Lo <erin.lo@mediatek.com>
 Date: Mon, 28 Dec 2015 15:09:02 +0800
-Subject: [PATCH 12/66] ARM: mediatek: Add MT2701 config options for mediatek
+Subject: [PATCH 12/78] ARM: mediatek: Add MT2701 config options for mediatek
  SoCs.
 
 The upcoming MTK pinctrl driver have a big pin table for each SoC
diff --git a/target/linux/mediatek/patches-4.4/0013-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt2.patch b/target/linux/mediatek/patches-4.4/0013-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt2.patch
index 11f2cf9ea9..715ca08397 100644
--- a/target/linux/mediatek/patches-4.4/0013-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt2.patch
+++ b/target/linux/mediatek/patches-4.4/0013-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt2.patch
@@ -1,7 +1,7 @@
 From 1a254735cad9db5c8605c972b0f16b3929dc0d6e Mon Sep 17 00:00:00 2001
 From: Biao Huang <biao.huang@mediatek.com>
 Date: Mon, 28 Dec 2015 15:09:03 +0800
-Subject: [PATCH 13/66] dt-bindings: mediatek: Modify pinctrl bindings for
+Subject: [PATCH 13/78] dt-bindings: mediatek: Modify pinctrl bindings for
  mt2701
 
 Signed-off-by: Biao Huang <biao.huang@mediatek.com>
diff --git a/target/linux/mediatek/patches-4.4/0014-pinctrl-dt-bindings-Add-pinfunc-header-file-for-mt27.patch b/target/linux/mediatek/patches-4.4/0014-pinctrl-dt-bindings-Add-pinfunc-header-file-for-mt27.patch
index 58de1fe217..e97ddb1300 100644
--- a/target/linux/mediatek/patches-4.4/0014-pinctrl-dt-bindings-Add-pinfunc-header-file-for-mt27.patch
+++ b/target/linux/mediatek/patches-4.4/0014-pinctrl-dt-bindings-Add-pinfunc-header-file-for-mt27.patch
@@ -1,7 +1,7 @@
 From 416720ba33d4fd7d3166c17be7c13651cc08d408 Mon Sep 17 00:00:00 2001
 From: Biao Huang <biao.huang@mediatek.com>
 Date: Mon, 28 Dec 2015 15:09:04 +0800
-Subject: [PATCH 14/66] pinctrl: dt bindings: Add pinfunc header file for
+Subject: [PATCH 14/78] pinctrl: dt bindings: Add pinfunc header file for
  mt2701
 
 Add pinfunc header file, mt2701 related dts will include it
diff --git a/target/linux/mediatek/patches-4.4/0015-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt7.patch b/target/linux/mediatek/patches-4.4/0015-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt7.patch
index 974f39ea3a..4cae264eae 100644
--- a/target/linux/mediatek/patches-4.4/0015-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt7.patch
+++ b/target/linux/mediatek/patches-4.4/0015-dt-bindings-mediatek-Modify-pinctrl-bindings-for-mt7.patch
@@ -1,7 +1,7 @@
 From ddc72b659b3642d0496dee4e1ee39416ca008053 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Thu, 7 Jan 2016 23:42:06 +0100
-Subject: [PATCH 15/66] dt-bindings: mediatek: Modify pinctrl bindings for
+Subject: [PATCH 15/78] dt-bindings: mediatek: Modify pinctrl bindings for
  mt7623
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
diff --git a/target/linux/mediatek/patches-4.4/0016-pinctrl-dt-bindings-Add-pinctrl-file-for-mt7623.patch b/target/linux/mediatek/patches-4.4/0016-pinctrl-dt-bindings-Add-pinctrl-file-for-mt7623.patch
index fca1fab9f3..299be7c962 100644
--- a/target/linux/mediatek/patches-4.4/0016-pinctrl-dt-bindings-Add-pinctrl-file-for-mt7623.patch
+++ b/target/linux/mediatek/patches-4.4/0016-pinctrl-dt-bindings-Add-pinctrl-file-for-mt7623.patch
@@ -1,7 +1,7 @@
 From 1255eaacd6cc9d1fa6bb33185380efed22008baf Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Sat, 27 Jun 2015 13:13:05 +0200
-Subject: [PATCH 16/66] pinctrl: dt bindings: Add pinctrl file for mt7623
+Subject: [PATCH 16/78] pinctrl: dt bindings: Add pinctrl file for mt7623
 
 Add the driver and header files required to make pinctrl work on MediaTek
 MT7623.
diff --git a/target/linux/mediatek/patches-4.4/0017-clk-add-hifsys-reset.patch b/target/linux/mediatek/patches-4.4/0017-clk-add-hifsys-reset.patch
index 685a496f85..f2f1c19ba6 100644
--- a/target/linux/mediatek/patches-4.4/0017-clk-add-hifsys-reset.patch
+++ b/target/linux/mediatek/patches-4.4/0017-clk-add-hifsys-reset.patch
@@ -1,7 +1,7 @@
 From 294cf90337d70ad74edf147180bbeef837298bd0 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 6 Jan 2016 20:06:49 +0100
-Subject: [PATCH 17/66] clk: add hifsys reset
+Subject: [PATCH 17/78] clk: add hifsys reset
 
 Hi,
 
diff --git a/target/linux/mediatek/patches-4.4/0018-dt-bindings-Add-a-binding-for-Mediatek-xHCI-host-con.patch b/target/linux/mediatek/patches-4.4/0018-dt-bindings-Add-a-binding-for-Mediatek-xHCI-host-con.patch
index 216d388572..6f64c709ea 100644
--- a/target/linux/mediatek/patches-4.4/0018-dt-bindings-Add-a-binding-for-Mediatek-xHCI-host-con.patch
+++ b/target/linux/mediatek/patches-4.4/0018-dt-bindings-Add-a-binding-for-Mediatek-xHCI-host-con.patch
@@ -1,7 +1,7 @@
 From 84d37aeef94deae3ce87e677f6016a5d980429e8 Mon Sep 17 00:00:00 2001
 From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
 Date: Tue, 17 Nov 2015 17:18:39 +0800
-Subject: [PATCH 18/66] dt-bindings: Add a binding for Mediatek xHCI host
+Subject: [PATCH 18/78] dt-bindings: Add a binding for Mediatek xHCI host
  controller
 
 add a DT binding documentation of xHCI host controller for the
diff --git a/target/linux/mediatek/patches-4.4/0019-xhci-mediatek-support-MTK-xHCI-host-controller.patch b/target/linux/mediatek/patches-4.4/0019-xhci-mediatek-support-MTK-xHCI-host-controller.patch
index 188728d779..29a6b45355 100644
--- a/target/linux/mediatek/patches-4.4/0019-xhci-mediatek-support-MTK-xHCI-host-controller.patch
+++ b/target/linux/mediatek/patches-4.4/0019-xhci-mediatek-support-MTK-xHCI-host-controller.patch
@@ -1,7 +1,7 @@
 From 651d8fff94718c7e48b8a40d7774878eb8ed62ee Mon Sep 17 00:00:00 2001
 From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
 Date: Tue, 17 Nov 2015 17:18:40 +0800
-Subject: [PATCH 19/66] xhci: mediatek: support MTK xHCI host controller
+Subject: [PATCH 19/78] xhci: mediatek: support MTK xHCI host controller
 
 There some vendor quirks for MTK xhci host controller:
 1. It defines some extra SW scheduling parameters for HW
diff --git a/target/linux/mediatek/patches-4.4/0020-arm64-dts-mediatek-add-xHCI-usb-phy-for-mt8173.patch b/target/linux/mediatek/patches-4.4/0020-arm64-dts-mediatek-add-xHCI-usb-phy-for-mt8173.patch
index e9ce56e3af..33d68085fb 100644
--- a/target/linux/mediatek/patches-4.4/0020-arm64-dts-mediatek-add-xHCI-usb-phy-for-mt8173.patch
+++ b/target/linux/mediatek/patches-4.4/0020-arm64-dts-mediatek-add-xHCI-usb-phy-for-mt8173.patch
@@ -1,7 +1,7 @@
 From 31a22fbd0d3b187be61c4c5d22b19c95abb327c3 Mon Sep 17 00:00:00 2001
 From: "chunfeng.yun@mediatek.com" <chunfeng.yun@mediatek.com>
 Date: Tue, 17 Nov 2015 17:18:41 +0800
-Subject: [PATCH 20/66] arm64: dts: mediatek: add xHCI & usb phy for mt8173
+Subject: [PATCH 20/78] arm64: dts: mediatek: add xHCI & usb phy for mt8173
 
 add xHCI and phy drivers for MT8173-EVB
 
diff --git a/target/linux/mediatek/patches-4.4/0021-Document-DT-Add-bindings-for-mediatek-MT7623-SoC-Pla.patch b/target/linux/mediatek/patches-4.4/0021-Document-DT-Add-bindings-for-mediatek-MT7623-SoC-Pla.patch
index b7aae926c8..61b0aa4ac2 100644
--- a/target/linux/mediatek/patches-4.4/0021-Document-DT-Add-bindings-for-mediatek-MT7623-SoC-Pla.patch
+++ b/target/linux/mediatek/patches-4.4/0021-Document-DT-Add-bindings-for-mediatek-MT7623-SoC-Pla.patch
@@ -1,7 +1,7 @@
 From 162deec293400cb132161606629654acaec7cb4b Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Tue, 5 Jan 2016 12:13:54 +0100
-Subject: [PATCH 21/66] Document: DT: Add bindings for mediatek MT7623 SoC
+Subject: [PATCH 21/78] Document: DT: Add bindings for mediatek MT7623 SoC
  Platform
 
 This adds a DT binding documentation for the MT7623 SoC from Mediatek.
diff --git a/target/linux/mediatek/patches-4.4/0022-soc-mediatek-add-compat-string-for-mt7623-to-scpsys.patch b/target/linux/mediatek/patches-4.4/0022-soc-mediatek-add-compat-string-for-mt7623-to-scpsys.patch
index eefbffdf41..36aea67ed7 100644
--- a/target/linux/mediatek/patches-4.4/0022-soc-mediatek-add-compat-string-for-mt7623-to-scpsys.patch
+++ b/target/linux/mediatek/patches-4.4/0022-soc-mediatek-add-compat-string-for-mt7623-to-scpsys.patch
@@ -1,7 +1,7 @@
 From fa5d94d6b4b314f751b1c32bb5a87a80b866d05e Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Tue, 5 Jan 2016 16:52:31 +0100
-Subject: [PATCH 22/66] soc: mediatek: add compat string for mt7623 to scpsys
+Subject: [PATCH 22/78] soc: mediatek: add compat string for mt7623 to scpsys
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
diff --git a/target/linux/mediatek/patches-4.4/0023-ARM-dts-mediatek-add-MT7623-basic-support.patch b/target/linux/mediatek/patches-4.4/0023-ARM-dts-mediatek-add-MT7623-basic-support.patch
index af0a68f213..b25c91bee8 100644
--- a/target/linux/mediatek/patches-4.4/0023-ARM-dts-mediatek-add-MT7623-basic-support.patch
+++ b/target/linux/mediatek/patches-4.4/0023-ARM-dts-mediatek-add-MT7623-basic-support.patch
@@ -1,7 +1,7 @@
-From cfe366d7a20f88c7fc92faaf8b25c24e730bd40b Mon Sep 17 00:00:00 2001
+From a4df3e7e4e906a4e9dac1f8c43f6192f22ef6242 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Tue, 5 Jan 2016 12:16:17 +0100
-Subject: [PATCH 23/66] ARM: dts: mediatek: add MT7623 basic support
+Subject: [PATCH 23/78] ARM: dts: mediatek: add MT7623 basic support
 
 This adds basic chip support for Mediatek MT7623.
 
@@ -9,16 +9,18 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
  arch/arm/boot/dts/Makefile        |    1 +
  arch/arm/boot/dts/mt7623-evb.dts  |  459 +++++++++++++++++++++++++++++++++
- arch/arm/boot/dts/mt7623.dtsi     |  507 +++++++++++++++++++++++++++++++++++++
+ arch/arm/boot/dts/mt7623.dtsi     |  510 +++++++++++++++++++++++++++++++++++++
  arch/arm/mach-mediatek/Kconfig    |    4 +
  arch/arm/mach-mediatek/mediatek.c |    1 +
- 5 files changed, 972 insertions(+)
+ 5 files changed, 975 insertions(+)
  create mode 100644 arch/arm/boot/dts/mt7623-evb.dts
  create mode 100644 arch/arm/boot/dts/mt7623.dtsi
 
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index 30bbc37..2bce370 100644
 --- a/arch/arm/boot/dts/Makefile
 +++ b/arch/arm/boot/dts/Makefile
-@@ -774,6 +774,7 @@
+@@ -774,6 +774,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
  	mt6580-evbp1.dtb \
  	mt6589-aquaris5.dtb \
  	mt6592-evb.dtb \
@@ -26,6 +28,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  	mt8127-moose.dtb \
  	mt8135-evbp1.dtb
  dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb
+diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
+new file mode 100644
+index 0000000..5e9381d
 --- /dev/null
 +++ b/arch/arm/boot/dts/mt7623-evb.dts
 @@ -0,0 +1,459 @@
@@ -488,9 +493,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +	mediatek,reset-pin = <&pio 15 0>;
 +	status = "okay";
 +};
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+new file mode 100644
+index 0000000..c53c10d
 --- /dev/null
 +++ b/arch/arm/boot/dts/mt7623.dtsi
-@@ -0,0 +1,508 @@
+@@ -0,0 +1,510 @@
 +/*
 + * Copyright (c) 2016 MediaTek Inc.
 + * Author: John Crispin <blogic@openwrt.org>
@@ -947,7 +955,9 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +	
 +		clocks = <&topckgen CLK_TOP_ETHIF_SEL>;
 +		clock-names = "ethif";
-+		interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
++		interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
++			      GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
++			      GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
 +		power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
 +
 +		mediatek,ethsys = <&ethsys>;
@@ -999,9 +1009,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
 +		status = "disabled";
 +	};
 +};
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index 37dd438..7fb605e 100644
 --- a/arch/arm/mach-mediatek/Kconfig
 +++ b/arch/arm/mach-mediatek/Kconfig
-@@ -21,6 +21,10 @@
+@@ -21,6 +21,10 @@ config MACH_MT6592
  	bool "MediaTek MT6592 SoCs support"
  	default ARCH_MEDIATEK
  
@@ -1012,9 +1024,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  config MACH_MT8127
  	bool "MediaTek MT8127 SoCs support"
  	default ARCH_MEDIATEK
+diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c
+index d019a08..bcfca37 100644
 --- a/arch/arm/mach-mediatek/mediatek.c
 +++ b/arch/arm/mach-mediatek/mediatek.c
-@@ -46,6 +46,7 @@
+@@ -46,6 +46,7 @@ static void __init mediatek_timer_init(void)
  static const char * const mediatek_board_dt_compat[] = {
  	"mediatek,mt6589",
  	"mediatek,mt6592",
@@ -1022,3 +1036,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
  	"mediatek,mt8127",
  	"mediatek,mt8135",
  	NULL,
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch b/target/linux/mediatek/patches-4.4/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
index 53db649c25..408946109a 100644
--- a/target/linux/mediatek/patches-4.4/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
+++ b/target/linux/mediatek/patches-4.4/0024-dt-bindings-add-MediaTek-PCIe-binding-documentation.patch
@@ -1,7 +1,7 @@
-From 2ff725af8a512481d68ebd7f8ad122b1c98f3fad Mon Sep 17 00:00:00 2001
+From 97478bae3a11b5e87d61b88267e915f7c5ddf4e9 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 6 Jan 2016 21:55:10 +0100
-Subject: [PATCH 24/66] dt-bindings: add MediaTek PCIe binding documentation
+Subject: [PATCH 24/78] dt-bindings: add MediaTek PCIe binding documentation
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
@@ -157,10 +157,10 @@ index 0000000..8fea3ed
 +		};
 +	};
 diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
-index 1ba7790..ec19283 100644
+index c53c10d..c8c802d 100644
 --- a/arch/arm/boot/dts/mt7623.dtsi
 +++ b/arch/arm/boot/dts/mt7623.dtsi
-@@ -291,6 +291,18 @@
+@@ -292,6 +292,18 @@
  		status = "disabled";
  	};
  
diff --git a/target/linux/mediatek/patches-4.4/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch b/target/linux/mediatek/patches-4.4/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
index 36a7a85582..fe67755acb 100644
--- a/target/linux/mediatek/patches-4.4/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
+++ b/target/linux/mediatek/patches-4.4/0025-PCI-mediatek-add-support-for-PCIe-found-on-MT7623-MT.patch
@@ -1,7 +1,7 @@
-From 1ac5a6be891fb934e2a864bb2e424f05315f7385 Mon Sep 17 00:00:00 2001
+From b44b44cfdcb9b2d7ba513d24df1b16b8c57cce59 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Tue, 5 Jan 2016 20:20:04 +0100
-Subject: [PATCH 25/66] PCI: mediatek: add support for PCIe found on
+Subject: [PATCH 25/78] PCI: mediatek: add support for PCIe found on
  MT7623/MT2701
 
 Add PCIe controller support on MediaTek MT2701/MT7623. The driver supports
diff --git a/target/linux/mediatek/patches-4.4/0026-scpsys-various-fixes.patch b/target/linux/mediatek/patches-4.4/0026-scpsys-various-fixes.patch
index bf26783964..dea9152252 100644
--- a/target/linux/mediatek/patches-4.4/0026-scpsys-various-fixes.patch
+++ b/target/linux/mediatek/patches-4.4/0026-scpsys-various-fixes.patch
@@ -1,7 +1,7 @@
-From 6c5c23a6c21b1a244db79d6387db915c72f50367 Mon Sep 17 00:00:00 2001
+From fe8fd85507870bf3aa5ff257944f15b50888d17c Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Sun, 21 Feb 2016 13:52:12 +0100
-Subject: [PATCH 26/66] scpsys: various fixes
+Subject: [PATCH 26/78] scpsys: various fixes
 
 ---
  drivers/clk/mediatek/clk-mt2701.c        |    2 ++
diff --git a/target/linux/mediatek/patches-4.4/0027-soc-mediatek-PMIC-wrap-Clear-the-vldclr-if-state-mac.patch b/target/linux/mediatek/patches-4.4/0027-soc-mediatek-PMIC-wrap-Clear-the-vldclr-if-state-mac.patch
index 2607f93b65..d9b98c1f46 100644
--- a/target/linux/mediatek/patches-4.4/0027-soc-mediatek-PMIC-wrap-Clear-the-vldclr-if-state-mac.patch
+++ b/target/linux/mediatek/patches-4.4/0027-soc-mediatek-PMIC-wrap-Clear-the-vldclr-if-state-mac.patch
@@ -1,7 +1,7 @@
-From dadfca5daee5cb40d34542425392e694eddc5bc1 Mon Sep 17 00:00:00 2001
+From 2fc7dd0f48d9c2096d76562a1960b78b064701f7 Mon Sep 17 00:00:00 2001
 From: Henry Chen <henryc.chen@mediatek.com>
 Date: Mon, 4 Jan 2016 20:02:52 +0800
-Subject: [PATCH 27/66] soc: mediatek: PMIC wrap: Clear the vldclr if state
+Subject: [PATCH 27/78] soc: mediatek: PMIC wrap: Clear the vldclr if state
  machine stay on FSM_VLDCLR state.
 
 Sometimes PMIC is too busy to send data in time to cause pmic wrap timeout,
diff --git a/target/linux/mediatek/patches-4.4/0028-ARM-mediatek-add-MT7623-smp-bringup-code.patch b/target/linux/mediatek/patches-4.4/0028-ARM-mediatek-add-MT7623-smp-bringup-code.patch
index 24521cb369..4bbfe7427a 100644
--- a/target/linux/mediatek/patches-4.4/0028-ARM-mediatek-add-MT7623-smp-bringup-code.patch
+++ b/target/linux/mediatek/patches-4.4/0028-ARM-mediatek-add-MT7623-smp-bringup-code.patch
@@ -1,7 +1,7 @@
-From 7512d9b4bf8ab222b4d542ada87368229770383f Mon Sep 17 00:00:00 2001
+From b7d11ec865f99085a907f1a082f70404734e954c Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Tue, 5 Jan 2016 17:24:28 +0100
-Subject: [PATCH 28/66] ARM: mediatek: add MT7623 smp bringup code
+Subject: [PATCH 28/78] ARM: mediatek: add MT7623 smp bringup code
 
 Add support for booting secondary CPUs on MT7623.
 
diff --git a/target/linux/mediatek/patches-4.4/0029-soc-mediatek-PMIC-wrap-clear-the-STAUPD_TRIG-bit-of-.patch b/target/linux/mediatek/patches-4.4/0029-soc-mediatek-PMIC-wrap-clear-the-STAUPD_TRIG-bit-of-.patch
index eab74bcf30..592b13f0f0 100644
--- a/target/linux/mediatek/patches-4.4/0029-soc-mediatek-PMIC-wrap-clear-the-STAUPD_TRIG-bit-of-.patch
+++ b/target/linux/mediatek/patches-4.4/0029-soc-mediatek-PMIC-wrap-clear-the-STAUPD_TRIG-bit-of-.patch
@@ -1,7 +1,7 @@
-From f8296ee9561945b5cebb570e06259b8865ef0b91 Mon Sep 17 00:00:00 2001
+From 7e0c3f4f4c7a55eda670c97b1b3b793ceecc1655 Mon Sep 17 00:00:00 2001
 From: Henry Chen <henryc.chen@mediatek.com>
 Date: Thu, 21 Jan 2016 19:04:00 +0800
-Subject: [PATCH 29/66] soc: mediatek: PMIC wrap: clear the STAUPD_TRIG bit of
+Subject: [PATCH 29/78] soc: mediatek: PMIC wrap: clear the STAUPD_TRIG bit of
  WDT_SRC_EN
 
 Since STAUPD interrupts aren't handled on mt8173, disable watchdog timeout
diff --git a/target/linux/mediatek/patches-4.4/0030-ARM-mediatek-add-mt2701-smp-bringup-code.patch b/target/linux/mediatek/patches-4.4/0030-ARM-mediatek-add-mt2701-smp-bringup-code.patch
index 9039cc1f38..e901a63c19 100644
--- a/target/linux/mediatek/patches-4.4/0030-ARM-mediatek-add-mt2701-smp-bringup-code.patch
+++ b/target/linux/mediatek/patches-4.4/0030-ARM-mediatek-add-mt2701-smp-bringup-code.patch
@@ -1,7 +1,7 @@
-From 977ccf394047647354093535f9b07f13a74949df Mon Sep 17 00:00:00 2001
+From 672ed0995e4ebf3f14bb58d08d6ba4c78337b90b Mon Sep 17 00:00:00 2001
 From: Louis Yu <louis.yu@mediatek.com>
 Date: Thu, 7 Jan 2016 20:09:43 +0800
-Subject: [PATCH 30/66] ARM: mediatek: add mt2701 smp bringup code
+Subject: [PATCH 30/78] ARM: mediatek: add mt2701 smp bringup code
 
 Add support for booting secondary CPUs on mt2701.
 
diff --git a/target/linux/mediatek/patches-4.4/0031-dt-bindings-ARM-Mediatek-add-MT2701-7623-string-to-t.patch b/target/linux/mediatek/patches-4.4/0031-dt-bindings-ARM-Mediatek-add-MT2701-7623-string-to-t.patch
index fd44518320..d360096656 100644
--- a/target/linux/mediatek/patches-4.4/0031-dt-bindings-ARM-Mediatek-add-MT2701-7623-string-to-t.patch
+++ b/target/linux/mediatek/patches-4.4/0031-dt-bindings-ARM-Mediatek-add-MT2701-7623-string-to-t.patch
@@ -1,7 +1,7 @@
-From add1cc43bd41e6fc755852a5767e710cb3314013 Mon Sep 17 00:00:00 2001
+From 1474be4d7f9559804671ab01911232368496a1de Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 13:12:19 +0100
-Subject: [PATCH 31/66] dt-bindings: ARM: Mediatek: add MT2701/7623 string to
+Subject: [PATCH 31/78] dt-bindings: ARM: Mediatek: add MT2701/7623 string to
  the PMIC wrapper doc
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
diff --git a/target/linux/mediatek/patches-4.4/0032-soc-mediatek-PMIC-wrap-don-t-duplicate-the-wrapper-d.patch b/target/linux/mediatek/patches-4.4/0032-soc-mediatek-PMIC-wrap-don-t-duplicate-the-wrapper-d.patch
index 5ee4ea4240..a29df296ed 100644
--- a/target/linux/mediatek/patches-4.4/0032-soc-mediatek-PMIC-wrap-don-t-duplicate-the-wrapper-d.patch
+++ b/target/linux/mediatek/patches-4.4/0032-soc-mediatek-PMIC-wrap-don-t-duplicate-the-wrapper-d.patch
@@ -1,7 +1,7 @@
-From 6792f25663b6064f21f033241bbeb6b023fa8ce7 Mon Sep 17 00:00:00 2001
+From 542f2cd3287fb955efcc5ceca14b690ba19f8c57 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 06:42:01 +0100
-Subject: [PATCH 32/66] soc: mediatek: PMIC wrap: don't duplicate the wrapper
+Subject: [PATCH 32/78] soc: mediatek: PMIC wrap: don't duplicate the wrapper
  data
 
 As we add support for more devices struct pmic_wrapper_type will grow and
diff --git a/target/linux/mediatek/patches-4.4/0033-soc-mediatek-PMIC-wrap-add-wrapper-callbacks-for-ini.patch b/target/linux/mediatek/patches-4.4/0033-soc-mediatek-PMIC-wrap-add-wrapper-callbacks-for-ini.patch
index 55881e0ffd..ec0cd5dba5 100644
--- a/target/linux/mediatek/patches-4.4/0033-soc-mediatek-PMIC-wrap-add-wrapper-callbacks-for-ini.patch
+++ b/target/linux/mediatek/patches-4.4/0033-soc-mediatek-PMIC-wrap-add-wrapper-callbacks-for-ini.patch
@@ -1,7 +1,7 @@
-From b67fd66c46f7cc6ac869aafc7f920846aed6bc12 Mon Sep 17 00:00:00 2001
+From 915340f70c0594d1f0717fee3eb678fa71206509 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 05:27:17 +0100
-Subject: [PATCH 33/66] soc: mediatek: PMIC wrap: add wrapper callbacks for
+Subject: [PATCH 33/78] soc: mediatek: PMIC wrap: add wrapper callbacks for
  init_reg_clock
 
 Split init_reg_clock up into SoC specific callbacks. The patch also
diff --git a/target/linux/mediatek/patches-4.4/0034-soc-mediatek-PMIC-wrap-split-SoC-specific-init-into-.patch b/target/linux/mediatek/patches-4.4/0034-soc-mediatek-PMIC-wrap-split-SoC-specific-init-into-.patch
index e73c43a38a..e9132db6e3 100644
--- a/target/linux/mediatek/patches-4.4/0034-soc-mediatek-PMIC-wrap-split-SoC-specific-init-into-.patch
+++ b/target/linux/mediatek/patches-4.4/0034-soc-mediatek-PMIC-wrap-split-SoC-specific-init-into-.patch
@@ -1,7 +1,7 @@
-From 4dd080818ec30dd101b6248b418751de5ac508f2 Mon Sep 17 00:00:00 2001
+From b2287b0afb757870ce0f4324bb4bc597d3f90a2a Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 10:12:00 +0100
-Subject: [PATCH 34/66] soc: mediatek: PMIC wrap: split SoC specific init into
+Subject: [PATCH 34/78] soc: mediatek: PMIC wrap: split SoC specific init into
  callback
 
 This patch moves the SoC specific wrapper init code into separate callback
diff --git a/target/linux/mediatek/patches-4.4/0035-soc-mediatek-PMIC-wrap-WRAP_INT_EN-needs-a-different.patch b/target/linux/mediatek/patches-4.4/0035-soc-mediatek-PMIC-wrap-WRAP_INT_EN-needs-a-different.patch
index 8451679633..97300eed9c 100644
--- a/target/linux/mediatek/patches-4.4/0035-soc-mediatek-PMIC-wrap-WRAP_INT_EN-needs-a-different.patch
+++ b/target/linux/mediatek/patches-4.4/0035-soc-mediatek-PMIC-wrap-WRAP_INT_EN-needs-a-different.patch
@@ -1,7 +1,7 @@
-From 72300493dbf58de75972fce86e64f4728ff9b594 Mon Sep 17 00:00:00 2001
+From 76f0c7770046ed436be085af1257e0ab06fdd41f Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 10:14:39 +0100
-Subject: [PATCH 35/66] soc: mediatek: PMIC wrap: WRAP_INT_EN needs a
+Subject: [PATCH 35/78] soc: mediatek: PMIC wrap: WRAP_INT_EN needs a
  different bitmask for MT2701/7623
 
 MT2701 and MT7623 use a different bitmask for PWRAP_INT_EN.
diff --git a/target/linux/mediatek/patches-4.4/0036-soc-mediatek-PMIC-wrap-SPI_WRITE-needs-a-different-b.patch b/target/linux/mediatek/patches-4.4/0036-soc-mediatek-PMIC-wrap-SPI_WRITE-needs-a-different-b.patch
index 7f7a905a7e..6110d007fd 100644
--- a/target/linux/mediatek/patches-4.4/0036-soc-mediatek-PMIC-wrap-SPI_WRITE-needs-a-different-b.patch
+++ b/target/linux/mediatek/patches-4.4/0036-soc-mediatek-PMIC-wrap-SPI_WRITE-needs-a-different-b.patch
@@ -1,7 +1,7 @@
-From 8e28fd218224df9c1a108a0b0d4c3a2ec51ddc62 Mon Sep 17 00:00:00 2001
+From fca1819591e7824d474c900a3524c0c1fee6a300 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 10:21:42 +0100
-Subject: [PATCH 36/66] soc: mediatek: PMIC wrap: SPI_WRITE needs a different
+Subject: [PATCH 36/78] soc: mediatek: PMIC wrap: SPI_WRITE needs a different
  bitmask for MT2701/7623
 
 Different SoCs will use different bitmask for the SPI_WRITE command. This
diff --git a/target/linux/mediatek/patches-4.4/0037-soc-mediatek-PMIC-wrap-move-wdt_src-into-the-pmic_wr.patch b/target/linux/mediatek/patches-4.4/0037-soc-mediatek-PMIC-wrap-move-wdt_src-into-the-pmic_wr.patch
index ed225a5daf..f3307be5b1 100644
--- a/target/linux/mediatek/patches-4.4/0037-soc-mediatek-PMIC-wrap-move-wdt_src-into-the-pmic_wr.patch
+++ b/target/linux/mediatek/patches-4.4/0037-soc-mediatek-PMIC-wrap-move-wdt_src-into-the-pmic_wr.patch
@@ -1,7 +1,7 @@
-From 3a01206ed5749b5469459f82f1152e3699cb8baf Mon Sep 17 00:00:00 2001
+From dfdef729324d87d40468f76f0f2767a09c9b0ab0 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 10:48:35 +0100
-Subject: [PATCH 37/66] soc: mediatek: PMIC wrap: move wdt_src into the
+Subject: [PATCH 37/78] soc: mediatek: PMIC wrap: move wdt_src into the
  pmic_wrapper_type struct
 
 Different SoCs will use different bitmask for the wdt_src. This patch
diff --git a/target/linux/mediatek/patches-4.4/0038-soc-mediatek-PMIC-wrap-remove-pwrap_is_mt8135-and-pw.patch b/target/linux/mediatek/patches-4.4/0038-soc-mediatek-PMIC-wrap-remove-pwrap_is_mt8135-and-pw.patch
index ad8ff5ff3b..3dfc7bd212 100644
--- a/target/linux/mediatek/patches-4.4/0038-soc-mediatek-PMIC-wrap-remove-pwrap_is_mt8135-and-pw.patch
+++ b/target/linux/mediatek/patches-4.4/0038-soc-mediatek-PMIC-wrap-remove-pwrap_is_mt8135-and-pw.patch
@@ -1,7 +1,7 @@
-From 20f4eef2f061619762aa87d484b359c3f9a0b228 Mon Sep 17 00:00:00 2001
+From 34d49f015488dcb3de31060846414bd4254e752a Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 10:54:18 +0100
-Subject: [PATCH 38/66] soc: mediatek: PMIC wrap: remove pwrap_is_mt8135() and
+Subject: [PATCH 38/78] soc: mediatek: PMIC wrap: remove pwrap_is_mt8135() and
  pwrap_is_mt8173()
 
 With more SoCs being added the list of helper functions like these would
diff --git a/target/linux/mediatek/patches-4.4/0039-soc-mediatek-PMIC-wrap-add-a-slave-specific-struct.patch b/target/linux/mediatek/patches-4.4/0039-soc-mediatek-PMIC-wrap-add-a-slave-specific-struct.patch
index 5cde24249d..47658dc1b9 100644
--- a/target/linux/mediatek/patches-4.4/0039-soc-mediatek-PMIC-wrap-add-a-slave-specific-struct.patch
+++ b/target/linux/mediatek/patches-4.4/0039-soc-mediatek-PMIC-wrap-add-a-slave-specific-struct.patch
@@ -1,7 +1,7 @@
-From 023e530aa86c95352dfc97df960ee0039ef2c030 Mon Sep 17 00:00:00 2001
+From ae593b270b87f9ed6c35dec3ac69dd6bda43c0a0 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 09:55:08 +0100
-Subject: [PATCH 39/66] soc: mediatek: PMIC wrap: add a slave specific struct
+Subject: [PATCH 39/78] soc: mediatek: PMIC wrap: add a slave specific struct
 
 This patch adds a new struct pwrap_slv_type that we use to store the slave
 specific data. The patch adds 2 new helper functions to access the dew
diff --git a/target/linux/mediatek/patches-4.4/0040-soc-mediatek-PMIC-wrap-add-mt6323-slave-support.patch b/target/linux/mediatek/patches-4.4/0040-soc-mediatek-PMIC-wrap-add-mt6323-slave-support.patch
index a01f992c81..5aaced639f 100644
--- a/target/linux/mediatek/patches-4.4/0040-soc-mediatek-PMIC-wrap-add-mt6323-slave-support.patch
+++ b/target/linux/mediatek/patches-4.4/0040-soc-mediatek-PMIC-wrap-add-mt6323-slave-support.patch
@@ -1,7 +1,7 @@
-From 5db18f42fc5584a516cb7a8b705c97a7f1c4bf1b Mon Sep 17 00:00:00 2001
+From 8f3597ca8c8a28d2501d03643569f0cea920c24d Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 11:40:43 +0100
-Subject: [PATCH 40/66] soc: mediatek: PMIC wrap: add mt6323 slave support
+Subject: [PATCH 40/78] soc: mediatek: PMIC wrap: add mt6323 slave support
 
 Add support for MT6323 slaves. This PMIC can be found on MT2701 and MT7623
 EVB. The only function that we need to touch is pwrap_init_cipher().
diff --git a/target/linux/mediatek/patches-4.4/0041-soc-mediatek-PMIC-wrap-add-MT2701-7623-support.patch b/target/linux/mediatek/patches-4.4/0041-soc-mediatek-PMIC-wrap-add-MT2701-7623-support.patch
index a7e8775edf..a897640964 100644
--- a/target/linux/mediatek/patches-4.4/0041-soc-mediatek-PMIC-wrap-add-MT2701-7623-support.patch
+++ b/target/linux/mediatek/patches-4.4/0041-soc-mediatek-PMIC-wrap-add-MT2701-7623-support.patch
@@ -1,7 +1,7 @@
-From 444c4931cc6c2d1d9c94d8bbd4ee89438abca212 Mon Sep 17 00:00:00 2001
+From 09351f7c59e08d9c259546e03af3af05c10d644c Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 20 Jan 2016 12:09:14 +0100
-Subject: [PATCH 41/66] soc: mediatek: PMIC wrap: add MT2701/7623 support
+Subject: [PATCH 41/78] soc: mediatek: PMIC wrap: add MT2701/7623 support
 
 Add the registers, callbacks and data structures required to make the
 wrapper work on MT2701 and MT7623.
diff --git a/target/linux/mediatek/patches-4.4/0042-dt-bindings-mfd-Add-bindings-for-the-MediaTek-MT6323.patch b/target/linux/mediatek/patches-4.4/0042-dt-bindings-mfd-Add-bindings-for-the-MediaTek-MT6323.patch
index b31f063679..cdb930a891 100644
--- a/target/linux/mediatek/patches-4.4/0042-dt-bindings-mfd-Add-bindings-for-the-MediaTek-MT6323.patch
+++ b/target/linux/mediatek/patches-4.4/0042-dt-bindings-mfd-Add-bindings-for-the-MediaTek-MT6323.patch
@@ -1,7 +1,7 @@
-From eb574bce59e66295ee288de0df450a6e82bb5b56 Mon Sep 17 00:00:00 2001
+From 94f7b9dabcd073bf629268514f87e5dbd2cafd13 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Sun, 10 Jan 2016 17:12:37 +0100
-Subject: [PATCH 42/66] dt-bindings: mfd: Add bindings for the MediaTek MT6323
+Subject: [PATCH 42/78] dt-bindings: mfd: Add bindings for the MediaTek MT6323
  PMIC
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
diff --git a/target/linux/mediatek/patches-4.4/0043-mfd-mt6397-int_con-and-int_status-may-vary-in-locati.patch b/target/linux/mediatek/patches-4.4/0043-mfd-mt6397-int_con-and-int_status-may-vary-in-locati.patch
index 9d2379dc96..1761e7745a 100644
--- a/target/linux/mediatek/patches-4.4/0043-mfd-mt6397-int_con-and-int_status-may-vary-in-locati.patch
+++ b/target/linux/mediatek/patches-4.4/0043-mfd-mt6397-int_con-and-int_status-may-vary-in-locati.patch
@@ -1,7 +1,7 @@
-From 337807a89aec90a313a77336a9296ccd926c7015 Mon Sep 17 00:00:00 2001
+From aa90d988b627b41c774891fd72560699f30faefa Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Fri, 8 Jan 2016 08:33:17 +0100
-Subject: [PATCH 43/66] mfd: mt6397: int_con and int_status may vary in
+Subject: [PATCH 43/78] mfd: mt6397: int_con and int_status may vary in
  location
 
 MT6323 has the INT_CON and INT_STATUS located at a different position.
diff --git a/target/linux/mediatek/patches-4.4/0044-mfd-mt6397-add-support-for-different-Slave-types.patch b/target/linux/mediatek/patches-4.4/0044-mfd-mt6397-add-support-for-different-Slave-types.patch
index 0550087533..38ba17f016 100644
--- a/target/linux/mediatek/patches-4.4/0044-mfd-mt6397-add-support-for-different-Slave-types.patch
+++ b/target/linux/mediatek/patches-4.4/0044-mfd-mt6397-add-support-for-different-Slave-types.patch
@@ -1,7 +1,7 @@
-From c6fab1574939f968257029dd75da51ab266081c9 Mon Sep 17 00:00:00 2001
+From 1ef53a11f0c282008aa572eb7c97fa1e79621ea3 Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Fri, 8 Jan 2016 08:41:52 +0100
-Subject: [PATCH 44/66] mfd: mt6397: add support for different Slave types
+Subject: [PATCH 44/78] mfd: mt6397: add support for different Slave types
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
diff --git a/target/linux/mediatek/patches-4.4/0045-mfd-mt6397-add-MT6323-support-to-MT6397-driver.patch b/target/linux/mediatek/patches-4.4/0045-mfd-mt6397-add-MT6323-support-to-MT6397-driver.patch
index 3b8a752c1c..dac60eeeff 100644
--- a/target/linux/mediatek/patches-4.4/0045-mfd-mt6397-add-MT6323-support-to-MT6397-driver.patch
+++ b/target/linux/mediatek/patches-4.4/0045-mfd-mt6397-add-MT6323-support-to-MT6397-driver.patch
@@ -1,7 +1,7 @@
-From d3d044cff01ef835ef36b6f7d22b19fe47b65e46 Mon Sep 17 00:00:00 2001
+From 49d9c28b1252c7920840662425aa7e4705cfbccf Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Fri, 8 Jan 2016 04:09:43 +0100
-Subject: [PATCH 45/66] mfd: mt6397: add MT6323 support to MT6397 driver
+Subject: [PATCH 45/78] mfd: mt6397: add MT6323 support to MT6397 driver
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 ---
diff --git a/target/linux/mediatek/patches-4.4/0046-regulator-Add-document-for-MT6323-regulator.patch b/target/linux/mediatek/patches-4.4/0046-regulator-Add-document-for-MT6323-regulator.patch
index f5b7f2363d..5beb8d186b 100644
--- a/target/linux/mediatek/patches-4.4/0046-regulator-Add-document-for-MT6323-regulator.patch
+++ b/target/linux/mediatek/patches-4.4/0046-regulator-Add-document-for-MT6323-regulator.patch
@@ -1,7 +1,7 @@
-From 9877cc960be38947b6bce371e3dce2be185dc337 Mon Sep 17 00:00:00 2001
+From f5b5c70021187c0ef70edf83865b62fec473e8fb Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Sun, 10 Jan 2016 17:31:46 +0100
-Subject: [PATCH 46/66] regulator: Add document for MT6323 regulator
+Subject: [PATCH 46/78] regulator: Add document for MT6323 regulator
 
 Signed-off-by: John Crispin <blogic@openwrt.org>
 Cc: devicetree@vger.kernel.org
diff --git a/target/linux/mediatek/patches-4.4/0047-regulator-mt6323-Add-support-for-MT6323-regulator.patch b/target/linux/mediatek/patches-4.4/0047-regulator-mt6323-Add-support-for-MT6323-regulator.patch
index 37d7fd1904..fc2b3d2fb2 100644
--- a/target/linux/mediatek/patches-4.4/0047-regulator-mt6323-Add-support-for-MT6323-regulator.patch
+++ b/target/linux/mediatek/patches-4.4/0047-regulator-mt6323-Add-support-for-MT6323-regulator.patch
@@ -1,7 +1,7 @@
-From 34163b123140c2668d52385bb9ab501cb025f943 Mon Sep 17 00:00:00 2001
+From 031a2ff537366855e88bc95e5d42ea522b6c5ad8 Mon Sep 17 00:00:00 2001
 From: Chen Zhong <chen.zhong@mediatek.com>
 Date: Fri, 8 Jan 2016 04:17:37 +0100
-Subject: [PATCH 47/66] regulator: mt6323: Add support for MT6323 regulator
+Subject: [PATCH 47/78] regulator: mt6323: Add support for MT6323 regulator
 
 The MT6323 is a regulator found on boards based on MediaTek MT7623 and
 probably other SoCs. It is a so called pmic and connects as a slave to
diff --git a/target/linux/mediatek/patches-4.4/0048-net-next-mediatek-document-MediaTek-SoC-ethernet-bin.patch b/target/linux/mediatek/patches-4.4/0048-net-next-mediatek-document-MediaTek-SoC-ethernet-bin.patch
index 34a033e450..3e668992ab 100644
--- a/target/linux/mediatek/patches-4.4/0048-net-next-mediatek-document-MediaTek-SoC-ethernet-bin.patch
+++ b/target/linux/mediatek/patches-4.4/0048-net-next-mediatek-document-MediaTek-SoC-ethernet-bin.patch
@@ -1,7 +1,7 @@
-From 13e64dd7fb55bf3948005863a494646874c22c1b Mon Sep 17 00:00:00 2001
+From b79b0519fb67c22cbed341c5e9dca5ad0aa4d15c Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 2 Mar 2016 07:18:52 +0100
-Subject: [PATCH 48/66] net-next: mediatek: document MediaTek SoC ethernet
+Subject: [PATCH 48/78] net-next: mediatek: document MediaTek SoC ethernet
  binding
 
 This adds the binding documentation for the MediaTek Ethernet
diff --git a/target/linux/mediatek/patches-4.4/0049-net-next-mediatek-add-support-for-MT7623-ethernet.patch b/target/linux/mediatek/patches-4.4/0049-net-next-mediatek-add-support-for-MT7623-ethernet.patch
index a3e3efd999..792e9e76a1 100644
--- a/target/linux/mediatek/patches-4.4/0049-net-next-mediatek-add-support-for-MT7623-ethernet.patch
+++ b/target/linux/mediatek/patches-4.4/0049-net-next-mediatek-add-support-for-MT7623-ethernet.patch
@@ -1,7 +1,7 @@
-From ce02aa9cebf5805427b874201b4ccb2a5e770597 Mon Sep 17 00:00:00 2001
+From 999621b6bfff903d16691799a23bfccac91c31df Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 2 Mar 2016 04:27:10 +0100
-Subject: [PATCH 49/66] net-next: mediatek: add support for MT7623 ethernet
+Subject: [PATCH 49/78] net-next: mediatek: add support for MT7623 ethernet
 
 Add ethernet support for MediaTek SoCs from the MT7623 family. These have
 dual GMAC. Depending on the exact version, there might be a built-in
diff --git a/target/linux/mediatek/patches-4.4/0050-net-next-mediatek-add-Kconfig-and-Makefile.patch b/target/linux/mediatek/patches-4.4/0050-net-next-mediatek-add-Kconfig-and-Makefile.patch
index d2bd7a866f..4c6ff0196b 100644
--- a/target/linux/mediatek/patches-4.4/0050-net-next-mediatek-add-Kconfig-and-Makefile.patch
+++ b/target/linux/mediatek/patches-4.4/0050-net-next-mediatek-add-Kconfig-and-Makefile.patch
@@ -1,7 +1,7 @@
-From e39d6547a391e3e32f88b6dde16dee271e905562 Mon Sep 17 00:00:00 2001
+From 0189b71d0ce9feef1c3fac7d9c3eb7dae57dabdf Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 2 Mar 2016 04:32:43 +0100
-Subject: [PATCH 50/66] net-next: mediatek: add Kconfig and Makefile
+Subject: [PATCH 50/78] net-next: mediatek: add Kconfig and Makefile
 
 This patch adds the Makefile and Kconfig required to make the driver build.
 
diff --git a/target/linux/mediatek/patches-4.4/0051-net-next-mediatek-add-an-entry-to-MAINTAINERS.patch b/target/linux/mediatek/patches-4.4/0051-net-next-mediatek-add-an-entry-to-MAINTAINERS.patch
index fe1f4af182..41c1b69aa9 100644
--- a/target/linux/mediatek/patches-4.4/0051-net-next-mediatek-add-an-entry-to-MAINTAINERS.patch
+++ b/target/linux/mediatek/patches-4.4/0051-net-next-mediatek-add-an-entry-to-MAINTAINERS.patch
@@ -1,7 +1,7 @@
-From 0ab2afe39e683c82b5c176047e81eeb2d1b9119c Mon Sep 17 00:00:00 2001
+From 76ac7ac355452b4a2cf5cf7b1d9ab5b08e349b4b Mon Sep 17 00:00:00 2001
 From: John Crispin <blogic@openwrt.org>
 Date: Wed, 2 Mar 2016 04:34:04 +0100
-Subject: [PATCH 51/66] net-next: mediatek: add an entry to MAINTAINERS
+Subject: [PATCH 51/78] net-next: mediatek: add an entry to MAINTAINERS
 
 Add myself and Felix as the Maintainers for the MediaTek ethernet driver.
 
diff --git a/target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch b/target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch
new file mode 100644
index 0000000000..8cc0075b93
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0052-mtd-nand-add-an-mtd_to_nand-helper.patch
@@ -0,0 +1,35 @@
+From 17b9724cf84aa32f15334c23d5df34ad3cb885f3 Mon Sep 17 00:00:00 2001
+From: Boris BREZILLON <boris.brezillon@free-electrons.com>
+Date: Mon, 16 Nov 2015 14:37:35 +0100
+Subject: [PATCH 52/78] mtd: nand: add an mtd_to_nand() helper
+
+Some drivers are retrieving the nand_chip pointer using the container_of
+macro on a struct wrapping both the nand_chip and the mtd_info struct while
+the standard way of retrieving this pointer is through mtd->priv.
+Provide an helper to do that.
+
+Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ include/linux/mtd/nand.h |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index 5a9d1d4..a4839b3 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -719,6 +719,11 @@ struct nand_chip {
+ 	void *priv;
+ };
+ 
++static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
++{
++	return mtd->priv;
++}
++
+ /*
+  * NAND Flash Manufacturer ID Codes
+  */
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0052-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch b/target/linux/mediatek/patches-4.4/0052-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
deleted file mode 100644
index 5e542916e0..0000000000
--- a/target/linux/mediatek/patches-4.4/0052-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 7809955b6f4319773a5ef561a5e98c61dc891a47 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Tue, 15 Mar 2016 10:18:49 +0300
-Subject: [PATCH 52/66] net: mediatek: checking for IS_ERR() instead of NULL
-
-of_phy_connect() returns NULL on error, it never returns error pointers.
-
-Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index ba3afa5..9759fe5 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -186,9 +186,9 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
- 
- 	phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
- 				mtk_phy_link_adjust, 0, phy_mode);
--	if (IS_ERR(phydev)) {
-+	if (!phydev) {
- 		dev_err(eth->dev, "could not connect to PHY\n");
--		return PTR_ERR(phydev);
-+		return -ENODEV;
- 	}
- 
- 	dev_info(eth->dev,
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch b/target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch
new file mode 100644
index 0000000000..29c0d1b427
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0053-mtd-nand-add-nand_to_mtd-helper.patch
@@ -0,0 +1,32 @@
+From 86e5fe2edbe2ca4f0d83a26d9b3d02620cd78f37 Mon Sep 17 00:00:00 2001
+From: Boris BREZILLON <boris.brezillon@free-electrons.com>
+Date: Tue, 1 Dec 2015 12:03:07 +0100
+Subject: [PATCH 53/78] mtd: nand: add nand_to_mtd() helper
+
+Add a new helper to retrieve the MTD device attached to a NAND chip.
+
+Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ include/linux/mtd/nand.h |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index a4839b3..c75424f 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -724,6 +724,11 @@ static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
+ 	return mtd->priv;
+ }
+ 
++static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
++{
++	return &chip->mtd;
++}
++
+ /*
+  * NAND Flash Manufacturer ID Codes
+  */
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0053-net-mediatek-unlock-on-error-in-mtk_tx_map.patch b/target/linux/mediatek/patches-4.4/0053-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
deleted file mode 100644
index 89ad003855..0000000000
--- a/target/linux/mediatek/patches-4.4/0053-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From a2559aaca7c4a9b80699147390efda51df20ac96 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Tue, 15 Mar 2016 10:19:04 +0300
-Subject: [PATCH 53/66] net: mediatek: unlock on error in mtk_tx_map()
-
-There was a missing unlock on the error path.
-
-Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 9759fe5..c2c2e206 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -661,6 +661,8 @@ err_dma:
- 		itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
- 	} while (itxd != txd);
- 
-+	spin_unlock_irqrestore(&eth->page_lock, flags);
-+
- 	return -ENOMEM;
- }
- 
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch b/target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch
new file mode 100644
index 0000000000..9cbc064075
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0054-mtd-nand-add-helpers-to-access-priv.patch
@@ -0,0 +1,39 @@
+From ca10b32de369729b8e7947fe1945e8e393d803cd Mon Sep 17 00:00:00 2001
+From: Boris BREZILLON <boris.brezillon@free-electrons.com>
+Date: Thu, 10 Dec 2015 09:00:39 +0100
+Subject: [PATCH 54/78] mtd: nand: add helpers to access ->priv
+
+Add two helpers to access the field reserved for private controller data.
+This makes it clearer what this field is reserved for and ease future
+refactoring.
+
+Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ include/linux/mtd/nand.h |   10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index c75424f..345f864 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -729,6 +729,16 @@ static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
+ 	return &chip->mtd;
+ }
+ 
++static inline void *nand_get_controller_data(struct nand_chip *chip)
++{
++	return chip->priv;
++}
++
++static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
++{
++	chip->priv = priv;
++}
++
+ /*
+  * NAND Flash Manufacturer ID Codes
+  */
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0054-net-mediatek-use-dma_addr_t-correctly.patch b/target/linux/mediatek/patches-4.4/0054-net-mediatek-use-dma_addr_t-correctly.patch
deleted file mode 100644
index d00d9b6bcb..0000000000
--- a/target/linux/mediatek/patches-4.4/0054-net-mediatek-use-dma_addr_t-correctly.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From ca4d9b6f3476e18e3136e488debdb35cd402d8d3 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:10 +0100
-Subject: [PATCH 54/66] net: mediatek: use dma_addr_t correctly
-
-dma_alloc_coherent() expects a dma_addr_t pointer as its argument,
-not an 'unsigned int', and gcc correctly warns about broken
-code in the mtk_init_fq_dma function:
-
-drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_init_fq_dma':
-drivers/net/ethernet/mediatek/mtk_eth_soc.c:463:13: error: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type [-Werror=incompatible-pointer-types]
-
-This changes the type of the local variable to dma_addr_t.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index c2c2e206..a005bc4 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -453,7 +453,7 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
- /* the qdma core needs scratch memory to be setup */
- static int mtk_init_fq_dma(struct mtk_eth *eth)
- {
--	unsigned int phy_ring_head, phy_ring_tail;
-+	dma_addr_t phy_ring_head, phy_ring_tail;
- 	int cnt = MTK_DMA_SIZE;
- 	dma_addr_t dma_addr;
- 	int i;
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch b/target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch
new file mode 100644
index 0000000000..29a6cd6f97
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0055-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch
@@ -0,0 +1,42 @@
+From b4a93bff70545d73337e2cae7209fd1ae4d9d8e8 Mon Sep 17 00:00:00 2001
+From: Boris BREZILLON <boris.brezillon@free-electrons.com>
+Date: Tue, 1 Dec 2015 12:03:06 +0100
+Subject: [PATCH 55/78] mtd: nand: embed an mtd_info structure into nand_chip
+
+Currently all NAND controller drivers are providing both the mtd_info and
+nand_chip struct and then let the NAND subsystem to initialize a few
+things before registering the mtd instance to the MTD layer.
+Embed an mtd_info field into nand_chip to add some consistency to all NAND
+controller drivers.
+This change will also help factorizing boilerplate code copied in all NAND
+drivers.
+
+Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ include/linux/mtd/nand.h |    2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index 345f864..1ded588 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -540,6 +540,7 @@ struct nand_buffers {
+ 
+ /**
+  * struct nand_chip - NAND Private Flash Chip Data
++ * @mtd:		MTD device registered to the MTD framework
+  * @IO_ADDR_R:		[BOARDSPECIFIC] address to read the 8 I/O lines of the
+  *			flash device
+  * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the
+@@ -640,6 +641,7 @@ struct nand_buffers {
+  */
+ 
+ struct nand_chip {
++	struct mtd_info mtd;
+ 	void __iomem *IO_ADDR_R;
+ 	void __iomem *IO_ADDR_W;
+ 
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0055-net-mediatek-remove-incorrect-dma_mask-assignment.patch b/target/linux/mediatek/patches-4.4/0055-net-mediatek-remove-incorrect-dma_mask-assignment.patch
deleted file mode 100644
index 4bf0947db8..0000000000
--- a/target/linux/mediatek/patches-4.4/0055-net-mediatek-remove-incorrect-dma_mask-assignment.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 9d602a7040c0fe9c81f2beffb3c277442a6d9ea2 Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:11 +0100
-Subject: [PATCH 55/66] net: mediatek: remove incorrect dma_mask assignment
-
-Device drivers should not mess with the DMA mask directly,
-but instead call dma_set_mask() etc if needed.
-
-In case of the mtk_eth_soc driver, the mask already gets set
-correctly when the device is created, and setting it again
-is against the documented API.
-
-This removes the incorrect setting.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index a005bc4..fcd4ed7 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1678,9 +1678,6 @@ static int mtk_probe(struct platform_device *pdev)
- 	struct mtk_eth *eth;
- 	int err;
- 
--	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
--	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
--
- 	device_reset(&pdev->dev);
- 
- 	match = of_match_device(of_mtk_match, &pdev->dev);
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch b/target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch
new file mode 100644
index 0000000000..a7ec4c44ac
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0056-mtd-add-get-set-of_node-flash_node-helpers.patch
@@ -0,0 +1,87 @@
+From 3ed29e95facfd50427721f8e9093dc36c2304e42 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 22 Mar 2016 03:52:07 +0100
+Subject: [PATCH 56/78] mtd: add get/set of_node/flash_node helpers
+
+We are going to begin using the mtd->dev.of_node field for MTD device
+nodes, so let's add helpers for it. Also, we'll be making some
+conversions on spi_nor (and nand_chip eventually) too, so get that ready
+with their own helpers.
+
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+---
+ include/linux/mtd/mtd.h     |   11 +++++++++++
+ include/linux/mtd/nand.h    |   11 +++++++++++
+ include/linux/mtd/spi-nor.h |   11 +++++++++++
+ 3 files changed, 33 insertions(+)
+
+diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
+index f17fa75..cc84923 100644
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -254,6 +254,17 @@ struct mtd_info {
+ 	int usecount;
+ };
+ 
++static inline void mtd_set_of_node(struct mtd_info *mtd,
++				   struct device_node *np)
++{
++	mtd->dev.of_node = np;
++}
++
++static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
++{
++	return mtd->dev.of_node;
++}
++
+ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
+ int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
+ 	      void **virt, resource_size_t *phys);
+diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
+index 1ded588..3c34ca4 100644
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -741,6 +741,17 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
+ 	chip->priv = priv;
+ }
+ 
++static inline void nand_set_flash_node(struct nand_chip *chip,
++				       struct device_node *np)
++{
++	chip->flash_node = np;
++}
++
++static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
++{
++	return chip->flash_node;
++}
++
+ /*
+  * NAND Flash Manufacturer ID Codes
+  */
+diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
+index c8723b6..6d991df 100644
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -185,6 +185,17 @@ struct spi_nor {
+ 	void *priv;
+ };
+ 
++static inline void spi_nor_set_flash_node(struct spi_nor *nor,
++					  struct device_node *np)
++{
++	nor->flash_node = np;
++}
++
++static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
++{
++	return nor->flash_node;
++}
++
+ /**
+  * spi_nor_scan() - scan the SPI NOR
+  * @nor:	the spi_nor structure
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0056-net-mediatek-check-device_reset-return-code.patch b/target/linux/mediatek/patches-4.4/0056-net-mediatek-check-device_reset-return-code.patch
deleted file mode 100644
index 00e5c8bfc8..0000000000
--- a/target/linux/mediatek/patches-4.4/0056-net-mediatek-check-device_reset-return-code.patch
+++ /dev/null
@@ -1,38 +0,0 @@
-From b461f1a8dfcf32f289a559b6eba4e784b37d121c Mon Sep 17 00:00:00 2001
-From: Arnd Bergmann <arnd@arndb.de>
-Date: Mon, 14 Mar 2016 15:07:12 +0100
-Subject: [PATCH 56/66] net: mediatek: check device_reset return code
-
-The device_reset() function may fail, so we have to check
-its return value, e.g. to make deferred probing work correctly.
-gcc warns about it because of the warn_unused_result attribute:
-
-drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_probe':
-drivers/net/ethernet/mediatek/mtk_eth_soc.c:1679:2: error: ignoring return value of 'device_reset', declared with attribute warn_unused_result [-Werror=unused-result]
-
-This adds the trivial error check to propagate the return value
-to the generic platform device probe code.
-
-Signed-off-by: Arnd Bergmann <arnd@arndb.de>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index fcd4ed7..7f2126b 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -1678,7 +1678,9 @@ static int mtk_probe(struct platform_device *pdev)
- 	struct mtk_eth *eth;
- 	int err;
- 
--	device_reset(&pdev->dev);
-+	err = device_reset(&pdev->dev);
-+	if (err)
-+		return err;
- 
- 	match = of_match_device(of_mtk_match, &pdev->dev);
- 	soc = (struct mtk_soc_data *)match->data;
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch b/target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
new file mode 100644
index 0000000000..e42d66092a
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0057-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
@@ -0,0 +1,64 @@
+From 5f0a1fa77e5d53b8dbb751fcfc59c7ef3d78ddf2 Mon Sep 17 00:00:00 2001
+From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+Date: Wed, 2 Mar 2016 12:00:11 -0500
+Subject: [PATCH 57/78] mtd: mediatek: device tree docs for MTK Smart Device
+ Gen1 NAND
+
+This patch adds documentation support for Smart Device Gen1 type of
+NAND controllers.
+
+Mediatek's SoC 2701 is one of the SoCs that implements this controller.
+
+Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+---
+ .../devicetree/bindings/mtd/mtksdg1-nand.txt       |   38 ++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
+
+diff --git a/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt b/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
+new file mode 100644
+index 0000000..129d17b
+--- /dev/null
++++ b/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
+@@ -0,0 +1,38 @@
++MTK Smart Device SoCs NAND controller DT binding
++
++Required properties:
++- compatible:          Should be "mediatek,mt2701-nfc".
++- reg:                 The first contains base physical address and size of
++                       NAND controller's registers. The second contains base
++                       physical address and size of NAND ECC engine.
++- interrupts:          the NFC NFI interrupt, and the NFC ECC interrupt
++- clocks:              NAND controller clocks.
++- clock-names:         NAND controller clocks internal name.
++- vmch-supply:         NAND power supply.
++- #address-cells:      Partition address, should be set 1.
++- #size-cells:         Partition size, should be set 1.
++
++Optional properties:
++
++nand-on-flash-bbt:  Use a flash based bad block table.
++
++Optional subnodes:
++- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
++
++Example:
++
++	nand: nand@1100d000 {
++		compatible = "mediatek,mt2701-nfc";
++		reg = <0 0x1100d000 0 0x1000>, <0 0x1100e000 0 0x1000>;
++		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>,
++			<GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
++		clocks = <&pericfg CLK_PERI_NFI>, <&pericfg CLK_PERI_NFI_ECC>,
++			<&pericfg CLK_PERI_NFI_PAD>;
++		clock-names = "nfi_ck", "nfi_ecc_ck", "nfi_pad_ck";
++		vmch-supply = <&mt6323_vmch_reg>;
++		status = "disabled";
++		#address-cells = <1>;
++		#size-cells = <1>;
++
++		...
++	};
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0057-net-mediatek-out-of-tree-fixes.patch b/target/linux/mediatek/patches-4.4/0057-net-mediatek-out-of-tree-fixes.patch
deleted file mode 100644
index 1e78d49999..0000000000
--- a/target/linux/mediatek/patches-4.4/0057-net-mediatek-out-of-tree-fixes.patch
+++ /dev/null
@@ -1,2469 +0,0 @@
-From cee958b55f35f953481c2ddf9609dbd018ef5979 Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Mon, 21 Mar 2016 16:36:22 +0100
-Subject: [PATCH 57/66] net: mediatek: out of tree fixes
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- arch/arm/boot/dts/mt7623-evb.dts            |    1 -
- arch/arm/boot/dts/mt7623.dtsi               |   40 +-
- drivers/net/ethernet/mediatek/Makefile      |    2 +-
- drivers/net/ethernet/mediatek/gsw_mt7620.h  |  250 +++++++
- drivers/net/ethernet/mediatek/gsw_mt7623.c  | 1058 +++++++++++++++++++++++++++
- drivers/net/ethernet/mediatek/mt7530.c      |  808 ++++++++++++++++++++
- drivers/net/ethernet/mediatek/mt7530.h      |   20 +
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |   41 +-
- drivers/net/ethernet/mediatek/mtk_eth_soc.h |    5 +
- 9 files changed, 2202 insertions(+), 23 deletions(-)
- create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
- create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c
- create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
- create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
-
-diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
-index 5e9381d..bc2b3f1 100644
---- a/arch/arm/boot/dts/mt7623-evb.dts
-+++ b/arch/arm/boot/dts/mt7623-evb.dts
-@@ -425,7 +425,6 @@
- &usb1 {
- 	vusb33-supply = <&mt6323_vusb_reg>;
- 	vbus-supply = <&usb_p1_vbus>;
--//	mediatek,wakeup-src = <1>;
- 	status = "okay";
- };
- 
-diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
-index ec19283..0c65045 100644
---- a/arch/arm/boot/dts/mt7623.dtsi
-+++ b/arch/arm/boot/dts/mt7623.dtsi
-@@ -452,23 +452,30 @@
- 	};
- 
- 	ethsys: syscon@1b000000 {
--		#address-cells = <1>;
--		#size-cells = <1>;
- 		compatible = "mediatek,mt2701-ethsys", "syscon";
- 		reg = <0 0x1b000000 0 0x1000>;
-+		#reset-cells = <1>;
- 		#clock-cells = <1>;
- 	};
- 
- 	eth: ethernet@1b100000 {
- 		compatible = "mediatek,mt7623-eth";
--		reg = <0 0x1b100000 0 0x10000>;
-+		reg = <0 0x1b100000 0 0x20000>;
- 	
--		clocks = <&topckgen CLK_TOP_ETHIF_SEL>;
--		clock-names = "ethif";
-+		clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
-+			 <&ethsys CLK_ETHSYS_ESW>,
-+			 <&ethsys CLK_ETHSYS_GP2>,
-+			 <&ethsys CLK_ETHSYS_GP1>;
-+		clock-names = "ethif", "esw", "gp2", "gp1";
- 		interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
- 		power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
- 
-+		resets = <&ethsys 6>;
-+		reset-names = "eth";
-+
- 		mediatek,ethsys = <&ethsys>;
-+		mediatek,pctl = <&syscfg_pctl_a>;
-+
- 		mediatek,switch = <&gsw>;
- 
- 		#address-cells = <1>;
-@@ -480,6 +487,8 @@
- 			compatible = "mediatek,eth-mac";
- 			reg = <0>;
- 
-+			phy-handle = <&phy4>;
-+
- 			status = "disabled";
- 		};
- 
-@@ -487,6 +496,7 @@
- 			compatible = "mediatek,eth-mac";
- 			reg = <1>;
- 
-+			phy-handle = <&phy5>;
- 			status = "disabled";
- 		};
- 	
-@@ -494,6 +504,16 @@
- 			#address-cells = <1>;
- 			#size-cells = <0>;
- 
-+			phy4: ethernet-phy@4 {
-+				reg = <4>;
-+				phy-mode = "rgmii";
-+			};
-+
-+			phy5: ethernet-phy@5 {
-+				reg = <5>;
-+				phy-mode = "rgmii";
-+			};
-+
- 			phy1f: ethernet-phy@1f {
- 				reg = <0x1f>;
- 				phy-mode = "rgmii";
-@@ -503,14 +523,12 @@
- 
- 	gsw: switch@1b100000 {
- 		compatible = "mediatek,mt7623-gsw";
--		reg = <0 0x1b110000 0 0x300000>;
- 		interrupt-parent = <&pio>;
- 		interrupts = <168 IRQ_TYPE_EDGE_RISING>;
--		clocks = <&apmixedsys CLK_APMIXED_TRGPLL>,
--			 <&ethsys CLK_ETHSYS_ESW>,
--			 <&ethsys CLK_ETHSYS_GP2>,
--			 <&ethsys CLK_ETHSYS_GP1>;
--		clock-names = "trgpll", "esw", "gp2", "gp1";
-+		resets = <&ethsys 2>;
-+		reset-names = "eth";
-+		clocks = <&apmixedsys CLK_APMIXED_TRGPLL>;
-+		clock-names = "trgpll";
- 		mt7530-supply = <&mt6323_vpa_reg>;
- 		mediatek,pctl-regmap = <&syscfg_pctl_a>;
- 		mediatek,ethsys = <&ethsys>;
-diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
-index aa3f1c8..82001c4 100644
---- a/drivers/net/ethernet/mediatek/Makefile
-+++ b/drivers/net/ethernet/mediatek/Makefile
-@@ -2,4 +2,4 @@
- # Makefile for the Mediatek SoCs built-in ethernet macs
- #
- 
--obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth_soc.o
-+obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mt7530.o gsw_mt7623.o mtk_eth_soc.o
-diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
-new file mode 100644
-index 0000000..7013803
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
-@@ -0,0 +1,250 @@
-+/*   This program is free software; you can redistribute it and/or modify
-+ *   it under the terms of the GNU General Public License as published by
-+ *   the Free Software Foundation; version 2 of the License
-+ *
-+ *   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 the
-+ *   GNU General Public License for more details.
-+ *
-+ *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
-+ *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
-+ *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
-+ */
-+
-+#ifndef _RALINK_GSW_MT7620_H__
-+#define _RALINK_GSW_MT7620_H__
-+
-+#define GSW_REG_PHY_TIMEOUT	(5 * HZ)
-+
-+#define MT7620_GSW_REG_PIAC	0x0004
-+
-+#define GSW_NUM_VLANS		16
-+#define GSW_NUM_VIDS		4096
-+#define GSW_NUM_PORTS		7
-+#define GSW_PORT6		6
-+
-+#define GSW_MDIO_ACCESS		BIT(31)
-+#define GSW_MDIO_READ		BIT(19)
-+#define GSW_MDIO_WRITE		BIT(18)
-+#define GSW_MDIO_START		BIT(16)
-+#define GSW_MDIO_ADDR_SHIFT	20
-+#define GSW_MDIO_REG_SHIFT	25
-+
-+#define GSW_REG_PORT_PMCR(x)	(0x3000 + (x * 0x100))
-+#define GSW_REG_PORT_STATUS(x)	(0x3008 + (x * 0x100))
-+#define GSW_REG_SMACCR0		0x3fE4
-+#define GSW_REG_SMACCR1		0x3fE8
-+#define GSW_REG_CKGCR		0x3ff0
-+
-+#define GSW_REG_IMR		0x7008
-+#define GSW_REG_ISR		0x700c
-+#define GSW_REG_GPC1		0x7014
-+
-+#define SYSC_REG_CHIP_REV_ID	0x0c
-+#define SYSC_REG_CFG		0x10
-+#define SYSC_REG_CFG1		0x14
-+#define RST_CTRL_MCM		BIT(2)
-+#define SYSC_PAD_RGMII2_MDIO	0x58
-+#define SYSC_GPIO_MODE		0x60
-+
-+#define PORT_IRQ_ST_CHG		0x7f
-+
-+#define MT7621_ESW_PHY_POLLING	0x0000
-+#define MT7620_ESW_PHY_POLLING	0x7000
-+
-+#define	PMCR_IPG		BIT(18)
-+#define	PMCR_MAC_MODE		BIT(16)
-+#define	PMCR_FORCE		BIT(15)
-+#define	PMCR_TX_EN		BIT(14)
-+#define	PMCR_RX_EN		BIT(13)
-+#define	PMCR_BACKOFF		BIT(9)
-+#define	PMCR_BACKPRES		BIT(8)
-+#define	PMCR_RX_FC		BIT(5)
-+#define	PMCR_TX_FC		BIT(4)
-+#define	PMCR_SPEED(_x)		(_x << 2)
-+#define	PMCR_DUPLEX		BIT(1)
-+#define	PMCR_LINK		BIT(0)
-+
-+#define PHY_AN_EN		BIT(31)
-+#define PHY_PRE_EN		BIT(30)
-+#define PMY_MDC_CONF(_x)	((_x & 0x3f) << 24)
-+
-+/* ethernet subsystem config register */
-+#define ETHSYS_SYSCFG0		0x14
-+/* ethernet subsystem clock register */
-+#define ETHSYS_CLKCFG0		0x2c
-+#define ETHSYS_TRGMII_CLK_SEL362_5	BIT(11)
-+
-+/* p5 RGMII wrapper TX clock control register */
-+#define MT7530_P5RGMIITXCR	0x7b04
-+/* p5 RGMII wrapper RX clock control register */
-+#define MT7530_P5RGMIIRXCR	0x7b00
-+/* TRGMII TDX ODT registers */
-+#define MT7530_TRGMII_TD0_ODT	0x7a54
-+#define MT7530_TRGMII_TD1_ODT	0x7a5c
-+#define MT7530_TRGMII_TD2_ODT	0x7a64
-+#define MT7530_TRGMII_TD3_ODT	0x7a6c
-+#define MT7530_TRGMII_TD4_ODT	0x7a74
-+#define MT7530_TRGMII_TD5_ODT	0x7a7c
-+/* TRGMII TCK ctrl register */
-+#define MT7530_TRGMII_TCK_CTRL	0x7a78
-+/* TRGMII Tx ctrl register */
-+#define MT7530_TRGMII_TXCTRL	0x7a40
-+/* port 6 extended control register */
-+#define MT7530_P6ECR            0x7830
-+/* IO driver control register */
-+#define MT7530_IO_DRV_CR	0x7810
-+/* top signal control register */
-+#define MT7530_TOP_SIG_CTRL	0x7808
-+/* modified hwtrap register */
-+#define MT7530_MHWTRAP		0x7804
-+/* hwtrap status register */
-+#define MT7530_HWTRAP		0x7800
-+/* status interrupt register */
-+#define MT7530_SYS_INT_STS	0x700c
-+/* system nterrupt register */
-+#define MT7530_SYS_INT_EN	0x7008
-+/* system control register */
-+#define MT7530_SYS_CTRL		0x7000
-+/* port MAC status register */
-+#define MT7530_PMSR_P(x)	(0x3008 + (x * 0x100))
-+/* port MAC control register */
-+#define MT7530_PMCR_P(x)	(0x3000 + (x * 0x100))
-+
-+#define MT7621_XTAL_SHIFT	6
-+#define MT7621_XTAL_MASK	0x7
-+#define MT7621_XTAL_25		6
-+#define MT7621_XTAL_40		3
-+#define MT7621_MDIO_DRV_MASK	(3 << 4)
-+#define MT7621_GE1_MODE_MASK	(3 << 12)
-+
-+#define TRGMII_TXCTRL_TXC_INV	BIT(30)
-+#define P6ECR_INTF_MODE_RGMII	BIT(1)
-+#define P5RGMIIRXCR_C_ALIGN	BIT(8)
-+#define P5RGMIIRXCR_DELAY_2	BIT(1)
-+#define P5RGMIITXCR_DELAY_2	(BIT(8) | BIT(2))
-+
-+/* TOP_SIG_CTRL bits */
-+#define TOP_SIG_CTRL_NORMAL	(BIT(17) | BIT(16))
-+
-+/* MHWTRAP bits */
-+#define MHWTRAP_MANUAL		BIT(16)
-+#define MHWTRAP_P5_MAC_SEL	BIT(13)
-+#define MHWTRAP_P6_DIS		BIT(8)
-+#define MHWTRAP_P5_RGMII_MODE	BIT(7)
-+#define MHWTRAP_P5_DIS		BIT(6)
-+#define MHWTRAP_PHY_ACCESS	BIT(5)
-+
-+/* HWTRAP bits */
-+#define HWTRAP_XTAL_SHIFT	9
-+#define HWTRAP_XTAL_MASK	0x3
-+
-+/* SYS_CTRL bits */
-+#define SYS_CTRL_SW_RST		BIT(1)
-+#define SYS_CTRL_REG_RST	BIT(0)
-+
-+/* PMCR bits */
-+#define PMCR_IFG_XMIT_96	BIT(18)
-+#define PMCR_MAC_MODE		BIT(16)
-+#define PMCR_FORCE_MODE		BIT(15)
-+#define PMCR_TX_EN		BIT(14)
-+#define PMCR_RX_EN		BIT(13)
-+#define PMCR_BACK_PRES_EN	BIT(9)
-+#define PMCR_BACKOFF_EN		BIT(8)
-+#define PMCR_TX_FC_EN		BIT(5)
-+#define PMCR_RX_FC_EN		BIT(4)
-+#define PMCR_FORCE_SPEED_1000	BIT(3)
-+#define PMCR_FORCE_FDX		BIT(1)
-+#define PMCR_FORCE_LNK		BIT(0)
-+#define PMCR_FIXED_LINK		(PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
-+				 PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
-+				 PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
-+				 PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
-+				 PMCR_FORCE_LNK)
-+
-+#define PMCR_FIXED_LINK_FC	(PMCR_FIXED_LINK | \
-+				 PMCR_TX_FC_EN | PMCR_RX_FC_EN)
-+
-+/* TRGMII control registers */
-+#define GSW_INTF_MODE		0x390
-+#define GSW_TRGMII_TD0_ODT	0x354
-+#define GSW_TRGMII_TD1_ODT	0x35c
-+#define GSW_TRGMII_TD2_ODT	0x364
-+#define GSW_TRGMII_TD3_ODT	0x36c
-+#define GSW_TRGMII_TXCTL_ODT	0x374
-+#define GSW_TRGMII_TCK_ODT	0x37c
-+#define GSW_TRGMII_RCK_CTRL	0x300
-+
-+#define INTF_MODE_TRGMII	BIT(1)
-+#define TRGMII_RCK_CTRL_RX_RST	BIT(31)
-+
-+
-+/* possible XTAL speed */
-+#define	MT7623_XTAL_40		0
-+#define MT7623_XTAL_20		1
-+#define MT7623_XTAL_25		3
-+
-+/* GPIO port control registers */
-+#define	GPIO_OD33_CTRL8		0x4c0
-+#define	GPIO_BIAS_CTRL		0xed0
-+#define GPIO_DRV_SEL10		0xf00
-+
-+/* on MT7620 the functio of port 4 can be software configured */
-+enum {
-+	PORT4_EPHY = 0,
-+	PORT4_EXT,
-+};
-+
-+/* struct mt7620_gsw -	the structure that holds the SoC specific data
-+ * @dev:		The Device struct
-+ * @base:		The base address
-+ * @piac_offset:	The PIAC base may change depending on SoC
-+ * @irq:		The IRQ we are using
-+ * @port4:		The port4 mode on MT7620
-+ * @autopoll:		Is MDIO autopolling enabled
-+ * @ethsys:		The ethsys register map
-+ * @pctl:		The pin control register map
-+ * @clk_trgpll:		The trgmii pll clock
-+ */
-+struct mt7620_gsw {
-+	struct mtk_eth		*eth;
-+	struct device		*dev;
-+	void __iomem		*base;
-+	u32			piac_offset;
-+	int			irq;
-+	int			port4;
-+	unsigned long int	autopoll;
-+
-+	struct regmap		*ethsys;
-+	struct regmap		*pctl;
-+
-+	struct clk		*clk_trgpll;
-+
-+	int			trgmii_force;
-+};
-+
-+/* switch register I/O wrappers */
-+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
-+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
-+
-+/* the callback used by the driver core to bringup the switch */
-+int mtk_gsw_init(struct mtk_eth *eth);
-+
-+/* MDIO access wrappers */
-+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
-+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
-+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
-+int mt7620_has_carrier(struct mtk_eth *eth);
-+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
-+			     int speed, int duplex);
-+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
-+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
-+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
-+
-+u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
-+		      u32 phy_register, u32 write_data);
-+u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
-+void mt7620_handle_carrier(struct mtk_eth *eth);
-+
-+#endif
-diff --git a/drivers/net/ethernet/mediatek/gsw_mt7623.c b/drivers/net/ethernet/mediatek/gsw_mt7623.c
-new file mode 100644
-index 0000000..4e486af
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
-@@ -0,0 +1,1058 @@
-+/*   This program is free software; you can redistribute it and/or modify
-+ *   it under the terms of the GNU General Public License as published by
-+ *   the Free Software Foundation; version 2 of the License
-+ *
-+ *   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 the
-+ *   GNU General Public License for more details.
-+ *
-+ *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
-+ *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
-+ *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/types.h>
-+#include <linux/platform_device.h>
-+#include <linux/of_device.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_gpio.h>
-+#include <linux/clk.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/regulator/consumer.h>
-+#include <linux/pm_runtime.h>
-+#include <linux/regmap.h>
-+#include <linux/reset.h>
-+#include <linux/mii.h>
-+#include <linux/interrupt.h>
-+#include <linux/netdevice.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/phy.h>
-+#include <linux/ethtool.h>
-+#include <linux/version.h>
-+#include <linux/atomic.h>
-+
-+#include "mtk_eth_soc.h"
-+#include "gsw_mt7620.h"
-+#include "mt7530.h"
-+
-+#define ETHSYS_CLKCFG0			0x2c
-+#define ETHSYS_TRGMII_CLK_SEL362_5	BIT(11)
-+
-+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
-+{
-+	_mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+	_mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
-+	_mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16);
-+}
-+
-+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
-+{
-+	u16 high, low;
-+
-+	_mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+	low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf);
-+	high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10);
-+
-+	return (high << 16) | (low & 0xffff);
-+}
-+
-+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
-+{
-+	u32 val = mt7530_mdio_r32(gsw, reg);
-+
-+	val &= mask;
-+	val |= set;
-+	mt7530_mdio_w32(gsw, reg, val);
-+}
-+
-+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
-+{
-+	mtk_w32(gsw->eth, val, reg + 0x10000);
-+}
-+
-+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
-+{
-+	return mtk_r32(gsw->eth, reg + 0x10000);
-+}
-+
-+void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg)
-+{
-+	u32 val = mtk_switch_r32(gsw, reg);
-+
-+	val &= mask;
-+	val |= set;
-+
-+	mtk_switch_w32(gsw, val, reg);
-+}
-+
-+int mt7623_gsw_config(struct mtk_eth *eth)
-+{
-+	if (eth->mii_bus && eth->mii_bus->phy_map[0x1f])
-+		mt7530_probe(eth->dev, NULL, eth->mii_bus, 1);
-+
-+	return 0;
-+}
-+
-+static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth)
-+{
-+	struct mtk_eth *eth = (struct mtk_eth *)_eth;
-+	struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
-+	u32 reg, i;
-+
-+	reg = mt7530_mdio_r32(gsw, 0x700c);
-+
-+	for (i = 0; i < 5; i++)
-+		if (reg & BIT(i)) {
-+			unsigned int link;
-+
-+			link = mt7530_mdio_r32(gsw,
-+					       0x3008 + (i * 0x100)) & 0x1;
-+
-+			if (link)
-+				dev_info(gsw->dev,
-+					 "port %d link up\n", i);
-+			else
-+				dev_info(gsw->dev,
-+					 "port %d link down\n", i);
-+		}
-+
-+//	mt7620_handle_carrier(eth);
-+	mt7530_mdio_w32(gsw, 0x700c, 0x1f);
-+
-+	return IRQ_HANDLED;
-+}
-+
-+static void wait_loop(struct mt7620_gsw *gsw)
-+{
-+	int i;
-+	int read_data;
-+
-+	for (i = 0; i < 320; i = i + 1)
-+		read_data = mtk_switch_r32(gsw, 0x610);
-+}
-+
-+static void trgmii_calibration_7623(struct mt7620_gsw *gsw)
-+{
-+
-+	unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };	/* minumum delay for all correct */
-+	unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };	/* maximum delay for all correct */
-+	unsigned int final_tap[5];
-+	unsigned int rxc_step_size;
-+	unsigned int rxd_step_size;
-+	unsigned int read_data;
-+	unsigned int tmp;
-+	unsigned int rd_wd;
-+	int i;
-+	unsigned int err_cnt[5];
-+	unsigned int init_toggle_data;
-+	unsigned int err_flag[5];
-+	unsigned int err_total_flag;
-+	unsigned int training_word;
-+	unsigned int rd_tap;
-+	u32 val;
-+
-+	u32 TRGMII_7623_base;
-+	u32 TRGMII_7623_RD_0;
-+	u32 TRGMII_RCK_CTRL;
-+
-+	TRGMII_7623_base = 0x300;	/* 0xFB110300 */
-+	TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10;
-+	TRGMII_RCK_CTRL = TRGMII_7623_base;
-+	rxd_step_size = 0x1;
-+	rxc_step_size = 0x4;
-+	init_toggle_data = 0x00000055;
-+	training_word = 0x000000AC;
-+
-+	/* RX clock gating in MT7623 */
-+	mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
-+
-+	/* Assert RX  reset in MT7623 */
-+	mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00);
-+
-+	/* Set TX OE edge in  MT7623 */
-+	mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78);
-+
-+	/* Disable RX clock gating in MT7623 */
-+	mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
-+
-+	/* Release RX reset in MT7623 */
-+	mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base);
-+
-+	for (i = 0; i < 5; i++)
-+		mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
-+
-+	pr_err("Enable Training Mode in MT7530\n");
-+	read_data = mt7530_mdio_r32(gsw, 0x7A40);
-+	read_data |= 0xC0000000;
-+	mt7530_mdio_w32(gsw, 0x7A40, read_data);	/* Enable Training Mode in MT7530 */
-+	err_total_flag = 0;
-+	pr_err("Adjust RXC delay in MT7623\n");
-+	read_data = 0x0;
-+	while (err_total_flag == 0 && read_data != 0x68) {
-+		pr_err("2nd Enable EDGE CHK in MT7623\n");
-+		/* Enable EDGE CHK in MT7623 */
-+		for (i = 0; i < 5; i++)
-+			    mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+
-+		wait_loop(gsw);
-+		err_total_flag = 1;
-+		for (i = 0; i < 5; i++) {
-+			err_cnt[i] =
-+			    mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8;
-+			err_cnt[i] &= 0x0000000f;
-+			rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16;
-+			rd_wd &= 0x000000ff;
-+			val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+			pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n",
-+			       err_cnt[i], rd_wd, val);
-+			if (err_cnt[i] != 0) {
-+				err_flag[i] = 1;
-+			} else if (rd_wd != 0x55) {
-+				err_flag[i] = 1;
-+			} else {
-+				err_flag[i] = 0;
-+			}
-+			err_total_flag = err_flag[i] & err_total_flag;
-+		}
-+
-+		pr_err("2nd Disable EDGE CHK in MT7623\n");
-+		/* Disable EDGE CHK in MT7623 */
-+		for (i = 0; i < 5; i++)
-+			    mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+		wait_loop(gsw);
-+		pr_err("2nd Disable EDGE CHK in MT7623\n");
-+		/* Adjust RXC delay */
-+		/* RX clock gating in MT7623 */
-+		mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
-+		read_data = mtk_switch_r32(gsw, TRGMII_7623_base);
-+		if (err_total_flag == 0) {
-+			tmp = (read_data & 0x0000007f) + rxc_step_size;
-+			pr_err(" RXC delay = %d\n", tmp); 
-+			read_data >>= 8;
-+			read_data &= 0xffffff80;
-+			read_data |= tmp;
-+			read_data <<= 8;
-+			read_data &= 0xffffff80;
-+			read_data |= tmp;
-+			mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
-+		} else {
-+			tmp = (read_data & 0x0000007f) + 16;
-+			pr_err(" RXC delay = %d\n", tmp); 
-+			read_data >>= 8;
-+			read_data &= 0xffffff80;
-+			read_data |= tmp;
-+			read_data <<= 8;
-+			read_data &= 0xffffff80;
-+			read_data |= tmp;
-+			mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
-+		}
-+		read_data &= 0x000000ff;
-+
-+		/* Disable RX clock gating in MT7623 */
-+		mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
-+		for (i = 0; i < 5; i++)
-+			mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
-+	}
-+
-+	/* Read RD_WD MT7623 */
-+	for (i = 0; i < 5; i++) {
-+		rd_tap = 0;
-+		while (err_flag[i] != 0 && rd_tap != 128) {
-+			/* Enable EDGE CHK in MT7623 */
-+			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+			wait_loop(gsw);
-+
-+			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+			err_cnt[i] = (read_data >> 8) & 0x0000000f;	/* Read MT7623 Errcnt */
-+			rd_wd = (read_data >> 16) & 0x000000ff;
-+			if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+				err_flag[i] = 1;
-+			} else {
-+				err_flag[i] = 0;
-+			}
-+			/* Disable EDGE CHK in MT7623 */
-+			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+			wait_loop(gsw);
-+			if (err_flag[i] != 0) {
-+				rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7623 */
-+				read_data = (read_data & 0xffffff80) | rd_tap;
-+				mtk_switch_w32(gsw, read_data,
-+					TRGMII_7623_RD_0 + i * 8);
-+				tap_a[i] = rd_tap;
-+			} else {
-+				rd_tap = (read_data & 0x0000007f) + 48;
-+				read_data = (read_data & 0xffffff80) | rd_tap;
-+				mtk_switch_w32(gsw, read_data,
-+					TRGMII_7623_RD_0 + i * 8);
-+			}
-+
-+		}
-+		pr_err("MT7623 %dth bit  Tap_a = %d\n", i, tap_a[i]);
-+	}
-+	/* pr_err("Last While Loop\n"); */
-+	for (i = 0; i < 5; i++) {
-+		while ((err_flag[i] == 0) && (rd_tap != 128)) {
-+			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+			rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7623 */
-+			read_data = (read_data & 0xffffff80) | rd_tap;
-+			mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
-+			/* Enable EDGE CHK in MT7623 */
-+			val =
-+			    mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000;
-+			val &= 0x4fffffff;
-+			mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8);
-+			wait_loop(gsw);
-+			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
-+			err_cnt[i] = (read_data >> 8) & 0x0000000f;	/* Read MT7623 Errcnt */
-+			rd_wd = (read_data >> 16) & 0x000000ff;
-+			if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+				err_flag[i] = 1;
-+			} else {
-+				err_flag[i] = 0;
-+			}
-+
-+			/* Disable EDGE CHK in MT7623 */
-+			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
-+			wait_loop(gsw);
-+
-+		}
-+
-+		tap_b[i] = rd_tap;	/* -rxd_step_size; */
-+		pr_err("MT7623 %dth bit  Tap_b = %d\n", i, tap_b[i]);
-+		final_tap[i] = (tap_a[i] + tap_b[i]) / 2;	/* Calculate RXD delay = (TAP_A + TAP_B)/2 */
-+		read_data = (read_data & 0xffffff80) | final_tap[i];
-+		mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
-+	}
-+
-+	read_data = mt7530_mdio_r32(gsw, 0x7A40);
-+	read_data &= 0x3fffffff;
-+	mt7530_mdio_w32(gsw, 0x7A40, read_data);
-+}
-+
-+static void trgmii_calibration_7530(struct mt7620_gsw *gsw)
-+{
-+
-+	unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };
-+	unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };
-+	unsigned int final_tap[5];
-+	unsigned int rxc_step_size;
-+	unsigned int rxd_step_size;
-+	unsigned int read_data;
-+	unsigned int tmp = 0;
-+	int i;
-+	unsigned int err_cnt[5];
-+	unsigned int rd_wd;
-+	unsigned int init_toggle_data;
-+	unsigned int err_flag[5];
-+	unsigned int err_total_flag;
-+	unsigned int training_word;
-+	unsigned int rd_tap;
-+
-+	u32 TRGMII_7623_base;
-+	u32 TRGMII_7530_RD_0;
-+	u32 TRGMII_RCK_CTRL;
-+	u32 TRGMII_7530_base;
-+	u32 TRGMII_7530_TX_base;
-+	u32 val;
-+
-+	TRGMII_7623_base = 0x300;
-+	TRGMII_7530_base = 0x7A00;
-+	TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10;
-+	TRGMII_RCK_CTRL = TRGMII_7623_base;
-+	rxd_step_size = 0x1;
-+	rxc_step_size = 0x8;
-+	init_toggle_data = 0x00000055;
-+	training_word = 0x000000AC;
-+
-+	TRGMII_7530_TX_base = TRGMII_7530_base + 0x50;
-+
-+	/* pr_err("Calibration begin ........\n"); */
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+	read_data = mt7530_mdio_r32(gsw, 0x7a10);
-+	/* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */
-+
-+	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+	read_data &= 0x3fffffff;
-+	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* RX clock gating in MT7530 */
-+
-+	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78);
-+	read_data |= 0x00002000;
-+	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data);	/* Set TX OE edge in  MT7530 */
-+
-+	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+	read_data |= 0x80000000;
-+	mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Assert RX  reset in MT7530 */
-+
-+	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+	read_data &= 0x7fffffff;
-+	mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Release RX reset in MT7530 */
-+
-+	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+	read_data |= 0xC0000000;
-+	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* Disable RX clock gating in MT7530 */
-+
-+	/* pr_err("Enable Training Mode in MT7623\n"); */
-+	/*Enable Training Mode in MT7623 */
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+	if (gsw->trgmii_force == 2000) {
-+		val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000;
-+		mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+	} else {
-+		val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
-+		mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
-+	}
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70);
-+	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800;
-+	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78);
-+	err_total_flag = 0;
-+	/* pr_err("Adjust RXC delay in MT7530\n"); */
-+	read_data = 0x0;
-+	while (err_total_flag == 0 && (read_data != 0x68)) {
-+		/* pr_err("2nd Enable EDGE CHK in MT7530\n"); */
-+		/* Enable EDGE CHK in MT7530 */
-+		for (i = 0; i < 5; i++) {
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+			/* pr_err("2nd Disable EDGE CHK in MT7530\n"); */
-+			err_cnt[i] =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			/* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
-+			/* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
-+			err_cnt[i] >>= 8;
-+			err_cnt[i] &= 0x0000ff0f;
-+			rd_wd = err_cnt[i] >> 8;
-+			rd_wd &= 0x000000ff;
-+			err_cnt[i] &= 0x0000000f;
-+			/* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */
-+			if (err_cnt[i] != 0) {
-+				err_flag[i] = 1;
-+			} else if (rd_wd != 0x55) {
-+				err_flag[i] = 1;
-+			} else {
-+				err_flag[i] = 0;
-+			}
-+			if (i == 0) {
-+				err_total_flag = err_flag[i];
-+			} else {
-+				err_total_flag = err_flag[i] & err_total_flag;
-+			}
-+			/* Disable EDGE CHK in MT7530 */
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+		}
-+		/*Adjust RXC delay */
-+		if (err_total_flag == 0) {
-+			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+			read_data |= 0x80000000;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Assert RX  reset in MT7530 */
-+
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+			read_data &= 0x3fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* RX clock gating in MT7530 */
-+
-+			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+			tmp = read_data;
-+			tmp &= 0x0000007f;
-+			tmp += rxc_step_size;
-+			/* pr_err("Current rxc delay = %d\n", tmp); */
-+			read_data &= 0xffffff80;
-+			read_data |= tmp;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);
-+			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+			/* pr_err("Current RXC delay = %x\n", read_data); */
-+
-+			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
-+			read_data &= 0x7fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Release RX reset in MT7530 */
-+
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
-+			read_data |= 0xc0000000;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* Disable RX clock gating in MT7530 */
-+			pr_err("####### MT7530 RXC delay is %d\n", tmp);
-+		}
-+		read_data = tmp;
-+	}
-+	pr_err("Finish RXC Adjustment while loop\n");
-+
-+	/* pr_err("Read RD_WD MT7530\n"); */
-+	/* Read RD_WD MT7530 */
-+	for (i = 0; i < 5; i++) {
-+		rd_tap = 0;
-+		while (err_flag[i] != 0 && rd_tap != 128) {
-+			/* Enable EDGE CHK in MT7530 */
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+			err_cnt[i] = (read_data >> 8) & 0x0000000f;
-+			rd_wd = (read_data >> 16) & 0x000000ff;
-+			if (err_cnt[i] != 0 || rd_wd != 0x55) {
-+				err_flag[i] = 1;
-+			} else {
-+				err_flag[i] = 0;
-+			}
-+			if (err_flag[i] != 0) {
-+				rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7530 */
-+				read_data = (read_data & 0xffffff80) | rd_tap;
-+				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+						read_data);
-+				tap_a[i] = rd_tap;
-+			} else {
-+				tap_a[i] = (read_data & 0x0000007f);	/* Record the min delay TAP_A */
-+				rd_tap = tap_a[i] + 0x4;
-+				read_data = (read_data & 0xffffff80) | rd_tap;
-+				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+						read_data);
-+			}
-+
-+			/* Disable EDGE CHK in MT7530 */
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+
-+		}
-+		pr_err("MT7530 %dth bit  Tap_a = %d\n", i, tap_a[i]);
-+	}
-+
-+	/* pr_err("Last While Loop\n"); */
-+	for (i = 0; i < 5; i++) {
-+		rd_tap = 0;
-+		while (err_flag[i] == 0 && (rd_tap != 128)) {
-+			/* Enable EDGE CHK in MT7530 */
-+			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+			err_cnt[i] = (read_data >> 8) & 0x0000000f;
-+			rd_wd = (read_data >> 16) & 0x000000ff;
-+			if (err_cnt[i] != 0 || rd_wd != 0x55)
-+				err_flag[i] = 1;
-+			else
-+				err_flag[i] = 0;
-+
-+			if (err_flag[i] == 0 && (rd_tap != 128)) {
-+				/* Add RXD delay in MT7530 */
-+				rd_tap = (read_data & 0x0000007f) + rxd_step_size;
-+				read_data = (read_data & 0xffffff80) | rd_tap;
-+				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+						read_data);
-+			}
-+			/* Disable EDGE CHK in MT7530 */
-+			read_data =
-+			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
-+			read_data |= 0x40000000;
-+			read_data &= 0x4fffffff;
-+			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
-+					read_data);
-+			wait_loop(gsw);
-+		}
-+		tap_b[i] = rd_tap;	/* - rxd_step_size; */
-+		pr_err("MT7530 %dth bit  Tap_b = %d\n", i, tap_b[i]);
-+		/* Calculate RXD delay = (TAP_A + TAP_B)/2 */
-+		final_tap[i] = (tap_a[i] + tap_b[i]) / 2;	
-+		/* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */
-+
-+		read_data = (read_data & 0xffffff80) | final_tap[i];
-+		mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data);
-+	}
-+
-+	if (gsw->trgmii_force == 2000)
-+		mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40);
-+	else
-+		mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40);
-+
-+}
-+
-+static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode)
-+{
-+
-+	u32 regValue;
-+
-+	/* TRGMII Clock */
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x1);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x404);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+
-+	if (xtal_mode == 1) {
-+		/* 25MHz */
-+		if (gsw->trgmii_force == 2600)
-+			/* 325MHz */
-+			_mtk_mdio_write(gsw->eth, 0, 14, 0x1a00);
-+		else if (gsw->trgmii_force == 2000)
-+			/* 250MHz */
-+			_mtk_mdio_write(gsw->eth, 0, 14, 0x1400);
-+	} else if (xtal_mode == 2) {
-+		/* 40MHz */
-+		if (gsw->trgmii_force == 2600)
-+			/* 325MHz */
-+			_mtk_mdio_write(gsw->eth, 0, 14, 0x1040);
-+		else if (gsw->trgmii_force == 2000)
-+			/* 250MHz */
-+			_mtk_mdio_write(gsw->eth, 0, 14, 0x0c80);
-+	}
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x405);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x0);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x409);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	if (xtal_mode == 1)
-+		/* 25MHz */
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
-+	else
-+		/* 40MHz */
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x40a);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	if (xtal_mode == 1)
-+		/* 25MHz */
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
-+	else
-+		/* 40MHz */
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x403);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x1800);
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x403);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x1c00);
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x401);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0xc020);
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x406);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0xa030);
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x406);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0xa038);
-+
-+//	udelay(120);		/* for MT7623 bring up test */
-+
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x3);
-+
-+	regValue = mt7530_mdio_r32(gsw, 0x7830);
-+	regValue &= 0xFFFFFFFC;
-+	regValue |= 0x00000001;
-+	mt7530_mdio_w32(gsw, 0x7830, regValue);
-+
-+	regValue = mt7530_mdio_r32(gsw, 0x7a40);
-+	regValue &= ~(0x1 << 30);
-+	regValue &= ~(0x1 << 28);
-+	mt7530_mdio_w32(gsw, 0x7a40, regValue);
-+
-+	mt7530_mdio_w32(gsw, 0x7a78, 0x55);
-+//	udelay(100);		/* for mt7623 bring up test */
-+
-+	mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300);
-+
-+	trgmii_calibration_7623(gsw);
-+	trgmii_calibration_7530(gsw);
-+
-+	mtk_switch_m32(gsw, 0, 0x80000000, 0x300);
-+	mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300);
-+
-+	/*MT7530 RXC reset */
-+	regValue = mt7530_mdio_r32(gsw, 0x7a00);
-+	regValue |= (0x1 << 31);
-+	mt7530_mdio_w32(gsw, 0x7a00, regValue);
-+	mdelay(1);
-+	regValue &= ~(0x1 << 31);
-+	mt7530_mdio_w32(gsw, 0x7a00, regValue);
-+	mdelay(100);
-+}
-+
-+static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np)
-+{
-+       u32     i;
-+       u32     val;
-+       u32     xtal_mode;
-+
-+	regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0,
-+			   ETHSYS_TRGMII_CLK_SEL362_5,
-+			   ETHSYS_TRGMII_CLK_SEL362_5);
-+
-+	/* reset the TRGMII core */
-+	mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE);
-+	mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL);
-+
-+	/* Hardware reset Switch */
-+	device_reset(eth->dev);
-+
-+	/* Wait for Switch Reset Completed*/
-+	for (i = 0; i < 100; i++) {
-+		mdelay(10);
-+		if (mt7530_mdio_r32(gsw, MT7530_HWTRAP))
-+			break;
-+	}
-+
-+	/* turn off all PHYs */
-+	for (i = 0; i <= 4; i++) {
-+		val = _mtk_mdio_read(gsw->eth, i, 0x0);
-+		val |= BIT(11);
-+		_mtk_mdio_write(gsw->eth, i, 0x0, val);
-+	}
-+
-+	/* reset the switch */
-+	mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
-+			SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
-+	udelay(100);
-+
-+	/* GE1, Force 1000M/FD, FC ON */
-+	mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
-+
-+	/* GE2, Force 1000M/FD, FC ON */
-+	mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC);
-+
-+	/* Enable Port 6, P5 as GMAC5, P5 disable */
-+	val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
-+	/* Enable Port 6 */
-+	val &= ~MHWTRAP_P6_DIS;
-+	/* Enable Port 5 */
-+	val &= ~MHWTRAP_P5_DIS;
-+	/* Port 5 as GMAC */
-+	val |= MHWTRAP_P5_MAC_SEL;
-+	/* Port 5 Interface mode */
-+	val |= MHWTRAP_P5_RGMII_MODE;
-+	/* Set MT7530 phy direct access mode**/
-+	val &= ~MHWTRAP_PHY_ACCESS;
-+	/* manual override of HW-Trap */
-+	val |= MHWTRAP_MANUAL;
-+	mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
-+
-+	val = mt7530_mdio_r32(gsw, 0x7800);
-+	val = (val >> 9) & 0x3;
-+	pr_err("!!%s: Mhz value= %d\n", __func__, val);
-+	if (val == 0x3) {
-+		xtal_mode = 1;
-+		/* 25Mhz Xtal - do nothing */
-+	} else if (val == 0x2) {
-+		/* 40Mhz */
-+		xtal_mode = 2;
-+
-+		/* disable MT7530 core clock */
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x0);
-+
-+		/* disable MT7530 PLL */
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x2020);
-+
-+		/* for MT7530 core clock = 500Mhz */
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x40e);
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x119);
-+
-+		/* enable MT7530 PLL */
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x2820);
-+
-+		udelay(20);
-+
-+		/* enable MT7530 core clock */
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
-+		_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
-+		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
-+	} else {
-+		xtal_mode = 3;
-+		/* 20Mhz Xtal - TODO */
-+	}
-+
-+	/* RGMII */
-+	_mtk_mdio_write(gsw->eth, 0, 14, 0x1);
-+
-+	/* set MT7530 central align */
-+	val = mt7530_mdio_r32(gsw, 0x7830);
-+	val &= ~1;
-+	val |= 1<<1;
-+	mt7530_mdio_w32(gsw, 0x7830, val);
-+
-+	val = mt7530_mdio_r32(gsw, 0x7a40);
-+	val &= ~(1<<30);
-+	mt7530_mdio_w32(gsw, 0x7a40, val);
-+
-+	mt7530_mdio_w32(gsw, 0x7a78, 0x855);
-+
-+	/* delay setting for 10/1000M */
-+	mt7530_mdio_w32(gsw, 0x7b00, 0x104);
-+	mt7530_mdio_w32(gsw, 0x7b04, 0x10);
-+
-+	/* lower Tx Driving */
-+	mt7530_mdio_w32(gsw, 0x7a54, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7a5c, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7a64, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7a6c, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7a74, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7a7c, 0x88);
-+	mt7530_mdio_w32(gsw, 0x7810, 0x11);
-+
-+	/* Set MT7623/MT7683 TX Driving */
-+	mtk_switch_w32(gsw, 0x88, 0x354);
-+	mtk_switch_w32(gsw, 0x88, 0x35c);
-+	mtk_switch_w32(gsw, 0x88, 0x364);
-+	mtk_switch_w32(gsw, 0x88, 0x36c);
-+	mtk_switch_w32(gsw, 0x88, 0x374);
-+	mtk_switch_w32(gsw, 0x88, 0x37c);
-+
-+#if defined (CONFIG_GE2_RGMII_AN)
-+//        *(volatile u_long *)(0xf0005f00) = 0xe00; //Set GE2 driving and slew rate
-+#else
-+  //      *(volatile u_long *)(0xf0005f00) = 0xa00; //Set GE2 driving and slew rate
-+#endif
-+    //   *(volatile u_long *)(0xf00054c0) = 0x5;   //set GE2 TDSEL
-+  //      *(volatile u_long *)(0xf0005ed0) = 0;     //set GE2 TUNE
-+
-+	mt7530_trgmii_clock_setting(gsw, xtal_mode);
-+
-+	//LANWANPartition(gsw);
-+
-+	/* disable EEE */
-+	for (i = 0; i <= 4; i++) {
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x7);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0x3C);
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x4007);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0x0);
-+
-+		/* Increase SlvDPSready time */
-+		_mtk_mdio_write(gsw->eth, i, 31, 0x52b5);
-+		_mtk_mdio_write(gsw->eth, i, 16, 0xafae);
-+		_mtk_mdio_write(gsw->eth, i, 18, 0x2f);
-+		_mtk_mdio_write(gsw->eth, i, 16, 0x8fae);
-+
-+		/* Incease post_update_timer */
-+		_mtk_mdio_write(gsw->eth, i, 31, 0x3);
-+		_mtk_mdio_write(gsw->eth, i, 17, 0x4b);
-+
-+		/* Adjust 100_mse_threshold */
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x1e);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0x123);
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x401e);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0xffff);
-+
-+		/* Disable mcc */
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x1e);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0xa6);
-+		_mtk_mdio_write(gsw->eth, i, 13, 0x401e);
-+		_mtk_mdio_write(gsw->eth, i, 14, 0x300);
-+
-+		/* Disable HW auto downshift*/
-+		_mtk_mdio_write(gsw->eth, i, 31, 0x1);
-+		val = _mtk_mdio_read(gsw->eth, i, 0x14);
-+		val &= ~(1<<4);
-+		_mtk_mdio_write(gsw->eth, i, 0x14, val);
-+	}
-+
-+	/* turn on all PHYs */
-+	for (i = 0; i <= 4; i++) {
-+		val = _mtk_mdio_read(gsw->eth, i, 0);
-+		val &= ~BIT(11);
-+		_mtk_mdio_write(gsw->eth, i, 0, val);
-+	}
-+
-+	/* enable irq */
-+	mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL);
-+}
-+
-+static const struct of_device_id mediatek_gsw_match[] = {
-+	{ .compatible = "mediatek,mt7623-gsw" },
-+	{},
-+};
-+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
-+
-+int mtk_gsw_init(struct mtk_eth *eth)
-+{
-+	struct device_node *np = eth->switch_np;
-+	struct platform_device *pdev = of_find_device_by_node(np);
-+	struct mt7620_gsw *gsw;
-+
-+	if (!pdev)
-+		return -ENODEV;
-+
-+	if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
-+		return -EINVAL;
-+
-+	gsw = platform_get_drvdata(pdev);
-+	if (!gsw)
-+		return -ENODEV;
-+	gsw->eth = eth;
-+	eth->sw_priv = gsw;
-+
-+	mt7623_hw_init(eth, gsw, np);
-+
-+	request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0,
-+			    "gsw", eth);
-+	mt7530_mdio_w32(gsw, 0x7008, 0x1f);
-+
-+	return 0;
-+}
-+
-+static int mt7623_gsw_probe(struct platform_device *pdev)
-+{
-+	struct device_node *np = pdev->dev.of_node;
-+	struct device_node *pctl;
-+	int reset_pin, ret;
-+	struct mt7620_gsw *gsw;
-+	struct regulator *supply;
-+
-+	gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
-+	if (!gsw)
-+		return -ENOMEM;
-+
-+	gsw->dev = &pdev->dev;
-+	gsw->trgmii_force = 2000;
-+	gsw->irq = irq_of_parse_and_map(np, 0);
-+	if (gsw->irq < 0)
-+		return -EINVAL;
-+
-+	gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys");
-+	if (IS_ERR(gsw->ethsys))
-+		return PTR_ERR(gsw->ethsys);
-+
-+	reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0);
-+	if (reset_pin < 0)
-+		return reset_pin;
-+
-+	pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
-+	if (IS_ERR(pctl))
-+		return PTR_ERR(pctl);
-+
-+	gsw->pctl = syscon_node_to_regmap(pctl);
-+	if (IS_ERR(pctl))
-+		return PTR_ERR(pctl);
-+
-+	ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset");
-+	if (ret)
-+		return ret;
-+
-+	gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll");
-+
-+	if (IS_ERR(gsw->clk_trgpll))
-+		return -ENODEV;
-+
-+	supply = devm_regulator_get(&pdev->dev, "mt7530");
-+	if (IS_ERR(supply))
-+		return PTR_ERR(supply);
-+
-+	regulator_set_voltage(supply, 1000000, 1000000);
-+	ret = regulator_enable(supply);
-+	if (ret) {
-+		dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret);
-+		return ret;
-+	}
-+	pm_runtime_enable(&pdev->dev);
-+	pm_runtime_get_sync(&pdev->dev);
-+
-+	ret = clk_set_rate(gsw->clk_trgpll, 500000000);
-+	if (ret)
-+		return ret;
-+
-+	clk_prepare_enable(gsw->clk_trgpll);
-+
-+	gpio_direction_output(reset_pin, 0);
-+	udelay(1000);
-+	gpio_set_value(reset_pin, 1);
-+	mdelay(100);
-+
-+	/* Set GE2 driving and slew rate */
-+	regmap_write(gsw->pctl, 0xF00, 0xa00);
-+	/* set GE2 TDSEL */
-+	regmap_write(gsw->pctl, 0x4C0, 0x5);
-+	/* set GE2 TUNE */
-+	regmap_write(gsw->pctl, 0xED0, 0x0);
-+
-+	platform_set_drvdata(pdev, gsw);
-+
-+	return 0;
-+}
-+
-+static int mt7623_gsw_remove(struct platform_device *pdev)
-+{
-+	struct mt7620_gsw *gsw = platform_get_drvdata(pdev);
-+
-+	clk_disable_unprepare(gsw->clk_trgpll);
-+
-+	pm_runtime_put_sync(&pdev->dev);
-+        pm_runtime_disable(&pdev->dev);
-+
-+	platform_set_drvdata(pdev, NULL);
-+
-+	return 0;
-+}
-+
-+static struct platform_driver gsw_driver = {
-+	.probe = mt7623_gsw_probe,
-+	.remove = mt7623_gsw_remove,
-+	.driver = {
-+		.name = "mt7623-gsw",
-+		.owner = THIS_MODULE,
-+		.of_match_table = mediatek_gsw_match,
-+	},
-+};
-+
-+module_platform_driver(gsw_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
-+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7623 SoC");
-diff --git a/drivers/net/ethernet/mediatek/mt7530.c b/drivers/net/ethernet/mediatek/mt7530.c
-new file mode 100644
-index 0000000..2e9d280
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mt7530.c
-@@ -0,0 +1,808 @@
-+/*
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * 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 the
-+ * GNU General Public License for more details.
-+ *
-+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
-+ */
-+
-+#include <linux/if.h>
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/list.h>
-+#include <linux/if_ether.h>
-+#include <linux/skbuff.h>
-+#include <linux/netdevice.h>
-+#include <linux/netlink.h>
-+#include <linux/bitops.h>
-+#include <net/genetlink.h>
-+#include <linux/switch.h>
-+#include <linux/delay.h>
-+#include <linux/phy.h>
-+#include <linux/netdevice.h>
-+#include <linux/etherdevice.h>
-+#include <linux/lockdep.h>
-+#include <linux/workqueue.h>
-+#include <linux/of_device.h>
-+
-+#include "mt7530.h"
-+
-+#define MT7530_CPU_PORT		6
-+#define MT7530_NUM_PORTS	8
-+#define MT7530_NUM_VLANS	16
-+#define MT7530_MAX_VID		4095
-+#define MT7530_MIN_VID		0
-+
-+/* registers */
-+#define REG_ESW_VLAN_VTCR		0x90
-+#define REG_ESW_VLAN_VAWD1		0x94
-+#define REG_ESW_VLAN_VAWD2		0x98
-+#define REG_ESW_VLAN_VTIM(x)	(0x100 + 4 * ((x) / 2))
-+
-+#define REG_ESW_VLAN_VAWD1_IVL_MAC	BIT(30)
-+#define REG_ESW_VLAN_VAWD1_VTAG_EN	BIT(28)
-+#define REG_ESW_VLAN_VAWD1_VALID	BIT(0)
-+
-+/* vlan egress mode */
-+enum {
-+	ETAG_CTRL_UNTAG	= 0,
-+	ETAG_CTRL_TAG	= 2,
-+	ETAG_CTRL_SWAP	= 1,
-+	ETAG_CTRL_STACK	= 3,
-+};
-+
-+#define REG_ESW_PORT_PCR(x)	(0x2004 | ((x) << 8))
-+#define REG_ESW_PORT_PVC(x)	(0x2010 | ((x) << 8))
-+#define REG_ESW_PORT_PPBV1(x)	(0x2014 | ((x) << 8))
-+
-+#define REG_HWTRAP		0x7804
-+
-+#define MIB_DESC(_s , _o, _n)   \
-+	{                       \
-+		.size = (_s),   \
-+		.offset = (_o), \
-+		.name = (_n),   \
-+	}
-+
-+struct mt7xxx_mib_desc {
-+	unsigned int size;
-+	unsigned int offset;
-+	const char *name;
-+};
-+
-+#define MT7621_MIB_COUNTER_BASE	0x4000
-+#define MT7621_MIB_COUNTER_PORT_OFFSET	0x100
-+#define MT7621_STATS_TDPC	0x00
-+#define MT7621_STATS_TCRC	0x04
-+#define MT7621_STATS_TUPC	0x08
-+#define MT7621_STATS_TMPC	0x0C
-+#define MT7621_STATS_TBPC	0x10
-+#define MT7621_STATS_TCEC	0x14
-+#define MT7621_STATS_TSCEC	0x18
-+#define MT7621_STATS_TMCEC	0x1C
-+#define MT7621_STATS_TDEC	0x20
-+#define MT7621_STATS_TLCEC	0x24
-+#define MT7621_STATS_TXCEC	0x28
-+#define MT7621_STATS_TPPC	0x2C
-+#define MT7621_STATS_TL64PC	0x30
-+#define MT7621_STATS_TL65PC	0x34
-+#define MT7621_STATS_TL128PC	0x38
-+#define MT7621_STATS_TL256PC	0x3C
-+#define MT7621_STATS_TL512PC	0x40
-+#define MT7621_STATS_TL1024PC	0x44
-+#define MT7621_STATS_TOC	0x48
-+#define MT7621_STATS_RDPC	0x60
-+#define MT7621_STATS_RFPC	0x64
-+#define MT7621_STATS_RUPC	0x68
-+#define MT7621_STATS_RMPC	0x6C
-+#define MT7621_STATS_RBPC	0x70
-+#define MT7621_STATS_RAEPC	0x74
-+#define MT7621_STATS_RCEPC	0x78
-+#define MT7621_STATS_RUSPC	0x7C
-+#define MT7621_STATS_RFEPC	0x80
-+#define MT7621_STATS_ROSPC	0x84
-+#define MT7621_STATS_RJEPC	0x88
-+#define MT7621_STATS_RPPC	0x8C
-+#define MT7621_STATS_RL64PC	0x90
-+#define MT7621_STATS_RL65PC	0x94
-+#define MT7621_STATS_RL128PC	0x98
-+#define MT7621_STATS_RL256PC	0x9C
-+#define MT7621_STATS_RL512PC	0xA0
-+#define MT7621_STATS_RL1024PC	0xA4
-+#define MT7621_STATS_ROC	0xA8
-+#define MT7621_STATS_RDPC_CTRL	0xB0
-+#define MT7621_STATS_RDPC_ING	0xB4
-+#define MT7621_STATS_RDPC_ARL	0xB8
-+
-+static const struct mt7xxx_mib_desc mt7621_mibs[] = {
-+	MIB_DESC(1, MT7621_STATS_TDPC, "TxDrop"),
-+	MIB_DESC(1, MT7621_STATS_TCRC, "TxCRC"),
-+	MIB_DESC(1, MT7621_STATS_TUPC, "TxUni"),
-+	MIB_DESC(1, MT7621_STATS_TMPC, "TxMulti"),
-+	MIB_DESC(1, MT7621_STATS_TBPC, "TxBroad"),
-+	MIB_DESC(1, MT7621_STATS_TCEC, "TxCollision"),
-+	MIB_DESC(1, MT7621_STATS_TSCEC, "TxSingleCol"),
-+	MIB_DESC(1, MT7621_STATS_TMCEC, "TxMultiCol"),
-+	MIB_DESC(1, MT7621_STATS_TDEC, "TxDefer"),
-+	MIB_DESC(1, MT7621_STATS_TLCEC, "TxLateCol"),
-+	MIB_DESC(1, MT7621_STATS_TXCEC, "TxExcCol"),
-+	MIB_DESC(1, MT7621_STATS_TPPC, "TxPause"),
-+	MIB_DESC(1, MT7621_STATS_TL64PC, "Tx64Byte"),
-+	MIB_DESC(1, MT7621_STATS_TL65PC, "Tx65Byte"),
-+	MIB_DESC(1, MT7621_STATS_TL128PC, "Tx128Byte"),
-+	MIB_DESC(1, MT7621_STATS_TL256PC, "Tx256Byte"),
-+	MIB_DESC(1, MT7621_STATS_TL512PC, "Tx512Byte"),
-+	MIB_DESC(1, MT7621_STATS_TL1024PC, "Tx1024Byte"),
-+	MIB_DESC(2, MT7621_STATS_TOC, "TxByte"),
-+	MIB_DESC(1, MT7621_STATS_RDPC, "RxDrop"),
-+	MIB_DESC(1, MT7621_STATS_RFPC, "RxFiltered"),
-+	MIB_DESC(1, MT7621_STATS_RUPC, "RxUni"),
-+	MIB_DESC(1, MT7621_STATS_RMPC, "RxMulti"),
-+	MIB_DESC(1, MT7621_STATS_RBPC, "RxBroad"),
-+	MIB_DESC(1, MT7621_STATS_RAEPC, "RxAlignErr"),
-+	MIB_DESC(1, MT7621_STATS_RCEPC, "RxCRC"),
-+	MIB_DESC(1, MT7621_STATS_RUSPC, "RxUnderSize"),
-+	MIB_DESC(1, MT7621_STATS_RFEPC, "RxFragment"),
-+	MIB_DESC(1, MT7621_STATS_ROSPC, "RxOverSize"),
-+	MIB_DESC(1, MT7621_STATS_RJEPC, "RxJabber"),
-+	MIB_DESC(1, MT7621_STATS_RPPC, "RxPause"),
-+	MIB_DESC(1, MT7621_STATS_RL64PC, "Rx64Byte"),
-+	MIB_DESC(1, MT7621_STATS_RL65PC, "Rx65Byte"),
-+	MIB_DESC(1, MT7621_STATS_RL128PC, "Rx128Byte"),
-+	MIB_DESC(1, MT7621_STATS_RL256PC, "Rx256Byte"),
-+	MIB_DESC(1, MT7621_STATS_RL512PC, "Rx512Byte"),
-+	MIB_DESC(1, MT7621_STATS_RL1024PC, "Rx1024Byte"),
-+	MIB_DESC(2, MT7621_STATS_ROC, "RxByte"),
-+	MIB_DESC(1, MT7621_STATS_RDPC_CTRL, "RxCtrlDrop"),
-+	MIB_DESC(1, MT7621_STATS_RDPC_ING, "RxIngDrop"),
-+	MIB_DESC(1, MT7621_STATS_RDPC_ARL, "RxARLDrop")
-+};
-+
-+enum {
-+	/* Global attributes. */
-+	MT7530_ATTR_ENABLE_VLAN,
-+};
-+
-+struct mt7530_port_entry {
-+	u16	pvid;
-+};
-+
-+struct mt7530_vlan_entry {
-+	u16	vid;
-+	u8	member;
-+	u8	etags;
-+};
-+
-+struct mt7530_priv {
-+	void __iomem		*base;
-+	struct mii_bus		*bus;
-+	struct switch_dev	swdev;
-+
-+	bool			global_vlan_enable;
-+	struct mt7530_vlan_entry	vlan_entries[MT7530_NUM_VLANS];
-+	struct mt7530_port_entry	port_entries[MT7530_NUM_PORTS];
-+};
-+
-+struct mt7530_mapping {
-+	char	*name;
-+	u16	pvids[MT7530_NUM_PORTS];
-+	u8	members[MT7530_NUM_VLANS];
-+	u8	etags[MT7530_NUM_VLANS];
-+	u16	vids[MT7530_NUM_VLANS];
-+} mt7530_defaults[] = {
-+	{
-+		.name = "llllw",
-+		.pvids = { 1, 1, 1, 1, 2, 1, 1 },
-+		.members = { 0, 0x6f, 0x50 },
-+		.etags = { 0, 0x40, 0x40 },
-+		.vids = { 0, 1, 2 },
-+	}, {
-+		.name = "wllll",
-+		.pvids = { 2, 1, 1, 1, 1, 1, 1 },
-+		.members = { 0, 0x7e, 0x41 },
-+		.etags = { 0, 0x40, 0x40 },
-+		.vids = { 0, 1, 2 },
-+	},
-+};
-+
-+struct mt7530_mapping*
-+mt7530_find_mapping(struct device_node *np)
-+{
-+	const char *map;
-+	int i;
-+
-+	if (of_property_read_string(np, "mediatek,portmap", &map))
-+		return NULL;
-+
-+	for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++)
-+		if (!strcmp(map, mt7530_defaults[i].name))
-+			return &mt7530_defaults[i];
-+
-+	return NULL;
-+}
-+
-+static void
-+mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map)
-+{
-+	int i = 0;
-+
-+	for (i = 0; i < MT7530_NUM_PORTS; i++)
-+		mt7530->port_entries[i].pvid = map->pvids[i];
-+
-+	for (i = 0; i < MT7530_NUM_VLANS; i++) {
-+		mt7530->vlan_entries[i].member = map->members[i];
-+		mt7530->vlan_entries[i].etags = map->etags[i];
-+		mt7530->vlan_entries[i].vid = map->vids[i];
-+	}
-+}
-+
-+static int
-+mt7530_reset_switch(struct switch_dev *dev)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	int i;
-+
-+	memset(eth->port_entries, 0, sizeof(eth->port_entries));
-+	memset(eth->vlan_entries, 0, sizeof(eth->vlan_entries));
-+
-+	/* set default vid of each vlan to the same number of vlan, so the vid
-+	 * won't need be set explicitly.
-+	 */
-+	for (i = 0; i < MT7530_NUM_VLANS; i++) {
-+		eth->vlan_entries[i].vid = i;
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_get_vlan_enable(struct switch_dev *dev,
-+			   const struct switch_attr *attr,
-+			   struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+	val->value.i = eth->global_vlan_enable;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_set_vlan_enable(struct switch_dev *dev,
-+			   const struct switch_attr *attr,
-+			   struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+	eth->global_vlan_enable = val->value.i != 0;
-+
-+	return 0;
-+}
-+
-+static u32
-+mt7530_r32(struct mt7530_priv *eth, u32 reg)
-+{
-+	u32 val;
-+	if (eth->bus) {
-+		u16 high, low;
-+
-+		mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+		low = mdiobus_read(eth->bus, 0x1f, (reg >> 2) & 0xf);
-+		high = mdiobus_read(eth->bus, 0x1f, 0x10);
-+
-+		return (high << 16) | (low & 0xffff);
-+	}
-+
-+	val = ioread32(eth->base + reg);
-+	pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
-+
-+	return val;
-+}
-+
-+static void
-+mt7530_w32(struct mt7530_priv *eth, u32 reg, u32 val)
-+{
-+	if (eth->bus) {
-+		mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
-+		mdiobus_write(eth->bus, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
-+		mdiobus_write(eth->bus, 0x1f, 0x10, val >> 16);
-+		return;
-+	}
-+
-+	pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
-+	iowrite32(val, eth->base + reg);
-+}
-+
-+static void
-+mt7530_vtcr(struct mt7530_priv *eth, u32 cmd, u32 val)
-+{
-+	int i;
-+
-+	mt7530_w32(eth, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
-+
-+	for (i = 0; i < 20; i++) {
-+		u32 val = mt7530_r32(eth, REG_ESW_VLAN_VTCR);
-+
-+		if ((val & BIT(31)) == 0)
-+			break;
-+
-+		udelay(1000);
-+	}
-+	if (i == 20)
-+		printk("mt7530: vtcr timeout\n");
-+}
-+
-+static int
-+mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+	if (port >= MT7530_NUM_PORTS)
-+		return -EINVAL;
-+
-+	*val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(port));
-+	*val &= 0xfff;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+
-+	if (port >= MT7530_NUM_PORTS)
-+		return -EINVAL;
-+
-+	if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
-+		return -EINVAL;
-+
-+	eth->port_entries[port].pvid = pvid;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	u32 member;
-+	u32 etags;
-+	int i;
-+
-+	val->len = 0;
-+
-+	if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
-+		return -EINVAL;
-+
-+	mt7530_vtcr(eth, 0, val->port_vlan);
-+
-+	member = mt7530_r32(eth, REG_ESW_VLAN_VAWD1);
-+	member >>= 16;
-+	member &= 0xff;
-+
-+	etags = mt7530_r32(eth, REG_ESW_VLAN_VAWD2);
-+
-+	for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+		struct switch_port *p;
-+		int etag;
-+
-+		if (!(member & BIT(i)))
-+			continue;
-+
-+		p = &val->value.ports[val->len++];
-+		p->id = i;
-+
-+		etag = (etags >> (i * 2)) & 0x3;
-+
-+		if (etag == ETAG_CTRL_TAG)
-+			p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
-+		else if (etag != ETAG_CTRL_UNTAG)
-+			printk("vlan egress tag control neither untag nor tag.\n");
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	u8 member = 0;
-+	u8 etags = 0;
-+	int i;
-+
-+	if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
-+			val->len > MT7530_NUM_PORTS)
-+		return -EINVAL;
-+
-+	for (i = 0; i < val->len; i++) {
-+		struct switch_port *p = &val->value.ports[i];
-+
-+		if (p->id >= MT7530_NUM_PORTS)
-+			return -EINVAL;
-+
-+		member |= BIT(p->id);
-+
-+		if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
-+			etags |= BIT(p->id);
-+	}
-+	eth->vlan_entries[val->port_vlan].member = member;
-+	eth->vlan_entries[val->port_vlan].etags = etags;
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
-+		struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	int vlan;
-+	u16 vid;
-+
-+	vlan = val->port_vlan;
-+	vid = (u16)val->value.i;
-+
-+	if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
-+		return -EINVAL;
-+
-+	if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
-+		return -EINVAL;
-+
-+	eth->vlan_entries[vlan].vid = vid;
-+	return 0;
-+}
-+
-+static int
-+mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
-+		struct switch_val *val)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	u32 vid;
-+	int vlan;
-+
-+	vlan = val->port_vlan;
-+
-+	vid = mt7530_r32(eth, REG_ESW_VLAN_VTIM(vlan));
-+	if (vlan & 1)
-+		vid = vid >> 12;
-+	vid &= 0xfff;
-+
-+	val->value.i = vid;
-+	return 0;
-+}
-+
-+static int
-+mt7530_apply_config(struct switch_dev *dev)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	int i, j;
-+	u8 tag_ports;
-+	u8 untag_ports;
-+
-+	if (!eth->global_vlan_enable) {
-+		for (i = 0; i < MT7530_NUM_PORTS; i++)
-+			mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0000);
-+
-+		for (i = 0; i < MT7530_NUM_PORTS; i++)
-+			mt7530_w32(eth, REG_ESW_PORT_PVC(i), 0x810000c0);
-+
-+		return 0;
-+	}
-+
-+	/* set all ports as security mode */
-+	for (i = 0; i < MT7530_NUM_PORTS; i++)
-+		mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0003);
-+
-+	/* check if a port is used in tag/untag vlan egress mode */
-+	tag_ports = 0;
-+	untag_ports = 0;
-+
-+	for (i = 0; i < MT7530_NUM_VLANS; i++) {
-+		u8 member = eth->vlan_entries[i].member;
-+		u8 etags = eth->vlan_entries[i].etags;
-+
-+		if (!member)
-+			continue;
-+
-+		for (j = 0; j < MT7530_NUM_PORTS; j++) {
-+			if (!(member & BIT(j)))
-+				continue;
-+
-+			if (etags & BIT(j))
-+				tag_ports |= 1u << j;
-+			else
-+				untag_ports |= 1u << j;
-+		}
-+	}
-+
-+	/* set all untag-only ports as transparent and the rest as user port */
-+	for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+		u32 pvc_mode = 0x81000000;
-+
-+		if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
-+			pvc_mode = 0x810000c0;
-+
-+		mt7530_w32(eth, REG_ESW_PORT_PVC(i), pvc_mode);
-+	}
-+
-+	for (i = 0; i < MT7530_NUM_VLANS; i++) {
-+		u16 vid = eth->vlan_entries[i].vid;
-+		u8 member = eth->vlan_entries[i].member;
-+		u8 etags = eth->vlan_entries[i].etags;
-+		u32 val;
-+
-+		/* vid of vlan */
-+		val = mt7530_r32(eth, REG_ESW_VLAN_VTIM(i));
-+		if (i % 2 == 0) {
-+			val &= 0xfff000;
-+			val |= vid;
-+		} else {
-+			val &= 0xfff;
-+			val |= (vid << 12);
-+		}
-+		mt7530_w32(eth, REG_ESW_VLAN_VTIM(i), val);
-+
-+		/* vlan port membership */
-+		if (member)
-+			mt7530_w32(eth, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
-+				REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
-+				REG_ESW_VLAN_VAWD1_VALID);
-+		else
-+			mt7530_w32(eth, REG_ESW_VLAN_VAWD1, 0);
-+
-+		/* egress mode */
-+		val = 0;
-+		for (j = 0; j < MT7530_NUM_PORTS; j++) {
-+			if (etags & BIT(j))
-+				val |= ETAG_CTRL_TAG << (j * 2);
-+			else
-+				val |= ETAG_CTRL_UNTAG << (j * 2);
-+		}
-+		mt7530_w32(eth, REG_ESW_VLAN_VAWD2, val);
-+
-+		/* write to vlan table */
-+		mt7530_vtcr(eth, 1, i);
-+	}
-+
-+	/* Port Default PVID */
-+	for (i = 0; i < MT7530_NUM_PORTS; i++) {
-+		u32 val;
-+		val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(i));
-+		val &= ~0xfff;
-+		val |= eth->port_entries[i].pvid;
-+		mt7530_w32(eth, REG_ESW_PORT_PPBV1(i), val);
-+	}
-+
-+	return 0;
-+}
-+
-+static int
-+mt7530_get_port_link(struct switch_dev *dev,  int port,
-+			struct switch_port_link *link)
-+{
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	u32 speed, pmsr;
-+
-+	if (port < 0 || port >= MT7530_NUM_PORTS)
-+		return -EINVAL;
-+
-+	pmsr = mt7530_r32(eth, 0x3008 + (0x100 * port));
-+
-+	link->link = pmsr & 1;
-+	link->duplex = (pmsr >> 1) & 1;
-+	speed = (pmsr >> 2) & 3;
-+
-+	switch (speed) {
-+	case 0:
-+		link->speed = SWITCH_PORT_SPEED_10;
-+		break;
-+	case 1:
-+		link->speed = SWITCH_PORT_SPEED_100;
-+		break;
-+	case 2:
-+	case 3: /* forced gige speed can be 2 or 3 */
-+		link->speed = SWITCH_PORT_SPEED_1000;
-+		break;
-+	default:
-+		link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-+		break;
-+	}
-+
-+	return 0;
-+}
-+
-+static const struct switch_attr mt7530_global[] = {
-+	{
-+		.type = SWITCH_TYPE_INT,
-+		.name = "enable_vlan",
-+		.description = "VLAN mode (1:enabled)",
-+		.max = 1,
-+		.id = MT7530_ATTR_ENABLE_VLAN,
-+		.get = mt7530_get_vlan_enable,
-+		.set = mt7530_set_vlan_enable,
-+	},
-+};
-+
-+static u64 get_mib_counter(struct mt7530_priv *eth, int i, int port)
-+{
-+	unsigned int port_base;
-+	u64 t;
-+
-+	port_base = MT7621_MIB_COUNTER_BASE +
-+		    MT7621_MIB_COUNTER_PORT_OFFSET * port;
-+
-+	t = mt7530_r32(eth, port_base + mt7621_mibs[i].offset);
-+	if (mt7621_mibs[i].size == 2) {
-+		u64 hi;
-+
-+		hi = mt7530_r32(eth, port_base + mt7621_mibs[i].offset + 4);
-+		t |= hi << 32;
-+	}
-+
-+	return t;
-+}
-+
-+static int mt7621_sw_get_port_mib(struct switch_dev *dev,
-+				  const struct switch_attr *attr,
-+				  struct switch_val *val)
-+{
-+	static char buf[4096];
-+	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
-+	int i, len = 0;
-+
-+	if (val->port_vlan >= MT7530_NUM_PORTS)
-+		return -EINVAL;
-+
-+	len += snprintf(buf + len, sizeof(buf) - len,
-+			"Port %d MIB counters\n", val->port_vlan);
-+
-+	for (i = 0; i < sizeof(mt7621_mibs) / sizeof(*mt7621_mibs); ++i) {
-+		u64 counter;
-+		len += snprintf(buf + len, sizeof(buf) - len,
-+				"%-11s: ", mt7621_mibs[i].name);
-+		counter = get_mib_counter(eth, i, val->port_vlan);
-+		len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
-+				counter);
-+	}
-+
-+	val->value.s = buf;
-+	val->len = len;
-+	return 0;
-+}
-+
-+static const struct switch_attr mt7621_port[] = {
-+	{
-+		.type = SWITCH_TYPE_STRING,
-+		.name = "mib",
-+		.description = "Get MIB counters for port",
-+		.get = mt7621_sw_get_port_mib,
-+		.set = NULL,
-+	},
-+};
-+
-+static const struct switch_attr mt7530_port[] = {
-+};
-+
-+static const struct switch_attr mt7530_vlan[] = {
-+	{
-+		.type = SWITCH_TYPE_INT,
-+		.name = "vid",
-+		.description = "VLAN ID (0-4094)",
-+		.set = mt7530_set_vid,
-+		.get = mt7530_get_vid,
-+		.max = 4094,
-+	},
-+};
-+
-+static const struct switch_dev_ops mt7621_ops = {
-+	.attr_global = {
-+		.attr = mt7530_global,
-+		.n_attr = ARRAY_SIZE(mt7530_global),
-+	},
-+/*	.attr_port = {
-+		.attr = mt7621_port,
-+		.n_attr = ARRAY_SIZE(mt7621_port),
-+	},*/
-+	.attr_vlan = {
-+		.attr = mt7530_vlan,
-+		.n_attr = ARRAY_SIZE(mt7530_vlan),
-+	},
-+	.get_vlan_ports = mt7530_get_vlan_ports,
-+	.set_vlan_ports = mt7530_set_vlan_ports,
-+	.get_port_pvid = mt7530_get_port_pvid,
-+	.set_port_pvid = mt7530_set_port_pvid,
-+	.get_port_link = mt7530_get_port_link,
-+	.apply_config = mt7530_apply_config,
-+	.reset_switch = mt7530_reset_switch,
-+};
-+
-+static const struct switch_dev_ops mt7530_ops = {
-+	.attr_global = {
-+		.attr = mt7530_global,
-+		.n_attr = ARRAY_SIZE(mt7530_global),
-+	},
-+	.attr_port = {
-+		.attr = mt7530_port,
-+		.n_attr = ARRAY_SIZE(mt7530_port),
-+	},
-+	.attr_vlan = {
-+		.attr = mt7530_vlan,
-+		.n_attr = ARRAY_SIZE(mt7530_vlan),
-+	},
-+	.get_vlan_ports = mt7530_get_vlan_ports,
-+	.set_vlan_ports = mt7530_set_vlan_ports,
-+	.get_port_pvid = mt7530_get_port_pvid,
-+	.set_port_pvid = mt7530_set_port_pvid,
-+	.get_port_link = mt7530_get_port_link,
-+	.apply_config = mt7530_apply_config,
-+	.reset_switch = mt7530_reset_switch,
-+};
-+
-+int
-+mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan)
-+{
-+	struct switch_dev *swdev;
-+	struct mt7530_priv *mt7530;
-+	struct mt7530_mapping *map;
-+	int ret;
-+
-+	mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL);
-+	if (!mt7530)
-+		return -ENOMEM;
-+
-+	mt7530->base = base;
-+	mt7530->bus = bus;
-+	mt7530->global_vlan_enable = vlan;
-+
-+	swdev = &mt7530->swdev;
-+	if (bus) {
-+		swdev->alias = "mt7530";
-+		swdev->name = "mt7530";
-+	} else if (IS_ENABLED(CONFIG_MACH_MT7623)) {
-+		swdev->alias = "mt7623";
-+		swdev->name = "mt7623";
-+	} else if (IS_ENABLED(CONFIG_SOC_MT7621)) {
-+		swdev->alias = "mt7621";
-+		swdev->name = "mt7621";
-+	} else {
-+		swdev->alias = "mt7620";
-+		swdev->name = "mt7620";
-+	}
-+	swdev->cpu_port = MT7530_CPU_PORT;
-+	swdev->ports = MT7530_NUM_PORTS;
-+	swdev->vlans = MT7530_NUM_VLANS;
-+	if (IS_ENABLED(CONFIG_SOC_MT7621) || IS_ENABLED(CONFIG_MACH_MT7623))
-+		swdev->ops = &mt7621_ops;
-+	else
-+		swdev->ops = &mt7530_ops;
-+
-+	ret = register_switch(swdev, NULL);
-+	if (ret) {
-+		dev_err(dev, "failed to register mt7530\n");
-+		return ret;
-+	}
-+
-+	mt7530_reset_switch(swdev);
-+
-+	map = mt7530_find_mapping(dev->of_node);
-+	if (map)
-+		mt7530_apply_mapping(mt7530, map);
-+	mt7530_apply_config(swdev);
-+
-+	/* magic vodoo */
-+	if (!(IS_ENABLED(CONFIG_SOC_MT7621) || IS_ENABLED(CONFIG_MACH_MT7623)) && bus && mt7530_r32(mt7530, REG_HWTRAP) !=  0x1117edf) {
-+	        dev_info(dev, "fixing up MHWTRAP register - bootloader probably played with it\n");
-+		mt7530_w32(mt7530, REG_HWTRAP, 0x1117edf);
-+	}
-+	dev_info(dev, "loaded %s driver\n", swdev->name);
-+
-+	return 0;
-+}
-diff --git a/drivers/net/ethernet/mediatek/mt7530.h b/drivers/net/ethernet/mediatek/mt7530.h
-new file mode 100644
-index 0000000..1fc8c62
---- /dev/null
-+++ b/drivers/net/ethernet/mediatek/mt7530.h
-@@ -0,0 +1,20 @@
-+/*
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License
-+ * as published by the Free Software Foundation; either version 2
-+ * of the License, or (at your option) any later version.
-+ *
-+ * 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 the
-+ * GNU General Public License for more details.
-+ *
-+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
-+ */
-+
-+#ifndef _MT7530_H__
-+#define _MT7530_H__
-+
-+int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
-+
-+#endif
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index 7f2126b..dd7f6e3 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -24,6 +24,9 @@
- 
- #include "mtk_eth_soc.h"
- 
-+/* the callback used by the driver core to bringup the switch */
-+int mtk_gsw_init(struct mtk_eth *eth);
-+
- static int mtk_msg_level = -1;
- module_param_named(msg_level, mtk_msg_level, int, 0);
- MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
-@@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth)
- 			return 0;
- 		if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
- 			break;
--		usleep_range(10, 20);
-+//		usleep_range(10, 20);
- 	}
- 
- 	dev_err(eth->dev, "mdio: MDIO timeout\n");
-@@ -138,6 +141,15 @@ static void mtk_phy_link_adjust(struct net_device *dev)
- 		  MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
- 		  MAC_MCR_BACKPR_EN;
- 
-+	if (!mac->id) {
-+		mcr |= MAC_MCR_SPEED_1000;
-+		mcr |= MAC_MCR_FORCE_LINK;
-+		mcr |= MAC_MCR_FORCE_DPX;
-+		mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
-+		mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
-+		return;
-+	}
-+
- 	switch (mac->phy_dev->speed) {
- 	case SPEED_1000:
- 		mcr |= MAC_MCR_SPEED_1000;
-@@ -157,11 +169,12 @@ static void mtk_phy_link_adjust(struct net_device *dev)
- 		mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
- 
- 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
--
- 	if (mac->phy_dev->link)
- 		netif_carrier_on(dev);
- 	else
- 		netif_carrier_off(dev);
-+
-+	return;
- }
- 
- static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
-@@ -193,7 +206,7 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
- 
- 	dev_info(eth->dev,
- 		 "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
--		 mac->id, phydev_name(phydev), phydev->phy_id,
-+		 mac->id, dev_name(&phydev->dev), phydev->phy_id,
- 		 phydev->drv->name);
- 
- 	mac->phy_dev = phydev;
-@@ -634,7 +647,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
- 
- 	spin_unlock_irqrestore(&eth->page_lock, flags);
- 
--	netdev_sent_queue(dev, skb->len);
- 	skb_tx_timestamp(skb);
- 
- 	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
-@@ -884,7 +896,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
- 	for (i = 0; i < MTK_MAC_COUNT; i++) {
- 		if (!eth->netdev[i] || !done[i])
- 			continue;
--		netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
- 		total += done[i];
- 	}
- 
-@@ -1251,6 +1262,8 @@ static int mtk_open(struct net_device *dev)
- 	phy_start(mac->phy_dev);
- 	netif_start_queue(dev);
- 
-+	netif_carrier_on(dev);
-+
- 	return 0;
- }
- 
-@@ -1283,6 +1296,7 @@ static int mtk_stop(struct net_device *dev)
- 	struct mtk_mac *mac = netdev_priv(dev);
- 	struct mtk_eth *eth = mac->hw;
- 
-+	netif_carrier_off(dev);
- 	netif_tx_disable(dev);
- 	phy_stop(mac->phy_dev);
- 
-@@ -1328,6 +1342,7 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
- 	/* Enable RX VLan Offloading */
- 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
- 
-+	mtk_gsw_init(eth);
- 	err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
- 			       dev_name(eth->dev), eth);
- 	if (err)
-@@ -1360,6 +1375,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
- 		mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
- 	}
- 
-+	mt7623_gsw_config(eth);
-+
- 	return 0;
- }
- 
-@@ -1466,11 +1483,13 @@ static int mtk_set_settings(struct net_device *dev,
- {
- 	struct mtk_mac *mac = netdev_priv(dev);
- 
--	if (cmd->phy_address != mac->phy_dev->mdio.addr) {
--		mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
--					       cmd->phy_address);
--		if (!mac->phy_dev)
-+	if (cmd->phy_address != mac->phy_dev->addr) {
-+		if (mac->hw->mii_bus->phy_map[cmd->phy_address]) {
-+			mac->phy_dev =
-+				mac->hw->mii_bus->phy_map[cmd->phy_address];
-+		} else {
- 			return -ENODEV;
-+		}
- 	}
- 
- 	return phy_ethtool_sset(mac->phy_dev, cmd);
-@@ -1563,7 +1582,6 @@ static void mtk_get_ethtool_stats(struct net_device *dev,
- 		data_src = (u64*)hwstats;
- 		data_dst = data;
- 		start = u64_stats_fetch_begin_irq(&hwstats->syncp);
--
- 		for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++)
- 			*data_dst++ = *(data_src + mtk_ethtool_stats[i].offset);
- 	} while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
-@@ -1734,6 +1752,9 @@ static int mtk_probe(struct platform_device *pdev)
- 	clk_prepare_enable(eth->clk_gp1);
- 	clk_prepare_enable(eth->clk_gp2);
- 
-+	eth->switch_np = of_parse_phandle(pdev->dev.of_node,
-+					  "mediatek,switch", 0);
-+
- 	eth->dev = &pdev->dev;
- 	eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
- 
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-index 48a5292..d737d61 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
-@@ -389,6 +389,9 @@ struct mtk_eth {
- 	struct clk			*clk_gp1;
- 	struct clk			*clk_gp2;
- 	struct mii_bus			*mii_bus;
-+
-+	struct device_node		*switch_np;
-+	void				*sw_priv;
- };
- 
- /* struct mtk_mac -	the structure that holds the info about the MACs of the
-@@ -418,4 +421,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
- void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
- u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
- 
-+int mt7623_gsw_config(struct mtk_eth *eth);
-+
- #endif /* MTK_ETH_H */
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0058-dont-disable-clocks.patch b/target/linux/mediatek/patches-4.4/0058-dont-disable-clocks.patch
deleted file mode 100644
index d91915ccde..0000000000
--- a/target/linux/mediatek/patches-4.4/0058-dont-disable-clocks.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 36875ed8153d9b1eeae676579302a2fc746b286b Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 23 Jun 2015 23:46:00 +0200
-Subject: [PATCH 58/66] dont disable clocks
-
----
- drivers/clk/clk.c |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
-index f13c3f4..5e9ddae 100644
---- a/drivers/clk/clk.c
-+++ b/drivers/clk/clk.c
-@@ -233,7 +233,7 @@ unlock_out:
- 	clk_enable_unlock(flags);
- }
- 
--static bool clk_ignore_unused;
-+static bool clk_ignore_unused = true;
- static int __init clk_ignore_unused_setup(char *__unused)
- {
- 	clk_ignore_unused = true;
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch b/target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
new file mode 100644
index 0000000000..df3c218476
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0058-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
@@ -0,0 +1,1798 @@
+From cc1959d5bc9a709729fcd02d78f4c27394393109 Mon Sep 17 00:00:00 2001
+From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+Date: Wed, 2 Mar 2016 12:00:12 -0500
+Subject: [PATCH 58/78] mtd: mediatek: driver for MTK Smart Device Gen1 NAND
+
+This patch adds support for mediatek's SDG1 NFC nand controller
+embedded in SoC 2701.
+
+UBIFS support has been successfully tested.
+
+Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+---
+ drivers/mtd/nand/Kconfig            |    6 +
+ drivers/mtd/nand/Makefile           |    1 +
+ drivers/mtd/nand/mtksdg1_nand.c     | 1535 +++++++++++++++++++++++++++++++++++
+ drivers/mtd/nand/mtksdg1_nand_ecc.h |   75 ++
+ drivers/mtd/nand/mtksdg1_nand_nfi.h |  119 +++
+ 5 files changed, 1736 insertions(+)
+ create mode 100644 drivers/mtd/nand/mtksdg1_nand.c
+ create mode 100644 drivers/mtd/nand/mtksdg1_nand_ecc.h
+ create mode 100644 drivers/mtd/nand/mtksdg1_nand_nfi.h
+
+diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
+index 2896640..5ec072a 100644
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -546,4 +546,10 @@ config MTD_NAND_HISI504
+ 	help
+ 	  Enables support for NAND controller on Hisilicon SoC Hip04.
+ 
++config MTD_NAND_MTKSDG1
++	tristate "Support for NAND controller on MTK Smart Device SoCs"
++	depends on HAS_DMA
++	help
++	Enables support for NAND controller on MTK Smart Device SoCs.
++
+ endif # MTD_NAND
+diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
+index 2c7f014..2a2620c 100644
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
+ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
++obj-$(CONFIG_MTD_NAND_MTKSDG1)		+= mtksdg1_nand.o
+ 
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+diff --git a/drivers/mtd/nand/mtksdg1_nand.c b/drivers/mtd/nand/mtksdg1_nand.c
+new file mode 100644
+index 0000000..55dd17d
+--- /dev/null
++++ b/drivers/mtd/nand/mtksdg1_nand.c
+@@ -0,0 +1,1535 @@
++/*
++ * MTK smart device NAND Flash controller driver.
++ * Copyright (C) 2015-2016 MediaTek Inc.
++ * Authors:	Xiaolei Li		<xiaolei.li@mediatek.com>
++ *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz@linaro.org>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/of_mtd.h>
++#include <linux/delay.h>
++#include <linux/clk.h>
++#include <linux/mtd/partitions.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/mtd.h>
++#include <linux/module.h>
++
++#include "mtksdg1_nand_nfi.h"
++#include "mtksdg1_nand_ecc.h"
++
++#define MTK_IRQ_ECC		"mtksdg1-nand-ecc"
++#define MTK_IRQ_NFI		"mtksdg1-nand-nfi"
++#define MTK_NAME		"mtksdg1-nand"
++
++#define KB(x)			((x) * 1024UL)
++#define MB(x)			(KB(x) * 1024UL)
++
++#define SECTOR_SHIFT		(10)
++#define SECTOR_SIZE		(1UL << SECTOR_SHIFT)
++#define BYTES_TO_SECTORS(x)	((x) >> SECTOR_SHIFT)
++#define SECTORS_TO_BYTES(x)	((x) << SECTOR_SHIFT)
++
++#define MTK_TIMEOUT		(500)
++#define MTK_RESET_TIMEOUT	(1 * HZ)
++
++#define MTK_ECC_PARITY_BITS	(14)
++#define MTK_NAND_MAX_CHIP	(2)
++
++#define MTK_OOB_ON		(1)
++#define MTK_OOB_OFF		(0)
++
++/* raw accesses do not use ECC (ecc = !raw) */
++#define MTK_ECC_OFF		(1)
++#define MTK_ECC_ON		(0)
++
++struct mtk_nfc_clk {
++	struct clk *nfiecc_clk;
++	struct clk *nfi_clk;
++	struct clk *pad_clk;
++};
++
++struct mtk_nfc_saved_reg {
++	struct {
++		u32 enccnfg;
++		u32 deccnfg;
++	} ecc;
++	struct {
++		u32 emp_thresh;
++		u16 pagefmt;
++		u32 acccon;
++		u16 cnrnb;
++		u16 csel;
++	} nfi;
++};
++
++struct mtk_nfc_host {
++	struct mtk_nfc_clk clk;
++	struct nand_chip chip;
++	struct device *dev;
++
++	struct {
++		struct completion complete;
++		void __iomem *base;
++	} nfi;
++
++	struct {
++		struct completion complete;
++		void __iomem *base;
++		u32 dec_sec;
++	} ecc;
++
++	u32 fdm_reg[MTKSDG1_NFI_FDM_REG_SIZE / sizeof(u32)];
++	bool switch_oob;
++	u32 row_nob;
++	u8 *buffer;
++
++#ifdef CONFIG_PM_SLEEP
++	struct mtk_nfc_saved_reg saved_reg;
++#endif
++};
++
++static struct nand_ecclayout nand_2k_64 = {
++	.oobfree = { {0, 16} },
++};
++
++static struct nand_ecclayout nand_4k_128 = {
++	.oobfree = { {0, 32} },
++};
++
++/* NFI register access */
++static inline void mtk_nfi_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
++{
++	writel(val, host->nfi.base + reg);
++}
++static inline void mtk_nfi_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
++{
++	writew(val, host->nfi.base + reg);
++}
++static inline u32 mtk_nfi_readl(struct mtk_nfc_host *host, u32 reg)
++{
++	return readl_relaxed(host->nfi.base + reg);
++}
++static inline u16 mtk_nfi_readw(struct mtk_nfc_host *host, u32 reg)
++{
++	return readw_relaxed(host->nfi.base + reg);
++}
++static inline u8 mtk_nfi_readb(struct mtk_nfc_host *host, u32 reg)
++{
++	return readb_relaxed(host->nfi.base + reg);
++}
++
++/* ECC register access */
++static inline void mtk_ecc_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
++{
++	writel(val, host->ecc.base + reg);
++}
++static inline void mtk_ecc_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
++{
++	writew(val, host->ecc.base + reg);
++}
++static inline u32 mtk_ecc_readl(struct mtk_nfc_host *host, u32 reg)
++{
++	return readl_relaxed(host->ecc.base + reg);
++}
++static inline u16 mtk_ecc_readw(struct mtk_nfc_host *host, u32 reg)
++{
++	return readw_relaxed(host->ecc.base + reg);
++}
++
++static void mtk_nfc_hw_reset(struct mtk_nfc_host *host)
++{
++	unsigned long timeout = MTK_RESET_TIMEOUT;
++	struct device *dev = host->dev;
++	u32 val;
++
++	/* reset the state machine, data fifo and fdm data */
++	mtk_nfi_writel(host, CON_FIFO_FLUSH | CON_NFI_RST, MTKSDG1_NFI_CON);
++	timeout += jiffies;
++	do {
++		val = mtk_nfi_readl(host, MTKSDG1_NFI_MASTER_STA);
++		val &= MASTER_STA_MASK;
++		if (!val)
++			return;
++		usleep_range(50, 100);
++
++	} while (time_before(jiffies, timeout));
++
++	dev_warn(dev, "nfi master active after in reset [0x%x] = 0x%x\n",
++		MTKSDG1_NFI_MASTER_STA, val);
++};
++
++static int mtk_nfc_set_command(struct mtk_nfc_host *host, u8 command)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct device *dev = host->dev;
++	u32 val;
++
++	mtk_nfi_writel(host, command, MTKSDG1_NFI_CMD);
++
++	/* wait for the NFI core to enter command mode */
++	timeout += jiffies;
++	do {
++		val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
++		val &= STA_CMD;
++		if (!val)
++			return 0;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++	dev_warn(dev, "nfi core timed out entering command mode\n");
++
++	return -EIO;
++}
++
++static int mtk_nfc_set_address(struct mtk_nfc_host *host, u32 column, u32 row,
++		u8 colnob, u8 row_nob)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct device *dev = host->dev;
++	u32 addr_nob, val;
++
++	addr_nob = colnob | (row_nob << ADDR_ROW_NOB_SHIFT);
++	mtk_nfi_writel(host, column, MTKSDG1_NFI_COLADDR);
++	mtk_nfi_writel(host, row, MTKSDG1_NFI_ROWADDR);
++	mtk_nfi_writel(host, addr_nob, MTKSDG1_NFI_ADDRNOB);
++
++	/* wait for the NFI core to enter address mode */
++	timeout += jiffies;
++	do {
++		val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
++		val &= STA_ADDR;
++		if (!val)
++			return 0;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	dev_warn(dev, "nfi core timed out entering address mode\n");
++
++	return -EIO;
++}
++
++static inline void mtk_ecc_encoder_idle(struct mtk_nfc_host *host)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct device *dev = host->dev;
++	u32 val;
++
++	timeout += jiffies;
++	do {
++		val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIDLE);
++		val &= ENC_IDLE;
++		if (val)
++			return;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	dev_warn(dev, "hw init ecc encoder not idle\n");
++}
++
++static inline void mtk_ecc_decoder_idle(struct mtk_nfc_host *host)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct device *dev = host->dev;
++	u32 val;
++
++	timeout += jiffies;
++	do {
++		val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIDLE);
++		val &= DEC_IDLE;
++		if (val)
++			return;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	dev_warn(dev, "hw init ecc decoder not idle\n");
++}
++
++static int mtk_nfc_transfer_done(struct mtk_nfc_host *host, u32 sectors)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	u32 cnt;
++
++	/* wait for the sector count */
++	timeout += jiffies;
++	do {
++		cnt = mtk_nfi_readl(host, MTKSDG1_NFI_ADDRCNTR);
++		cnt &= CNTR_MASK;
++		if (cnt >= sectors)
++			return 0;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	return  -EIO;
++}
++
++static int mtk_nfc_subpage_done(struct mtk_nfc_host *host, int sectors)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	u32 val;
++
++	timeout += jiffies;
++	do {
++		val = mtk_nfi_readl(host, MTKSDG1_NFI_BYTELEN);
++		val &= CNTR_MASK;
++		if (val >= sectors)
++			return 0;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	return -EIO;
++}
++
++static inline int mtk_nfc_data_ready(struct mtk_nfc_host *host)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	u8 val;
++
++	timeout += jiffies;
++	do {
++		val = mtk_nfi_readw(host, MTKSDG1_NFI_PIO_DIRDY);
++		val &= PIO_DI_RDY;
++		if (val)
++			return 0;
++		cpu_relax();
++
++	} while (time_before(jiffies, timeout));
++
++	/* data _MUST_ not be accessed */
++	return -EIO;
++}
++
++static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	struct device *dev = host->dev;
++	u32 dec_size, enc_size;
++	u32 ecc_bit, ecc_level;
++	u32 spare, fmt;
++	u32 reg;
++
++	host->row_nob = 1;
++	if (chip->chipsize > MB(32))
++		host->row_nob = chip->chipsize > MB(128) ? 3 : 2;
++
++	spare = mtd->oobsize / BYTES_TO_SECTORS(mtd->writesize);
++	switch (spare) {
++	case 16:
++		ecc_bit = ECC_CNFG_4BIT;
++		ecc_level = 4;
++		break;
++	case 32:
++		ecc_bit = ECC_CNFG_12BIT;
++		ecc_level = 12;
++		break;
++	default:
++		dev_err(dev, "invalid spare size per sector: %d\n", spare);
++		return -EINVAL;
++	}
++
++	chip->ecc.strength = ecc_level;
++	chip->ecc.size = SECTOR_SIZE;
++
++	switch (mtd->writesize) {
++	case KB(2):
++		fmt = PAGEFMT_512_2K;
++		chip->ecc.layout = &nand_2k_64;
++		break;
++	case KB(4):
++		fmt = PAGEFMT_2K_4K;
++		chip->ecc.layout = &nand_4k_128;
++		break;
++	case KB(8):
++		fmt = PAGEFMT_4K_8K;
++		break;
++	default:
++		dev_err(dev, "invalid page size: %d\n", mtd->writesize);
++		return -EINVAL;
++	}
++
++	/* configure PAGE FMT */
++	reg = fmt;
++	reg |= PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT;
++	reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_SHIFT;
++	reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_ECC_SHIFT;
++	mtk_nfi_writew(host, reg, MTKSDG1_NFI_PAGEFMT);
++
++	/* configure ECC encoder (in bits) */
++	enc_size = (SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE) << 3;
++	reg = ecc_bit | ECC_NFI_MODE | (enc_size << ECC_MS_SHIFT);
++	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
++
++	/* configure ECC decoder (inbits) */
++	dec_size = enc_size + ecc_level * MTK_ECC_PARITY_BITS;
++	reg = ecc_bit | ECC_NFI_MODE | (dec_size << ECC_MS_SHIFT);
++	reg |= (DEC_CNFG_CORRECT | DEC_EMPTY_EN);
++	mtk_ecc_writel(host, reg, MTKSDG1_ECC_DECCNFG);
++
++	return 0;
++}
++
++static void mtk_nfc_device_reset(struct mtk_nfc_host *host)
++{
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct device *dev = host->dev;
++	u16 chip;
++	int rc;
++
++	mtk_nfc_hw_reset(host);
++
++	/* enable reset done interrupt */
++	mtk_nfi_writew(host, INTR_RST_DONE_EN, MTKSDG1_NFI_INTR_EN);
++
++	/* configure FSM for reset operation */
++	mtk_nfi_writew(host, CNFG_OP_RESET, MTKSDG1_NFI_CNFG);
++
++	init_completion(&host->nfi.complete);
++
++	mtk_nfc_set_command(host, NAND_CMD_RESET);
++	rc = wait_for_completion_timeout(&host->nfi.complete, timeout);
++	if (!rc) {
++		chip = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
++		dev_err(dev, "device(%d) reset timeout\n", chip);
++	}
++}
++
++static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
++{
++	struct nand_chip *nand = mtd_to_nand(mtd);
++	struct mtk_nfc_host *host = nand_get_controller_data(nand);
++
++	if (chip < 0)
++		return;
++
++	mtk_nfi_writel(host, chip, MTKSDG1_NFI_CSEL);
++}
++
++static inline bool mtk_nfc_cmd_supported(unsigned command)
++{
++	switch (command) {
++	case NAND_CMD_RESET:
++	case NAND_CMD_READID:
++	case NAND_CMD_STATUS:
++	case NAND_CMD_READOOB:
++	case NAND_CMD_ERASE1:
++	case NAND_CMD_ERASE2:
++	case NAND_CMD_SEQIN:
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_CACHEDPROG:
++	case NAND_CMD_READ0:
++		return true;
++	default:
++		return false;
++	}
++}
++
++static void mtk_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
++		int page_addr)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(mtd_to_nand(mtd));
++	unsigned long const cmd_timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	struct completion *p = &host->nfi.complete;
++	u32 val;
++	int rc;
++
++	if (mtk_nfc_cmd_supported(command))
++		mtk_nfc_hw_reset(host);
++
++	switch (command) {
++	case NAND_CMD_RESET:
++		mtk_nfc_device_reset(host);
++		break;
++	case NAND_CMD_READID:
++		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
++		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_READID);
++		mtk_nfc_set_address(host, column, 0, 1, 0);
++		mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
++		break;
++	case NAND_CMD_STATUS:
++		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
++		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_STATUS);
++		mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
++		break;
++	case NAND_CMD_READOOB:
++		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_READ;
++		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_READ0);
++		column += mtd->writesize;
++		mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
++		val = CON_BRD | (1 << CON_SEC_SHIFT);
++		mtk_nfi_writel(host, val, MTKSDG1_NFI_CON);
++		break;
++	case NAND_CMD_ERASE1:
++		mtk_nfi_writew(host, INTR_ERS_DONE_EN, MTKSDG1_NFI_INTR_EN);
++		mtk_nfi_writew(host, CNFG_OP_ERASE, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_ERASE1);
++		mtk_nfc_set_address(host, 0, page_addr, 0, host->row_nob);
++		break;
++	case NAND_CMD_ERASE2:
++		init_completion(p);
++		mtk_nfc_set_command(host, NAND_CMD_ERASE2);
++		rc = wait_for_completion_timeout(p, cmd_timeout);
++		if (!rc)
++			dev_err(host->dev, "erase command timeout\n");
++		break;
++	case NAND_CMD_SEQIN:
++		mtk_nfi_writew(host, CNFG_OP_PRGM, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_SEQIN);
++		mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
++		break;
++	case NAND_CMD_PAGEPROG:
++	case NAND_CMD_CACHEDPROG:
++		mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
++		init_completion(p);
++		mtk_nfc_set_command(host, command);
++		rc = wait_for_completion_timeout(p, cmd_timeout);
++		if (!rc)
++			dev_err(host->dev, "pageprogr command timeout\n");
++		break;
++	case NAND_CMD_READ0:
++		val = CNFG_OP_READ | CNFG_READ_EN;
++		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
++		mtk_nfc_set_command(host, NAND_CMD_READ0);
++		break;
++	default:
++		dev_warn(host->dev, "command 0x%x not supported\n", command);
++		break;
++	}
++}
++
++static uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	int rc;
++
++	rc = mtk_nfc_data_ready(host);
++	if (rc < 0) {
++		dev_err(host->dev, "data not ready\n");
++		return NAND_STATUS_FAIL;
++	}
++
++	return mtk_nfi_readb(host, MTKSDG1_NFI_DATAR);
++}
++
++static void mtk_nfc_write_fdm(struct nand_chip *chip, u32 sectors)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	u8 *src, *dst;
++	int i, j, reg;
++
++	for (i = 0; i < sectors ; i++) {
++		/* read FDM from OOB into private area */
++		src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
++		dst = (u8 *)host->fdm_reg;
++		memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
++
++		/* write FDM to registers */
++		for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
++			reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
++			reg += j * sizeof(host->fdm_reg[0]);
++			mtk_nfi_writel(host, host->fdm_reg[j], reg);
++		}
++	}
++}
++
++static int mtk_nfc_write_page(struct mtd_info *mtd,
++			struct nand_chip *chip, const uint8_t *buf,
++			int oob_on, int page, int raw)
++{
++
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	struct completion *nfi = &host->nfi.complete;
++	struct device *dev = host->dev;
++	const bool use_ecc = !raw;
++	void *q = (void *) buf;
++	dma_addr_t dma_addr;
++	size_t dmasize;
++	u32 reg;
++	int ret;
++
++	dmasize = mtd->writesize + (raw ? mtd->oobsize : 0);
++
++	dma_addr = dma_map_single(dev, q, dmasize, DMA_TO_DEVICE);
++	if (dma_mapping_error(host->dev, dma_addr)) {
++		dev_err(host->dev, "dma mapping error\n");
++		return -EINVAL;
++	}
++
++	reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
++	reg |= CNFG_AHB | CNFG_DMA_BURST_EN;
++	if (use_ecc) {
++		/**
++		 * OOB will be generated
++		 *  - FDM: from register
++		 *  - ECC: from HW
++		 */
++		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
++		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
++
++		mtk_ecc_encoder_idle(host);
++		mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
++
++		/* write OOB into the FDM registers (OOB area in MTK NAND) */
++		if (oob_on)
++			mtk_nfc_write_fdm(chip, chip->ecc.steps);
++	} else {
++		/* OOB is part of the DMA transfer */
++		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
++	}
++
++	mtk_nfi_writel(host, chip->ecc.steps << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
++	mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
++	mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
++
++	init_completion(nfi);
++
++	/* start DMA */
++	reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BWR;
++	mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
++
++	ret = wait_for_completion_timeout(nfi, msecs_to_jiffies(MTK_TIMEOUT));
++	if (!ret) {
++		dev_err(dev, "program ahb done timeout\n");
++		mtk_nfi_writew(host, 0, MTKSDG1_NFI_INTR_EN);
++		ret = -ETIMEDOUT;
++		goto timeout;
++	}
++
++	ret = mtk_nfc_transfer_done(host, chip->ecc.steps);
++	if (ret < 0)
++		dev_err(dev, "hwecc write timeout\n");
++timeout:
++	dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
++
++	if (use_ecc) {
++		mtk_ecc_encoder_idle(host);
++		mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
++	}
++
++	mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
++
++	return ret;
++}
++
++static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
++			struct nand_chip *chip, const uint8_t *buf,
++			int oob_on, int page)
++{
++	return mtk_nfc_write_page(mtd, chip, buf, oob_on, page, MTK_ECC_ON);
++}
++
++static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++					const uint8_t *buf, int oob_on, int pg)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	uint8_t *src, *dst;
++	size_t len;
++	u32 i;
++
++	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
++
++	/* MTK internal 4KB page data layout:
++	 * ----------------------------------
++	 * PAGE = 4KB, SECTOR = 1KB, OOB=128B
++	 * page = sector_oob1 + sector_oob2 + sector_oob3 + sector_oob4
++	 * sector_oob = data (1KB) + FDM (8B) + ECC parity (21B) + free (3B)
++	 *
++	 */
++	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
++
++	for (i = 0; i < chip->ecc.steps; i++) {
++
++		if (buf) {
++			src = (uint8_t *) buf + i * SECTOR_SIZE;
++			dst = host->buffer + i * len;
++			memcpy(dst, src, SECTOR_SIZE);
++		}
++
++		if (oob_on) {
++			src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
++			dst = host->buffer + i * len + SECTOR_SIZE;
++			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
++		}
++	}
++
++	return mtk_nfc_write_page(mtd, chip, host->buffer, MTK_OOB_OFF, pg,
++				MTK_ECC_OFF);
++}
++
++static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	struct completion *ecc = &host->ecc.complete;
++	u32 reg, parity_bytes, i;
++	dma_addr_t dma_addr;
++	u32 *parity_region;
++	int rc, ret = 0;
++	size_t dmasize;
++
++	dmasize = SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE;
++	dma_addr = dma_map_single(host->dev, data, dmasize, DMA_TO_DEVICE);
++	if (dma_mapping_error(host->dev, dma_addr)) {
++		dev_err(host->dev, "dma mapping error\n");
++		return -EINVAL;
++	}
++
++	/* enable the encoder in DMA mode to calculate the ECC bytes  */
++	reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
++	reg &= (~ECC_ENC_MODE_MASK);
++	reg |= ECC_DMA_MODE;
++	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
++
++	mtk_ecc_writel(host, ENC_IRQEN, MTKSDG1_ECC_ENCIRQ_EN);
++	mtk_ecc_writel(host, lower_32_bits(dma_addr), MTKSDG1_ECC_ENCDIADDR);
++
++	init_completion(ecc);
++	mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
++
++	rc = wait_for_completion_timeout(ecc, msecs_to_jiffies(MTK_TIMEOUT));
++	if (!rc) {
++		dev_err(host->dev, "ecc encode done timeout\n");
++		mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
++		ret = -ETIMEDOUT;
++		goto timeout;
++	}
++
++	mtk_ecc_encoder_idle(host);
++
++	/**
++	 * Program ECC bytes to OOB
++	 *	per sector oob = FDM + ECC + SPARE
++	 */
++
++	parity_region = (u32 *) (data + SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE);
++	parity_bytes = (chip->ecc.strength * MTK_ECC_PARITY_BITS + 7) >> 3;
++
++	/* write the parity bytes generated by the ECC back to the OOB region */
++	for (i = 0; i < parity_bytes; i += sizeof(u32))
++		*parity_region++ = mtk_ecc_readl(host, MTKSDG1_ECC_ENCPAR0 + i);
++
++timeout:
++
++	dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
++
++	mtk_ecc_writew(host, 0, MTKSDG1_ECC_ENCCON);
++	reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
++	reg &= (~ECC_ENC_MODE_MASK);
++	reg |= ECC_NFI_MODE;
++	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
++
++	return ret;
++}
++
++static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
++		struct nand_chip *chip, uint32_t offset, uint32_t data_len,
++		const uint8_t *buf, int oob_on, int pg)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	uint8_t *src, *dst;
++	u32 start, end;
++	size_t len;
++	int i, ret;
++
++	start = BYTES_TO_SECTORS(offset);
++	end = BYTES_TO_SECTORS(offset + data_len + SECTOR_SIZE - 1);
++
++	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
++
++	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
++	for (i = 0; i < chip->ecc.steps; i++) {
++
++		/* write data */
++		src = (uint8_t *) buf + i * SECTOR_SIZE;
++		dst = host->buffer + i * len;
++		memcpy(dst, src, SECTOR_SIZE);
++
++		if (i < start)
++			continue;
++
++		if (i >= end)
++			continue;
++
++		/* write fdm */
++		if (oob_on) {
++			src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
++			dst = host->buffer + i * len + SECTOR_SIZE;
++			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
++		}
++
++		/* point to the start of data */
++		src = host->buffer + i * len;
++
++		/* program the CRC back to the OOB */
++		ret = mtk_nfc_sector_encode(chip, src);
++		if (ret < 0)
++			return ret;
++	}
++
++	/* use the data in the private buffer (now with FDM and CRC) to perform
++	 * a raw write
++	 */
++	src = host->buffer;
++	return mtk_nfc_write_page(mtd, chip, src, MTK_OOB_OFF, pg, MTK_ECC_OFF);
++}
++
++static int mtk_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				int page)
++{
++	u8 *buf = chip->buffers->databuf;
++	int ret;
++
++	memset(buf, 0xff, mtd->writesize);
++	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
++	ret = mtk_nfc_write_page_hwecc(mtd, chip, buf, MTK_OOB_ON, page);
++	if (ret < 0)
++		return -EIO;
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++	ret = chip->waitfunc(mtd, chip);
++
++	return ret & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++static int mtk_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++					int page)
++{
++	int ret;
++
++	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
++	ret = mtk_nfc_write_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
++	if (ret < 0)
++		return -EIO;
++
++	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++	ret = chip->waitfunc(mtd, chip);
++
++	return ret & NAND_STATUS_FAIL ? -EIO : 0;
++}
++
++static int mtk_nfc_ecc_check(struct mtd_info *mtd, struct nand_chip *chip,
++				u32 sectors)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	u32 offset, i, err, max_bitflip;
++
++	max_bitflip = 0;
++
++	for (i = 0; i < sectors; i++) {
++		offset = (i >> 2) << 2;
++		err = mtk_ecc_readl(host, MTKSDG1_ECC_DECENUM0 + offset);
++		err = err >> ((i % 4) * 8);
++		err &= ERR_MASK;
++		if (err == ERR_MASK) {
++			/* uncorrectable errors */
++			mtd->ecc_stats.failed++;
++			continue;
++		}
++
++		mtd->ecc_stats.corrected += err;
++		max_bitflip = max_t(u32, max_bitflip, err);
++	}
++
++	return max_bitflip;
++}
++
++static void mtk_nfc_read_fdm(struct nand_chip *chip, u32 sectors)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	int i, j, reg;
++	u8 *dst, *src;
++
++	for (i = 0; i < sectors; i++) {
++		/* read FDM register into host memory */
++		for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
++			reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
++			reg += j * sizeof(host->fdm_reg[0]);
++			host->fdm_reg[j] = mtk_nfi_readl(host, reg);
++		}
++
++		/* copy FDM register from host to OOB */
++		src = (u8 *)host->fdm_reg;
++		dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
++		memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
++	}
++}
++
++static int mtk_nfc_update_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				u8 *buf, u32 sectors)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	int i, bitflips = 0;
++
++	/* if the page is empty, no bitflips and clear data and oob */
++	if (mtk_nfi_readl(host, MTKSDG1_NFI_STA) & STA_EMP_PAGE) {
++		memset(buf, 0xff, SECTORS_TO_BYTES(sectors));
++
++		/* empty page: update OOB with 0xFF */
++		for (i = 0; i < sectors; i++) {
++			memset(chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE,
++				0xff, MTKSDG1_NFI_FDM_REG_SIZE);
++		}
++	} else {
++		/* update OOB with HW info */
++		mtk_nfc_read_fdm(chip, sectors);
++
++		/* return the bitflips */
++		bitflips = mtk_nfc_ecc_check(mtd, chip, sectors);
++	}
++
++	return bitflips;
++}
++
++static int mtk_nfc_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++	struct nand_chip *chip = mtd_to_nand(mtd);
++	u8 *buf = chip->buffers->databuf;
++	int rc, i, pg;
++
++	/* block_markbad writes 0x00 at data and OOB */
++	memset(buf, 0x00, mtd->writesize + mtd->oobsize);
++
++	/* Write to first/last page(s) if necessary */
++	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
++		ofs += mtd->erasesize - mtd->writesize;
++
++	i = 0;
++	do {
++		pg = (int)(ofs >> chip->page_shift);
++
++		/**
++		 *  write 0x00 to DATA & OOB in flash
++		 *  No need to reorganize the page since it is all 0x00
++		 */
++		chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, pg);
++		rc = mtk_nfc_write_page(mtd, chip, buf, MTK_OOB_OFF, pg,
++			MTK_ECC_OFF);
++		if (rc < 0)
++			return rc;
++
++		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++		rc = chip->waitfunc(mtd, chip);
++		rc = rc & NAND_STATUS_FAIL ? -EIO : 0;
++		if (rc < 0)
++			return rc;
++
++		ofs += mtd->writesize;
++		i++;
++
++	} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
++
++	return 0;
++}
++
++static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
++		uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
++		int page, int raw)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
++	u32 reg, column, spare, sectors, start, end;
++	struct completion *nfi, *ecc;
++	const bool use_ecc = !raw;
++	int bitflips = -EIO;
++	dma_addr_t dma_addr;
++	size_t len;
++	u8 *buf;
++	int rc;
++
++	nfi = &host->nfi.complete;
++	ecc = &host->ecc.complete;
++
++	start = BYTES_TO_SECTORS(data_offs);
++	end = BYTES_TO_SECTORS(data_offs + readlen + SECTOR_SIZE - 1);
++	sectors = end - start;
++
++	spare = mtd->oobsize / chip->ecc.steps;
++	column =  start * (SECTOR_SIZE + spare);
++
++	len = SECTORS_TO_BYTES(sectors) + (raw ? sectors * spare : 0);
++	buf = bufpoi + SECTORS_TO_BYTES(start);
++
++	/* map the device memory */
++	dma_addr = dma_map_single(host->dev, buf, len, DMA_FROM_DEVICE);
++	if (dma_mapping_error(host->dev, dma_addr)) {
++		dev_err(host->dev, "dma mapping error\n");
++		return -EINVAL;
++	}
++
++	/* configure the transfer  */
++	reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
++	reg |= CNFG_DMA_BURST_EN | CNFG_AHB;
++	if (use_ecc) {
++		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
++		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
++
++		/* enable encoder */
++		mtk_ecc_decoder_idle(host);
++		mtk_ecc_writel(host, DEC_EN, MTKSDG1_ECC_DECCON);
++	} else
++		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
++
++	mtk_nfi_writel(host, sectors << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
++	mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
++
++	init_completion(nfi);
++
++	mtk_nfc_set_address(host, column, page, 2, host->row_nob);
++	mtk_nfc_set_command(host, NAND_CMD_READSTART);
++	rc = wait_for_completion_timeout(nfi, timeout);
++	if (!rc) {
++		dev_err(host->dev, "read busy return timeout\n");
++		goto error;
++	}
++
++	mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
++	mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
++
++	if (use_ecc) {
++		/* program ECC with sector count */
++		host->ecc.dec_sec = sectors;
++		init_completion(ecc);
++		mtk_ecc_writew(host, DEC_IRQEN, MTKSDG1_ECC_DECIRQ_EN);
++	}
++
++	init_completion(nfi);
++
++	/* start DMA */
++	reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BRD;
++	mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
++
++	rc = wait_for_completion_timeout(nfi, timeout);
++	if (!rc)
++		dev_warn(host->dev, "read ahb/dma done timeout\n");
++
++	/* DMA interrupt didn't trigger, check page done just in case */
++	rc = mtk_nfc_subpage_done(host, sectors);
++	if (rc < 0) {
++		dev_err(host->dev, "subpage done timeout\n");
++		goto error;
++	}
++
++	/* raw transfer successful */
++	bitflips = 0;
++
++	if (use_ecc) {
++		rc = wait_for_completion_timeout(ecc, timeout);
++		if (!rc) {
++			dev_err(host->dev, "ecc decode timeout\n");
++			host->ecc.dec_sec = 0;
++			bitflips = -ETIMEDOUT;
++			goto error;
++		}
++		bitflips = mtk_nfc_update_oob(mtd, chip, buf, sectors);
++	}
++
++error:
++	dma_unmap_single(host->dev, dma_addr, len, DMA_FROM_DEVICE);
++
++	if (use_ecc) {
++		/* make sure the ECC dec irq  is disabled */
++		mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
++		mtk_ecc_decoder_idle(host);
++
++		/* disable ECC dec */
++		mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECCON);
++	}
++
++	mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
++
++	return bitflips;
++}
++
++static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
++				struct nand_chip *chip, uint32_t data_offs,
++				uint32_t readlen, uint8_t *bufpoi, int page)
++{
++	return mtk_nfc_read_subpage(mtd, chip, data_offs, readlen,
++					bufpoi, page, MTK_ECC_ON);
++}
++
++static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
++				uint8_t *buf, int oob_on, int page)
++{
++	return mtk_nfc_read_subpage_hwecc(mtd, chip, 0, mtd->writesize,
++						buf, page);
++}
++
++static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				uint8_t *buf, int oob_on, int page)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	uint8_t *src, *dst;
++	int i, ret;
++	size_t len;
++
++	dst = host->buffer;
++	memset(dst, 0xff, mtd->writesize + mtd->oobsize);
++	ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, dst, page, 1);
++	if (ret < 0)
++		return ret;
++
++	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
++
++	/* copy to the output buffer */
++	for (i = 0; i < chip->ecc.steps; i++) {
++
++		/* copy sector data */
++		if (buf) {
++			src = host->buffer + i * len;
++			dst = buf + i * SECTOR_SIZE;
++			memcpy(dst, src, SECTOR_SIZE);
++		}
++
++		/* copy FDM data to OOB */
++		if (oob_on) {
++			src = host->buffer + i * len + SECTOR_SIZE;
++			dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
++			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
++		}
++	}
++
++	return ret;
++}
++
++static void mtk_nfc_switch_oob(struct mtd_info *mtd, struct nand_chip *chip,
++					uint8_t *buf)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	size_t spare;
++	u32 sectors;
++	u8 *bufpoi;
++	int len;
++
++	spare = mtd->oobsize / chip->ecc.steps;
++	sectors = mtd->writesize / (SECTOR_SIZE + spare);
++
++	/**
++	 * MTK: DATA+oob1, DATA+oob2, DATA+oob3 ...
++	 * LNX: DATA+OOB
++	 */
++	/* point to the last oob_i from the NAND device*/
++	bufpoi = buf + mtd->writesize - (sectors * spare);
++	len = sizeof(host->fdm_reg);
++
++	/* copy NAND oob to private area */
++	memcpy(host->fdm_reg, bufpoi, len);
++
++	/* copy oob_poi to NAND */
++	memcpy(bufpoi, chip->oob_poi, len);
++
++	/* copy NAND oob to oob_poi */
++	memcpy(chip->oob_poi, host->fdm_reg, sizeof(host->fdm_reg));
++	memset(host->fdm_reg, 0x00, len);
++}
++
++static int mtk_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				int page)
++{
++	struct mtk_nfc_host *host = nand_get_controller_data(chip);
++	u8 *buf = chip->buffers->databuf;
++	struct mtd_ecc_stats stats;
++	int ret;
++
++	stats = mtd->ecc_stats;
++
++	memset(buf, 0xff, mtd->writesize);
++	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
++
++	ret = mtk_nfc_read_page_hwecc(mtd, chip, buf, 1, page);
++
++	if (host->switch_oob)
++		mtk_nfc_switch_oob(mtd, chip, buf);
++
++	if (ret < mtd->bitflip_threshold)
++		mtd->ecc_stats.corrected = stats.corrected;
++
++	return ret;
++}
++
++static int mtk_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				int page)
++{
++	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
++
++	return mtk_nfc_read_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
++}
++
++static inline void mtk_nfc_hw_init(struct mtk_nfc_host *host)
++{
++	mtk_nfi_writel(host, 0x10804211, MTKSDG1_NFI_ACCCON);
++	mtk_nfi_writew(host, 0xf1, MTKSDG1_NFI_CNRNB);
++	mtk_nfc_hw_reset(host);
++
++	/* clear interrupt */
++	mtk_nfi_readl(host, MTKSDG1_NFI_INTR_STA);
++	mtk_nfi_writel(host, 0, MTKSDG1_NFI_INTR_EN);
++
++	/* ECC encoder init */
++	mtk_ecc_encoder_idle(host);
++	mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
++
++	/* ECC decoder init */
++	mtk_ecc_decoder_idle(host);
++	mtk_ecc_writel(host, DEC_DE, MTKSDG1_ECC_DECCON);
++}
++
++static irqreturn_t mtk_nfi_irq(int irq, void *devid)
++{
++	struct mtk_nfc_host *host = devid;
++	u16 sta, ien;
++
++	sta = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_STA);
++	ien = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_EN);
++
++	if (!(sta & ien))
++		return IRQ_NONE;
++
++	mtk_nfi_writew(host, ~sta & ien, MTKSDG1_NFI_INTR_EN);
++	complete(&host->nfi.complete);
++
++	return IRQ_HANDLED;
++}
++
++static irqreturn_t mtk_ecc_irq(int irq, void *devid)
++{
++	struct mtk_nfc_host *host = devid;
++	u32 reg_val, mask;
++
++	reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIRQ_STA);
++	if (reg_val & DEC_IRQEN) {
++		if (host->ecc.dec_sec) {
++			mask = 1 << (host->ecc.dec_sec - 1);
++			reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECDONE);
++			if (mask & reg_val) {
++				host->ecc.dec_sec = 0;
++				complete(&host->ecc.complete);
++				mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
++			}
++		} else
++			dev_warn(host->dev, "spurious DEC_IRQ\n");
++
++		return IRQ_HANDLED;
++	}
++
++	reg_val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIRQ_STA);
++	if (reg_val & ENC_IRQEN) {
++		complete(&host->ecc.complete);
++		mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
++
++		return IRQ_HANDLED;
++	}
++
++	return IRQ_NONE;
++}
++
++static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
++{
++	int ret;
++
++	ret = clk_prepare_enable(clk->nfi_clk);
++	if (ret) {
++		dev_err(dev, "failed to enable nfi clk\n");
++		return ret;
++	}
++
++	ret = clk_prepare_enable(clk->nfiecc_clk);
++	if (ret) {
++		dev_err(dev, "failed to enable nfiecc clk\n");
++		goto out_nfiecc_clk_disable;
++	}
++
++	ret = clk_prepare_enable(clk->pad_clk);
++	if (ret) {
++		dev_err(dev, "failed to enable pad clk\n");
++		goto out_pad_clk_disable;
++	}
++
++	return 0;
++
++out_pad_clk_disable:
++	clk_disable_unprepare(clk->nfiecc_clk);
++
++out_nfiecc_clk_disable:
++	clk_disable_unprepare(clk->nfi_clk);
++
++	return ret;
++}
++
++static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
++{
++	clk_disable_unprepare(clk->nfi_clk);
++	clk_disable_unprepare(clk->nfiecc_clk);
++	clk_disable_unprepare(clk->pad_clk);
++}
++
++static int mtk_nfc_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = dev->of_node;
++	struct mtk_nfc_host *host;
++	struct nand_chip *chip;
++	struct mtd_info *mtd;
++	struct resource *res;
++	int ret, irq;
++	size_t len;
++
++	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
++	if (!host)
++		return -ENOMEM;
++
++	chip = &host->chip;
++	mtd = nand_to_mtd(chip);
++	host->dev = dev;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	host->nfi.base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(host->nfi.base)) {
++		ret = PTR_ERR(host->nfi.base);
++		dev_err(dev, "no nfi base\n");
++		return ret;
++	}
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++	host->ecc.base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(host->ecc.base)) {
++		ret = PTR_ERR(host->ecc.base);
++		dev_err(dev, "no ecc base\n");
++		return ret;
++	}
++
++	host->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
++	if (IS_ERR(host->clk.nfi_clk)) {
++		dev_err(dev, "no clk\n");
++		ret = PTR_ERR(host->clk.nfi_clk);
++		return ret;
++	}
++
++	host->clk.nfiecc_clk = devm_clk_get(dev, "nfiecc_clk");
++	if (IS_ERR(host->clk.nfiecc_clk)) {
++		dev_err(dev, "no ecc clk\n");
++		ret = PTR_ERR(host->clk.nfiecc_clk);
++		return ret;
++	}
++
++	host->clk.pad_clk = devm_clk_get(dev, "pad_clk");
++	if (IS_ERR(host->clk.pad_clk)) {
++		dev_err(dev, "no pad clk\n");
++		ret = PTR_ERR(host->clk.pad_clk);
++		return ret;
++	}
++
++	ret = mtk_nfc_enable_clk(dev, &host->clk);
++	if (ret)
++		return ret;
++
++	irq = platform_get_irq(pdev, 0);
++	if (irq < 0) {
++		dev_err(dev, "no nfi irq resource\n");
++		ret = -EINVAL;
++		goto clk_disable;
++	}
++
++	ret = devm_request_irq(dev, irq, mtk_nfi_irq, 0x0, MTK_IRQ_NFI, host);
++	if (ret) {
++		dev_err(dev, "failed to request nfi irq\n");
++		goto clk_disable;
++	}
++
++	irq = platform_get_irq(pdev, 1);
++	if (irq < 0) {
++		dev_err(dev, "no ecc irq resource\n");
++		ret = -EINVAL;
++		goto clk_disable;
++	}
++
++	ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, MTK_IRQ_ECC, host);
++	if (ret) {
++		dev_err(dev, "failed to request ecc irq\n");
++		goto clk_disable;
++	}
++
++	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
++	if (ret) {
++		dev_err(dev, "failed to set dma mask\n");
++		goto clk_disable;
++	}
++
++	platform_set_drvdata(pdev, host);
++
++	mtd_set_of_node(mtd, np);
++	mtd->owner = THIS_MODULE;
++	mtd->dev.parent = dev;
++	mtd->name = MTK_NAME;
++
++	nand_set_controller_data(chip, host);
++	chip->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
++	chip->block_markbad = mtk_nfc_block_markbad;
++	chip->select_chip = mtk_nfc_select_chip;
++	chip->read_byte = mtk_nfc_read_byte;
++	chip->cmdfunc = mtk_nfc_cmdfunc;
++	chip->ecc.mode = NAND_ECC_HW;
++	chip->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
++	chip->ecc.write_page_raw = mtk_nfc_write_page_raw;
++	chip->ecc.write_page = mtk_nfc_write_page_hwecc;
++	chip->ecc.write_oob_raw = mtk_nfc_write_oob_raw;
++	chip->ecc.write_oob = mtk_nfc_write_oob;
++	chip->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
++	chip->ecc.read_page_raw = mtk_nfc_read_page_raw;
++	chip->ecc.read_oob_raw = mtk_nfc_read_oob_raw;
++	chip->ecc.read_page = mtk_nfc_read_page_hwecc;
++	chip->ecc.read_oob = mtk_nfc_read_oob;
++
++	mtk_nfc_hw_init(host);
++
++	ret = nand_scan_ident(mtd, MTK_NAND_MAX_CHIP, NULL);
++	if (ret) {
++		ret = -ENODEV;
++		goto clk_disable;
++	}
++
++	ret = mtk_nfc_hw_runtime_config(mtd);
++	if (ret < 0) {
++		dev_err(dev, "nand device not supported\n");
++		goto clk_disable;
++	}
++
++	len = mtd->writesize + mtd->oobsize;
++	host->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
++	if (!host->buffer) {
++		ret = -ENOMEM;
++		goto clk_disable;
++	}
++
++	/* required to create bbt table if not present */
++	host->switch_oob = true;
++	ret = nand_scan_tail(mtd);
++	if (ret) {
++		ret = -ENODEV;
++		goto clk_disable;
++	}
++	host->switch_oob = false;
++
++	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
++	if (ret) {
++		dev_err(dev, "mtd parse partition error\n");
++		goto nand_free;
++	}
++
++	return 0;
++
++nand_free:
++	nand_release(mtd);
++
++clk_disable:
++	mtk_nfc_disable_clk(&host->clk);
++
++	return ret;
++}
++
++static int mtk_nfc_remove(struct platform_device *pdev)
++{
++	struct mtk_nfc_host *host = platform_get_drvdata(pdev);
++	struct mtd_info *mtd = nand_to_mtd(&host->chip);
++
++	nand_release(mtd);
++	mtk_nfc_disable_clk(&host->clk);
++
++	return 0;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static int mtk_nfc_suspend(struct device *dev)
++{
++	struct mtk_nfc_host *host = dev_get_drvdata(dev);
++	struct mtk_nfc_saved_reg *reg = &host->saved_reg;
++
++	reg->nfi.emp_thresh = mtk_nfi_readl(host, MTKSDG1_NFI_EMPTY_THRESH);
++	reg->ecc.enccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
++	reg->ecc.deccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_DECCNFG);
++	reg->nfi.pagefmt = mtk_nfi_readw(host, MTKSDG1_NFI_PAGEFMT);
++	reg->nfi.acccon = mtk_nfi_readl(host, MTKSDG1_NFI_ACCCON);
++	reg->nfi.cnrnb = mtk_nfi_readw(host, MTKSDG1_NFI_CNRNB);
++	reg->nfi.csel = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
++
++	mtk_nfc_disable_clk(&host->clk);
++
++	return 0;
++}
++
++static int mtk_nfc_resume(struct device *dev)
++{
++	struct mtk_nfc_host *host = dev_get_drvdata(dev);
++	struct mtk_nfc_saved_reg *reg = &host->saved_reg;
++	struct nand_chip *chip = &host->chip;
++	struct mtd_info *mtd = nand_to_mtd(chip);
++	int ret;
++	u32 i;
++
++	udelay(200);
++
++	ret = mtk_nfc_enable_clk(dev, &host->clk);
++	if (ret)
++		return ret;
++
++	for (i = 0; i < chip->numchips; i++) {
++		chip->select_chip(mtd, i);
++		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++	}
++
++	mtk_nfi_writel(host, reg->nfi.emp_thresh, MTKSDG1_NFI_EMPTY_THRESH);
++	mtk_nfi_writew(host, reg->nfi.pagefmt, MTKSDG1_NFI_PAGEFMT);
++	mtk_ecc_writel(host, reg->ecc.enccnfg, MTKSDG1_ECC_ENCCNFG);
++	mtk_ecc_writel(host, reg->ecc.deccnfg, MTKSDG1_ECC_DECCNFG);
++	mtk_nfi_writel(host, reg->nfi.acccon, MTKSDG1_NFI_ACCCON);
++	mtk_nfi_writew(host, reg->nfi.cnrnb, MTKSDG1_NFI_CNRNB);
++	mtk_nfi_writew(host, reg->nfi.csel, MTKSDG1_NFI_CSEL);
++
++	return 0;
++}
++
++static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
++#endif
++
++static const struct of_device_id mtk_nfc_id_table[] = {
++	{ .compatible = "mediatek,mt2701-nfc" },
++	{}
++};
++MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
++
++static struct platform_driver mtk_nfc_driver = {
++	.probe  = mtk_nfc_probe,
++	.remove = mtk_nfc_remove,
++	.driver = {
++		.name  = MTK_NAME,
++		.of_match_table = mtk_nfc_id_table,
++#ifdef CONFIG_PM_SLEEP
++		.pm = &mtk_nfc_pm_ops,
++#endif
++	},
++};
++
++module_platform_driver(mtk_nfc_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
++MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
++
+diff --git a/drivers/mtd/nand/mtksdg1_nand_ecc.h b/drivers/mtd/nand/mtksdg1_nand_ecc.h
+new file mode 100644
+index 0000000..d90b196
+--- /dev/null
++++ b/drivers/mtd/nand/mtksdg1_nand_ecc.h
+@@ -0,0 +1,75 @@
++/*
++ * MTK smart device ECC engine register.
++ * Copyright (C) 2015-2016 MediaTek Inc.
++ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#ifndef MTKSDG1_NAND_ECC_H
++#define MTKSDG1_NAND_ECC_H
++
++/* ECC engine register definition */
++#define MTKSDG1_ECC_ENCCON		(0x00)
++#define		ENC_EN			(1)
++#define		ENC_DE			(0)
++
++#define MTKSDG1_ECC_ENCCNFG		(0x04)
++#define		ECC_CNFG_4BIT		(0)
++#define		ECC_CNFG_12BIT		(4)
++#define		ECC_NFI_MODE		BIT(5)
++#define		ECC_DMA_MODE		(0)
++#define		ECC_ENC_MODE_MASK	(0x3 << 5)
++#define		ECC_MS_SHIFT		(16)
++
++#define MTKSDG1_ECC_ENCDIADDR		(0x08)
++
++#define MTKSDG1_ECC_ENCIDLE		(0x0C)
++#define		ENC_IDLE		BIT(0)
++
++#define MTKSDG1_ECC_ENCPAR0		(0x10)
++#define MTKSDG1_ECC_ENCSTA		(0x7C)
++
++#define MTKSDG1_ECC_ENCIRQ_EN		(0x80)
++#define		ENC_IRQEN		BIT(0)
++
++#define MTKSDG1_ECC_ENCIRQ_STA		(0x84)
++
++#define MTKSDG1_ECC_DECCON		(0x100)
++#define		DEC_EN			(1)
++#define		DEC_DE			(0)
++
++#define MTKSDG1_ECC_DECCNFG		(0x104)
++#define		DEC_EMPTY_EN		BIT(31)
++#define		DEC_CNFG_FER		(0x1 << 12)
++#define		DEC_CNFG_EL		(0x2 << 12)
++#define		DEC_CNFG_CORRECT	(0x3 << 12)
++
++#define MTKSDG1_ECC_DECIDLE		(0x10C)
++#define		DEC_IDLE		BIT(0)
++
++#define MTKSDG1_ECC_DECFER		(0x110)
++
++#define MTKSDG1_ECC_DECENUM0		(0x114)
++#define		ERR_MASK		(0x3f)
++
++#define MTKSDG1_ECC_DECDONE		(0x124)
++
++#define MTKSDG1_ECC_DECEL0		(0x128)
++
++#define MTKSDG1_ECC_DECIRQ_EN		(0x200)
++#define		DEC_IRQEN		BIT(0)
++
++#define MTKSDG1_ECC_DECIRQ_STA		(0x204)
++
++#define MTKSDG1_ECC_DECFSM		(0x208)
++#define		DECFSM_MASK		(0x7f0f0f0f)
++#define		DECFSM_IDLE		(0x01010101)
++#endif
+diff --git a/drivers/mtd/nand/mtksdg1_nand_nfi.h b/drivers/mtd/nand/mtksdg1_nand_nfi.h
+new file mode 100644
+index 0000000..a9aa6f6
+--- /dev/null
++++ b/drivers/mtd/nand/mtksdg1_nand_nfi.h
+@@ -0,0 +1,119 @@
++/*
++ * MTK smart device NAND Flash controller register.
++ * Copyright (C) 2015-2016 MediaTek Inc.
++ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#ifndef MTKSDG1_NAND_NFI_H
++#define MTKSDG1_NAND_NFI_H
++
++/* NAND controller register definition */
++#define MTKSDG1_NFI_CNFG		(0x00)
++#define		CNFG_AHB		BIT(0)
++#define		CNFG_READ_EN		BIT(1)
++#define		CNFG_DMA_BURST_EN	BIT(2)
++#define		CNFG_BYTE_RW		BIT(6)
++#define		CNFG_HW_ECC_EN		BIT(8)
++#define		CNFG_AUTO_FMT_EN	BIT(9)
++#define		CNFG_OP_IDLE		(0 << 12)
++#define		CNFG_OP_READ		(1 << 12)
++#define		CNFG_OP_SRD		(2 << 12)
++#define		CNFG_OP_PRGM		(3 << 12)
++#define		CNFG_OP_ERASE		(4 << 12)
++#define		CNFG_OP_RESET		(5 << 12)
++#define		CNFG_OP_CUST		(6 << 12)
++
++#define MTKSDG1_NFI_PAGEFMT		(0x04)
++#define		PAGEFMT_FDM_ECC_SHIFT	(12)
++#define		PAGEFMT_FDM_SHIFT	(8)
++#define		PAGEFMT_SPARE_16	(0)
++#define		PAGEFMT_SPARE_32	(4)
++#define		PAGEFMT_SPARE_SHIFT	(4)
++#define		PAGEFMT_SEC_SEL_512	BIT(2)
++#define		PAGEFMT_512_2K		(0)
++#define		PAGEFMT_2K_4K		(1)
++#define		PAGEFMT_4K_8K		(2)
++
++/* NFI control */
++#define MTKSDG1_NFI_CON			(0x08)
++#define		CON_FIFO_FLUSH		BIT(0)
++#define		CON_NFI_RST		BIT(1)
++#define		CON_SRD			BIT(4)	/* single read */
++#define		CON_BRD			BIT(8)  /* burst  read */
++#define		CON_BWR			BIT(9)	/* burst  write */
++#define		CON_SEC_SHIFT		(12)
++
++/* Timming control register */
++#define MTKSDG1_NFI_ACCCON		(0x0C)
++
++#define MTKSDG1_NFI_INTR_EN		(0x10)
++#define		INTR_RD_DONE_EN		BIT(0)
++#define		INTR_WR_DONE_EN		BIT(1)
++#define		INTR_RST_DONE_EN	BIT(2)
++#define		INTR_ERS_DONE_EN	BIT(3)
++#define		INTR_BUSY_RT_EN		BIT(4)
++#define		INTR_AHB_DONE_EN	BIT(6)
++
++#define MTKSDG1_NFI_INTR_STA		(0x14)
++
++#define MTKSDG1_NFI_CMD			(0x20)
++
++#define MTKSDG1_NFI_ADDRNOB		(0x30)
++#define		ADDR_ROW_NOB_SHIFT	(4)
++
++#define MTKSDG1_NFI_COLADDR		(0x34)
++#define MTKSDG1_NFI_ROWADDR		(0x38)
++#define MTKSDG1_NFI_STRDATA		(0x40)
++#define MTKSDG1_NFI_CNRNB		(0x44)
++#define MTKSDG1_NFI_DATAW		(0x50)
++#define MTKSDG1_NFI_DATAR		(0x54)
++#define MTKSDG1_NFI_PIO_DIRDY		(0x58)
++#define		PIO_DI_RDY		(0x01)
++
++/* NFI state*/
++#define MTKSDG1_NFI_STA			(0x60)
++#define		STA_CMD			BIT(0)
++#define		STA_ADDR		BIT(1)
++#define		STA_DATAR		BIT(2)
++#define		STA_DATAW		BIT(3)
++#define		STA_EMP_PAGE		BIT(12)
++
++#define MTKSDG1_NFI_FIFOSTA		(0x64)
++
++#define MTKSDG1_NFI_ADDRCNTR		(0x70)
++#define		CNTR_MASK		GENMASK(16, 12)
++
++#define MTKSDG1_NFI_STRADDR		(0x80)
++#define MTKSDG1_NFI_BYTELEN		(0x84)
++#define MTKSDG1_NFI_CSEL		(0x90)
++#define MTKSDG1_NFI_IOCON		(0x94)
++
++/* FDM data for sector: FDM0[L,H] - FDMF[L,H] */
++#define MTKSDG1_NFI_FDM_MAX_SEC		(0x10)
++#define MTKSDG1_NFI_FDM_REG_SIZE	(8)
++#define MTKSDG1_NFI_FDM0L		(0xA0)
++#define MTKSDG1_NFI_FDM0M		(0xA4)
++
++
++#define MTKSDG1_NFI_FIFODATA0		(0x190)
++#define MTKSDG1_NFI_DEBUG_CON1		(0x220)
++#define MTKSDG1_NFI_MASTER_STA		(0x224)
++#define		MASTER_STA_MASK		(0x0FFF)
++
++#define MTKSDG1_NFI_RANDOM_CNFG		(0x238)
++#define MTKSDG1_NFI_EMPTY_THRESH	(0x23C)
++#define MTKSDG1_NFI_NAND_TYPE		(0x240)
++#define MTKSDG1_NFI_ACCCON1		(0x244)
++#define MTKSDG1_NFI_DELAY_CTRL		(0x248)
++
++#endif
++
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0059-mtd-nand-add-an-mtd_to_nand-helper.patch b/target/linux/mediatek/patches-4.4/0059-mtd-nand-add-an-mtd_to_nand-helper.patch
deleted file mode 100644
index 636575ef7e..0000000000
--- a/target/linux/mediatek/patches-4.4/0059-mtd-nand-add-an-mtd_to_nand-helper.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 179937ef20beb9d4af4807f3540d4dfc4d48516a Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Mon, 16 Nov 2015 14:37:35 +0100
-Subject: [PATCH 59/66] mtd: nand: add an mtd_to_nand() helper
-
-Some drivers are retrieving the nand_chip pointer using the container_of
-macro on a struct wrapping both the nand_chip and the mtd_info struct while
-the standard way of retrieving this pointer is through mtd->priv.
-Provide an helper to do that.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h |    5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
-index 5a9d1d4..a4839b3 100644
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -719,6 +719,11 @@ struct nand_chip {
- 	void *priv;
- };
- 
-+static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
-+{
-+	return mtd->priv;
-+}
-+
- /*
-  * NAND Flash Manufacturer ID Codes
-  */
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch b/target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch
new file mode 100644
index 0000000000..392dea3ecc
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0059-mtd-nand-backport-fixes.patch
@@ -0,0 +1,46 @@
+From 96ec6b2ee8a19799835209d0a6753519b96277f8 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 02:28:08 +0200
+Subject: [PATCH 59/78] mtd: nand: backport fixes
+
+---
+ drivers/mtd/nand/mtksdg1_nand.c |    9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/mtd/nand/mtksdg1_nand.c b/drivers/mtd/nand/mtksdg1_nand.c
+index 55dd17d..f92b949 100644
+--- a/drivers/mtd/nand/mtksdg1_nand.c
++++ b/drivers/mtd/nand/mtksdg1_nand.c
+@@ -107,6 +107,9 @@ static struct nand_ecclayout nand_4k_128 = {
+ 	.oobfree = { {0, 32} },
+ };
+ 
++static const char * const part_probes[] = {
++	"cmdlinepart", "RedBoot", "ofpart", NULL };
++
+ /* NFI register access */
+ static inline void mtk_nfi_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
+ {
+@@ -1298,6 +1301,7 @@ static int mtk_nfc_probe(struct platform_device *pdev)
+ 
+ 	chip = &host->chip;
+ 	mtd = nand_to_mtd(chip);
++	mtd->priv = chip;
+ 	host->dev = dev;
+ 
+ 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+@@ -1428,7 +1432,10 @@ static int mtk_nfc_probe(struct platform_device *pdev)
+ 	}
+ 	host->switch_oob = false;
+ 
+-	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
++	ret = mtd_device_parse_register(mtd, part_probes,
++			&(struct mtd_part_parser_data) {
++				.of_node = pdev->dev.of_node,
++			}, NULL, 0);
+ 	if (ret) {
+ 		dev_err(dev, "mtd parse partition error\n");
+ 		goto nand_free;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0060-mtd-nand-add-nand_to_mtd-helper.patch b/target/linux/mediatek/patches-4.4/0060-mtd-nand-add-nand_to_mtd-helper.patch
deleted file mode 100644
index d68a2feaa5..0000000000
--- a/target/linux/mediatek/patches-4.4/0060-mtd-nand-add-nand_to_mtd-helper.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 8c32f64172fbf43d23c99dc4d32f5a1cb5eb08ae Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Tue, 1 Dec 2015 12:03:07 +0100
-Subject: [PATCH 60/66] mtd: nand: add nand_to_mtd() helper
-
-Add a new helper to retrieve the MTD device attached to a NAND chip.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h |    5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
-index a4839b3..c75424f 100644
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -724,6 +724,11 @@ static inline struct nand_chip *mtd_to_nand(struct mtd_info *mtd)
- 	return mtd->priv;
- }
- 
-+static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
-+{
-+	return &chip->mtd;
-+}
-+
- /*
-  * NAND Flash Manufacturer ID Codes
-  */
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0060-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch b/target/linux/mediatek/patches-4.4/0060-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
new file mode 100644
index 0000000000..a0e4f9ee5f
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0060-net-mediatek-checking-for-IS_ERR-instead-of-NULL.patch
@@ -0,0 +1,32 @@
+From c657573d75d71076fef8294f9d4f7f9a0e6f7a9e Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 15 Mar 2016 10:18:49 +0300
+Subject: [PATCH 60/78] net: mediatek: checking for IS_ERR() instead of NULL
+
+of_phy_connect() returns NULL on error, it never returns error pointers.
+
+Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index ba3afa5..9759fe5 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -186,9 +186,9 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ 
+ 	phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
+ 				mtk_phy_link_adjust, 0, phy_mode);
+-	if (IS_ERR(phydev)) {
++	if (!phydev) {
+ 		dev_err(eth->dev, "could not connect to PHY\n");
+-		return PTR_ERR(phydev);
++		return -ENODEV;
+ 	}
+ 
+ 	dev_info(eth->dev,
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0061-mtd-nand-add-helpers-to-access-priv.patch b/target/linux/mediatek/patches-4.4/0061-mtd-nand-add-helpers-to-access-priv.patch
deleted file mode 100644
index 49fbcbcc70..0000000000
--- a/target/linux/mediatek/patches-4.4/0061-mtd-nand-add-helpers-to-access-priv.patch
+++ /dev/null
@@ -1,39 +0,0 @@
-From 738a76df006dedd1feb87c596867438b7d59027a Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Thu, 10 Dec 2015 09:00:39 +0100
-Subject: [PATCH 61/66] mtd: nand: add helpers to access ->priv
-
-Add two helpers to access the field reserved for private controller data.
-This makes it clearer what this field is reserved for and ease future
-refactoring.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h |   10 ++++++++++
- 1 file changed, 10 insertions(+)
-
-diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
-index c75424f..345f864 100644
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -729,6 +729,16 @@ static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip)
- 	return &chip->mtd;
- }
- 
-+static inline void *nand_get_controller_data(struct nand_chip *chip)
-+{
-+	return chip->priv;
-+}
-+
-+static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
-+{
-+	chip->priv = priv;
-+}
-+
- /*
-  * NAND Flash Manufacturer ID Codes
-  */
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0061-net-mediatek-unlock-on-error-in-mtk_tx_map.patch b/target/linux/mediatek/patches-4.4/0061-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
new file mode 100644
index 0000000000..effb45fade
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0061-net-mediatek-unlock-on-error-in-mtk_tx_map.patch
@@ -0,0 +1,29 @@
+From d8261ad9e408adfa5d1758a0f655c7726f0f831b Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Tue, 15 Mar 2016 10:19:04 +0300
+Subject: [PATCH 61/78] net: mediatek: unlock on error in mtk_tx_map()
+
+There was a missing unlock on the error path.
+
+Fixes: 656e705243fd ('net-next: mediatek: add support for MT7623 ethernet')
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 9759fe5..c2c2e206 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -661,6 +661,8 @@ err_dma:
+ 		itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+ 	} while (itxd != txd);
+ 
++	spin_unlock_irqrestore(&eth->page_lock, flags);
++
+ 	return -ENOMEM;
+ }
+ 
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0062-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch b/target/linux/mediatek/patches-4.4/0062-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch
deleted file mode 100644
index 598f879f59..0000000000
--- a/target/linux/mediatek/patches-4.4/0062-mtd-nand-embed-an-mtd_info-structure-into-nand_chip.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 088bf341472e8da8595d49f15af9becf3a4b52e7 Mon Sep 17 00:00:00 2001
-From: Boris BREZILLON <boris.brezillon@free-electrons.com>
-Date: Tue, 1 Dec 2015 12:03:06 +0100
-Subject: [PATCH 62/66] mtd: nand: embed an mtd_info structure into nand_chip
-
-Currently all NAND controller drivers are providing both the mtd_info and
-nand_chip struct and then let the NAND subsystem to initialize a few
-things before registering the mtd instance to the MTD layer.
-Embed an mtd_info field into nand_chip to add some consistency to all NAND
-controller drivers.
-This change will also help factorizing boilerplate code copied in all NAND
-drivers.
-
-Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
----
- include/linux/mtd/nand.h |    2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
-index 345f864..1ded588 100644
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -540,6 +540,7 @@ struct nand_buffers {
- 
- /**
-  * struct nand_chip - NAND Private Flash Chip Data
-+ * @mtd:		MTD device registered to the MTD framework
-  * @IO_ADDR_R:		[BOARDSPECIFIC] address to read the 8 I/O lines of the
-  *			flash device
-  * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the
-@@ -640,6 +641,7 @@ struct nand_buffers {
-  */
- 
- struct nand_chip {
-+	struct mtd_info mtd;
- 	void __iomem *IO_ADDR_R;
- 	void __iomem *IO_ADDR_W;
- 
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0062-net-mediatek-use-dma_addr_t-correctly.patch b/target/linux/mediatek/patches-4.4/0062-net-mediatek-use-dma_addr_t-correctly.patch
new file mode 100644
index 0000000000..c44813068f
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0062-net-mediatek-use-dma_addr_t-correctly.patch
@@ -0,0 +1,35 @@
+From 26c749e825e87e8be46498be7ac425e83b0f22c6 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:10 +0100
+Subject: [PATCH 62/78] net: mediatek: use dma_addr_t correctly
+
+dma_alloc_coherent() expects a dma_addr_t pointer as its argument,
+not an 'unsigned int', and gcc correctly warns about broken
+code in the mtk_init_fq_dma function:
+
+drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_init_fq_dma':
+drivers/net/ethernet/mediatek/mtk_eth_soc.c:463:13: error: passing argument 3 of 'dma_alloc_coherent' from incompatible pointer type [-Werror=incompatible-pointer-types]
+
+This changes the type of the local variable to dma_addr_t.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index c2c2e206..a005bc4 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -453,7 +453,7 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
+ /* the qdma core needs scratch memory to be setup */
+ static int mtk_init_fq_dma(struct mtk_eth *eth)
+ {
+-	unsigned int phy_ring_head, phy_ring_tail;
++	dma_addr_t phy_ring_head, phy_ring_tail;
+ 	int cnt = MTK_DMA_SIZE;
+ 	dma_addr_t dma_addr;
+ 	int i;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0063-mtd-add-get-set-of_node-flash_node-helpers.patch b/target/linux/mediatek/patches-4.4/0063-mtd-add-get-set-of_node-flash_node-helpers.patch
deleted file mode 100644
index b72ecfdc8a..0000000000
--- a/target/linux/mediatek/patches-4.4/0063-mtd-add-get-set-of_node-flash_node-helpers.patch
+++ /dev/null
@@ -1,87 +0,0 @@
-From 63c8331b826ad5f21cb0175308099f18d5fe526a Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 22 Mar 2016 03:52:07 +0100
-Subject: [PATCH 63/66] mtd: add get/set of_node/flash_node helpers
-
-We are going to begin using the mtd->dev.of_node field for MTD device
-nodes, so let's add helpers for it. Also, we'll be making some
-conversions on spi_nor (and nand_chip eventually) too, so get that ready
-with their own helpers.
-
-Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
----
- include/linux/mtd/mtd.h     |   11 +++++++++++
- include/linux/mtd/nand.h    |   11 +++++++++++
- include/linux/mtd/spi-nor.h |   11 +++++++++++
- 3 files changed, 33 insertions(+)
-
-diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
-index f17fa75..cc84923 100644
---- a/include/linux/mtd/mtd.h
-+++ b/include/linux/mtd/mtd.h
-@@ -254,6 +254,17 @@ struct mtd_info {
- 	int usecount;
- };
- 
-+static inline void mtd_set_of_node(struct mtd_info *mtd,
-+				   struct device_node *np)
-+{
-+	mtd->dev.of_node = np;
-+}
-+
-+static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
-+{
-+	return mtd->dev.of_node;
-+}
-+
- int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
- int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
- 	      void **virt, resource_size_t *phys);
-diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
-index 1ded588..3c34ca4 100644
---- a/include/linux/mtd/nand.h
-+++ b/include/linux/mtd/nand.h
-@@ -741,6 +741,17 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
- 	chip->priv = priv;
- }
- 
-+static inline void nand_set_flash_node(struct nand_chip *chip,
-+				       struct device_node *np)
-+{
-+	chip->flash_node = np;
-+}
-+
-+static inline struct device_node *nand_get_flash_node(struct nand_chip *chip)
-+{
-+	return chip->flash_node;
-+}
-+
- /*
-  * NAND Flash Manufacturer ID Codes
-  */
-diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
-index c8723b6..6d991df 100644
---- a/include/linux/mtd/spi-nor.h
-+++ b/include/linux/mtd/spi-nor.h
-@@ -185,6 +185,17 @@ struct spi_nor {
- 	void *priv;
- };
- 
-+static inline void spi_nor_set_flash_node(struct spi_nor *nor,
-+					  struct device_node *np)
-+{
-+	nor->flash_node = np;
-+}
-+
-+static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
-+{
-+	return nor->flash_node;
-+}
-+
- /**
-  * spi_nor_scan() - scan the SPI NOR
-  * @nor:	the spi_nor structure
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0063-net-mediatek-remove-incorrect-dma_mask-assignment.patch b/target/linux/mediatek/patches-4.4/0063-net-mediatek-remove-incorrect-dma_mask-assignment.patch
new file mode 100644
index 0000000000..021f8b43da
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0063-net-mediatek-remove-incorrect-dma_mask-assignment.patch
@@ -0,0 +1,36 @@
+From 21f52c7a0ec569ae9dd72bfc619a7161e40a2e9d Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:11 +0100
+Subject: [PATCH 63/78] net: mediatek: remove incorrect dma_mask assignment
+
+Device drivers should not mess with the DMA mask directly,
+but instead call dma_set_mask() etc if needed.
+
+In case of the mtk_eth_soc driver, the mask already gets set
+correctly when the device is created, and setting it again
+is against the documented API.
+
+This removes the incorrect setting.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index a005bc4..fcd4ed7 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1678,9 +1678,6 @@ static int mtk_probe(struct platform_device *pdev)
+ 	struct mtk_eth *eth;
+ 	int err;
+ 
+-	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+-	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+-
+ 	device_reset(&pdev->dev);
+ 
+ 	match = of_match_device(of_mtk_match, &pdev->dev);
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0064-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch b/target/linux/mediatek/patches-4.4/0064-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
deleted file mode 100644
index 533ff56877..0000000000
--- a/target/linux/mediatek/patches-4.4/0064-mtd-mediatek-device-tree-docs-for-MTK-Smart-Device-G.patch
+++ /dev/null
@@ -1,64 +0,0 @@
-From 91f978e8a8f27eb9988d33904eaba55309b6c0b9 Mon Sep 17 00:00:00 2001
-From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
-Date: Wed, 2 Mar 2016 12:00:11 -0500
-Subject: [PATCH 64/66] mtd: mediatek: device tree docs for MTK Smart Device
- Gen1 NAND
-
-This patch adds documentation support for Smart Device Gen1 type of
-NAND controllers.
-
-Mediatek's SoC 2701 is one of the SoCs that implements this controller.
-
-Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
----
- .../devicetree/bindings/mtd/mtksdg1-nand.txt       |   38 ++++++++++++++++++++
- 1 file changed, 38 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
-
-diff --git a/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt b/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
-new file mode 100644
-index 0000000..129d17b
---- /dev/null
-+++ b/Documentation/devicetree/bindings/mtd/mtksdg1-nand.txt
-@@ -0,0 +1,38 @@
-+MTK Smart Device SoCs NAND controller DT binding
-+
-+Required properties:
-+- compatible:          Should be "mediatek,mt2701-nfc".
-+- reg:                 The first contains base physical address and size of
-+                       NAND controller's registers. The second contains base
-+                       physical address and size of NAND ECC engine.
-+- interrupts:          the NFC NFI interrupt, and the NFC ECC interrupt
-+- clocks:              NAND controller clocks.
-+- clock-names:         NAND controller clocks internal name.
-+- vmch-supply:         NAND power supply.
-+- #address-cells:      Partition address, should be set 1.
-+- #size-cells:         Partition size, should be set 1.
-+
-+Optional properties:
-+
-+nand-on-flash-bbt:  Use a flash based bad block table.
-+
-+Optional subnodes:
-+- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
-+
-+Example:
-+
-+	nand: nand@1100d000 {
-+		compatible = "mediatek,mt2701-nfc";
-+		reg = <0 0x1100d000 0 0x1000>, <0 0x1100e000 0 0x1000>;
-+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>,
-+			<GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
-+		clocks = <&pericfg CLK_PERI_NFI>, <&pericfg CLK_PERI_NFI_ECC>,
-+			<&pericfg CLK_PERI_NFI_PAD>;
-+		clock-names = "nfi_ck", "nfi_ecc_ck", "nfi_pad_ck";
-+		vmch-supply = <&mt6323_vmch_reg>;
-+		status = "disabled";
-+		#address-cells = <1>;
-+		#size-cells = <1>;
-+
-+		...
-+	};
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0064-net-mediatek-check-device_reset-return-code.patch b/target/linux/mediatek/patches-4.4/0064-net-mediatek-check-device_reset-return-code.patch
new file mode 100644
index 0000000000..06aab2b205
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0064-net-mediatek-check-device_reset-return-code.patch
@@ -0,0 +1,38 @@
+From e1e7d841480b3a0febdb999fd1c6c4213ee18ea7 Mon Sep 17 00:00:00 2001
+From: Arnd Bergmann <arnd@arndb.de>
+Date: Mon, 14 Mar 2016 15:07:12 +0100
+Subject: [PATCH 64/78] net: mediatek: check device_reset return code
+
+The device_reset() function may fail, so we have to check
+its return value, e.g. to make deferred probing work correctly.
+gcc warns about it because of the warn_unused_result attribute:
+
+drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_probe':
+drivers/net/ethernet/mediatek/mtk_eth_soc.c:1679:2: error: ignoring return value of 'device_reset', declared with attribute warn_unused_result [-Werror=unused-result]
+
+This adds the trivial error check to propagate the return value
+to the generic platform device probe code.
+
+Signed-off-by: Arnd Bergmann <arnd@arndb.de>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index fcd4ed7..7f2126b 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1678,7 +1678,9 @@ static int mtk_probe(struct platform_device *pdev)
+ 	struct mtk_eth *eth;
+ 	int err;
+ 
+-	device_reset(&pdev->dev);
++	err = device_reset(&pdev->dev);
++	if (err)
++		return err;
+ 
+ 	match = of_match_device(of_mtk_match, &pdev->dev);
+ 	soc = (struct mtk_soc_data *)match->data;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0065-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch b/target/linux/mediatek/patches-4.4/0065-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
deleted file mode 100644
index c21ca1db93..0000000000
--- a/target/linux/mediatek/patches-4.4/0065-mtd-mediatek-driver-for-MTK-Smart-Device-Gen1-NAND.patch
+++ /dev/null
@@ -1,1798 +0,0 @@
-From 7a9d3c8c4084fd37fa14c0e8db2830623f5da8cc Mon Sep 17 00:00:00 2001
-From: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
-Date: Wed, 2 Mar 2016 12:00:12 -0500
-Subject: [PATCH 65/66] mtd: mediatek: driver for MTK Smart Device Gen1 NAND
-
-This patch adds support for mediatek's SDG1 NFC nand controller
-embedded in SoC 2701.
-
-UBIFS support has been successfully tested.
-
-Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
----
- drivers/mtd/nand/Kconfig            |    6 +
- drivers/mtd/nand/Makefile           |    1 +
- drivers/mtd/nand/mtksdg1_nand.c     | 1535 +++++++++++++++++++++++++++++++++++
- drivers/mtd/nand/mtksdg1_nand_ecc.h |   75 ++
- drivers/mtd/nand/mtksdg1_nand_nfi.h |  119 +++
- 5 files changed, 1736 insertions(+)
- create mode 100644 drivers/mtd/nand/mtksdg1_nand.c
- create mode 100644 drivers/mtd/nand/mtksdg1_nand_ecc.h
- create mode 100644 drivers/mtd/nand/mtksdg1_nand_nfi.h
-
-diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
-index 2896640..5ec072a 100644
---- a/drivers/mtd/nand/Kconfig
-+++ b/drivers/mtd/nand/Kconfig
-@@ -546,4 +546,10 @@ config MTD_NAND_HISI504
- 	help
- 	  Enables support for NAND controller on Hisilicon SoC Hip04.
- 
-+config MTD_NAND_MTKSDG1
-+	tristate "Support for NAND controller on MTK Smart Device SoCs"
-+	depends on HAS_DMA
-+	help
-+	Enables support for NAND controller on MTK Smart Device SoCs.
-+
- endif # MTD_NAND
-diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
-index 2c7f014..2a2620c 100644
---- a/drivers/mtd/nand/Makefile
-+++ b/drivers/mtd/nand/Makefile
-@@ -55,5 +55,6 @@ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
- obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
- obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
- obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
-+obj-$(CONFIG_MTD_NAND_MTKSDG1)		+= mtksdg1_nand.o
- 
- nand-objs := nand_base.o nand_bbt.o nand_timings.o
-diff --git a/drivers/mtd/nand/mtksdg1_nand.c b/drivers/mtd/nand/mtksdg1_nand.c
-new file mode 100644
-index 0000000..55dd17d
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand.c
-@@ -0,0 +1,1535 @@
-+/*
-+ * MTK smart device NAND Flash controller driver.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Authors:	Xiaolei Li		<xiaolei.li@mediatek.com>
-+ *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz@linaro.org>
-+ *
-+ * 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 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 the
-+ * GNU General Public License for more details.
-+ */
-+
-+#include <linux/platform_device.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/interrupt.h>
-+#include <linux/of_mtd.h>
-+#include <linux/delay.h>
-+#include <linux/clk.h>
-+#include <linux/mtd/partitions.h>
-+#include <linux/mtd/nand.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/module.h>
-+
-+#include "mtksdg1_nand_nfi.h"
-+#include "mtksdg1_nand_ecc.h"
-+
-+#define MTK_IRQ_ECC		"mtksdg1-nand-ecc"
-+#define MTK_IRQ_NFI		"mtksdg1-nand-nfi"
-+#define MTK_NAME		"mtksdg1-nand"
-+
-+#define KB(x)			((x) * 1024UL)
-+#define MB(x)			(KB(x) * 1024UL)
-+
-+#define SECTOR_SHIFT		(10)
-+#define SECTOR_SIZE		(1UL << SECTOR_SHIFT)
-+#define BYTES_TO_SECTORS(x)	((x) >> SECTOR_SHIFT)
-+#define SECTORS_TO_BYTES(x)	((x) << SECTOR_SHIFT)
-+
-+#define MTK_TIMEOUT		(500)
-+#define MTK_RESET_TIMEOUT	(1 * HZ)
-+
-+#define MTK_ECC_PARITY_BITS	(14)
-+#define MTK_NAND_MAX_CHIP	(2)
-+
-+#define MTK_OOB_ON		(1)
-+#define MTK_OOB_OFF		(0)
-+
-+/* raw accesses do not use ECC (ecc = !raw) */
-+#define MTK_ECC_OFF		(1)
-+#define MTK_ECC_ON		(0)
-+
-+struct mtk_nfc_clk {
-+	struct clk *nfiecc_clk;
-+	struct clk *nfi_clk;
-+	struct clk *pad_clk;
-+};
-+
-+struct mtk_nfc_saved_reg {
-+	struct {
-+		u32 enccnfg;
-+		u32 deccnfg;
-+	} ecc;
-+	struct {
-+		u32 emp_thresh;
-+		u16 pagefmt;
-+		u32 acccon;
-+		u16 cnrnb;
-+		u16 csel;
-+	} nfi;
-+};
-+
-+struct mtk_nfc_host {
-+	struct mtk_nfc_clk clk;
-+	struct nand_chip chip;
-+	struct device *dev;
-+
-+	struct {
-+		struct completion complete;
-+		void __iomem *base;
-+	} nfi;
-+
-+	struct {
-+		struct completion complete;
-+		void __iomem *base;
-+		u32 dec_sec;
-+	} ecc;
-+
-+	u32 fdm_reg[MTKSDG1_NFI_FDM_REG_SIZE / sizeof(u32)];
-+	bool switch_oob;
-+	u32 row_nob;
-+	u8 *buffer;
-+
-+#ifdef CONFIG_PM_SLEEP
-+	struct mtk_nfc_saved_reg saved_reg;
-+#endif
-+};
-+
-+static struct nand_ecclayout nand_2k_64 = {
-+	.oobfree = { {0, 16} },
-+};
-+
-+static struct nand_ecclayout nand_4k_128 = {
-+	.oobfree = { {0, 32} },
-+};
-+
-+/* NFI register access */
-+static inline void mtk_nfi_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
-+{
-+	writel(val, host->nfi.base + reg);
-+}
-+static inline void mtk_nfi_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
-+{
-+	writew(val, host->nfi.base + reg);
-+}
-+static inline u32 mtk_nfi_readl(struct mtk_nfc_host *host, u32 reg)
-+{
-+	return readl_relaxed(host->nfi.base + reg);
-+}
-+static inline u16 mtk_nfi_readw(struct mtk_nfc_host *host, u32 reg)
-+{
-+	return readw_relaxed(host->nfi.base + reg);
-+}
-+static inline u8 mtk_nfi_readb(struct mtk_nfc_host *host, u32 reg)
-+{
-+	return readb_relaxed(host->nfi.base + reg);
-+}
-+
-+/* ECC register access */
-+static inline void mtk_ecc_writel(struct mtk_nfc_host *host, u32 val, u32 reg)
-+{
-+	writel(val, host->ecc.base + reg);
-+}
-+static inline void mtk_ecc_writew(struct mtk_nfc_host *host, u16 val, u32 reg)
-+{
-+	writew(val, host->ecc.base + reg);
-+}
-+static inline u32 mtk_ecc_readl(struct mtk_nfc_host *host, u32 reg)
-+{
-+	return readl_relaxed(host->ecc.base + reg);
-+}
-+static inline u16 mtk_ecc_readw(struct mtk_nfc_host *host, u32 reg)
-+{
-+	return readw_relaxed(host->ecc.base + reg);
-+}
-+
-+static void mtk_nfc_hw_reset(struct mtk_nfc_host *host)
-+{
-+	unsigned long timeout = MTK_RESET_TIMEOUT;
-+	struct device *dev = host->dev;
-+	u32 val;
-+
-+	/* reset the state machine, data fifo and fdm data */
-+	mtk_nfi_writel(host, CON_FIFO_FLUSH | CON_NFI_RST, MTKSDG1_NFI_CON);
-+	timeout += jiffies;
-+	do {
-+		val = mtk_nfi_readl(host, MTKSDG1_NFI_MASTER_STA);
-+		val &= MASTER_STA_MASK;
-+		if (!val)
-+			return;
-+		usleep_range(50, 100);
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	dev_warn(dev, "nfi master active after in reset [0x%x] = 0x%x\n",
-+		MTKSDG1_NFI_MASTER_STA, val);
-+};
-+
-+static int mtk_nfc_set_command(struct mtk_nfc_host *host, u8 command)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct device *dev = host->dev;
-+	u32 val;
-+
-+	mtk_nfi_writel(host, command, MTKSDG1_NFI_CMD);
-+
-+	/* wait for the NFI core to enter command mode */
-+	timeout += jiffies;
-+	do {
-+		val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
-+		val &= STA_CMD;
-+		if (!val)
-+			return 0;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+	dev_warn(dev, "nfi core timed out entering command mode\n");
-+
-+	return -EIO;
-+}
-+
-+static int mtk_nfc_set_address(struct mtk_nfc_host *host, u32 column, u32 row,
-+		u8 colnob, u8 row_nob)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct device *dev = host->dev;
-+	u32 addr_nob, val;
-+
-+	addr_nob = colnob | (row_nob << ADDR_ROW_NOB_SHIFT);
-+	mtk_nfi_writel(host, column, MTKSDG1_NFI_COLADDR);
-+	mtk_nfi_writel(host, row, MTKSDG1_NFI_ROWADDR);
-+	mtk_nfi_writel(host, addr_nob, MTKSDG1_NFI_ADDRNOB);
-+
-+	/* wait for the NFI core to enter address mode */
-+	timeout += jiffies;
-+	do {
-+		val = mtk_nfi_readl(host, MTKSDG1_NFI_STA);
-+		val &= STA_ADDR;
-+		if (!val)
-+			return 0;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	dev_warn(dev, "nfi core timed out entering address mode\n");
-+
-+	return -EIO;
-+}
-+
-+static inline void mtk_ecc_encoder_idle(struct mtk_nfc_host *host)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct device *dev = host->dev;
-+	u32 val;
-+
-+	timeout += jiffies;
-+	do {
-+		val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIDLE);
-+		val &= ENC_IDLE;
-+		if (val)
-+			return;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	dev_warn(dev, "hw init ecc encoder not idle\n");
-+}
-+
-+static inline void mtk_ecc_decoder_idle(struct mtk_nfc_host *host)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct device *dev = host->dev;
-+	u32 val;
-+
-+	timeout += jiffies;
-+	do {
-+		val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIDLE);
-+		val &= DEC_IDLE;
-+		if (val)
-+			return;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	dev_warn(dev, "hw init ecc decoder not idle\n");
-+}
-+
-+static int mtk_nfc_transfer_done(struct mtk_nfc_host *host, u32 sectors)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	u32 cnt;
-+
-+	/* wait for the sector count */
-+	timeout += jiffies;
-+	do {
-+		cnt = mtk_nfi_readl(host, MTKSDG1_NFI_ADDRCNTR);
-+		cnt &= CNTR_MASK;
-+		if (cnt >= sectors)
-+			return 0;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	return  -EIO;
-+}
-+
-+static int mtk_nfc_subpage_done(struct mtk_nfc_host *host, int sectors)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	u32 val;
-+
-+	timeout += jiffies;
-+	do {
-+		val = mtk_nfi_readl(host, MTKSDG1_NFI_BYTELEN);
-+		val &= CNTR_MASK;
-+		if (val >= sectors)
-+			return 0;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	return -EIO;
-+}
-+
-+static inline int mtk_nfc_data_ready(struct mtk_nfc_host *host)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	u8 val;
-+
-+	timeout += jiffies;
-+	do {
-+		val = mtk_nfi_readw(host, MTKSDG1_NFI_PIO_DIRDY);
-+		val &= PIO_DI_RDY;
-+		if (val)
-+			return 0;
-+		cpu_relax();
-+
-+	} while (time_before(jiffies, timeout));
-+
-+	/* data _MUST_ not be accessed */
-+	return -EIO;
-+}
-+
-+static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
-+{
-+	struct nand_chip *chip = mtd_to_nand(mtd);
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	struct device *dev = host->dev;
-+	u32 dec_size, enc_size;
-+	u32 ecc_bit, ecc_level;
-+	u32 spare, fmt;
-+	u32 reg;
-+
-+	host->row_nob = 1;
-+	if (chip->chipsize > MB(32))
-+		host->row_nob = chip->chipsize > MB(128) ? 3 : 2;
-+
-+	spare = mtd->oobsize / BYTES_TO_SECTORS(mtd->writesize);
-+	switch (spare) {
-+	case 16:
-+		ecc_bit = ECC_CNFG_4BIT;
-+		ecc_level = 4;
-+		break;
-+	case 32:
-+		ecc_bit = ECC_CNFG_12BIT;
-+		ecc_level = 12;
-+		break;
-+	default:
-+		dev_err(dev, "invalid spare size per sector: %d\n", spare);
-+		return -EINVAL;
-+	}
-+
-+	chip->ecc.strength = ecc_level;
-+	chip->ecc.size = SECTOR_SIZE;
-+
-+	switch (mtd->writesize) {
-+	case KB(2):
-+		fmt = PAGEFMT_512_2K;
-+		chip->ecc.layout = &nand_2k_64;
-+		break;
-+	case KB(4):
-+		fmt = PAGEFMT_2K_4K;
-+		chip->ecc.layout = &nand_4k_128;
-+		break;
-+	case KB(8):
-+		fmt = PAGEFMT_4K_8K;
-+		break;
-+	default:
-+		dev_err(dev, "invalid page size: %d\n", mtd->writesize);
-+		return -EINVAL;
-+	}
-+
-+	/* configure PAGE FMT */
-+	reg = fmt;
-+	reg |= PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT;
-+	reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_SHIFT;
-+	reg |= MTKSDG1_NFI_FDM_REG_SIZE << PAGEFMT_FDM_ECC_SHIFT;
-+	mtk_nfi_writew(host, reg, MTKSDG1_NFI_PAGEFMT);
-+
-+	/* configure ECC encoder (in bits) */
-+	enc_size = (SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE) << 3;
-+	reg = ecc_bit | ECC_NFI_MODE | (enc_size << ECC_MS_SHIFT);
-+	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+	/* configure ECC decoder (inbits) */
-+	dec_size = enc_size + ecc_level * MTK_ECC_PARITY_BITS;
-+	reg = ecc_bit | ECC_NFI_MODE | (dec_size << ECC_MS_SHIFT);
-+	reg |= (DEC_CNFG_CORRECT | DEC_EMPTY_EN);
-+	mtk_ecc_writel(host, reg, MTKSDG1_ECC_DECCNFG);
-+
-+	return 0;
-+}
-+
-+static void mtk_nfc_device_reset(struct mtk_nfc_host *host)
-+{
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct device *dev = host->dev;
-+	u16 chip;
-+	int rc;
-+
-+	mtk_nfc_hw_reset(host);
-+
-+	/* enable reset done interrupt */
-+	mtk_nfi_writew(host, INTR_RST_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+
-+	/* configure FSM for reset operation */
-+	mtk_nfi_writew(host, CNFG_OP_RESET, MTKSDG1_NFI_CNFG);
-+
-+	init_completion(&host->nfi.complete);
-+
-+	mtk_nfc_set_command(host, NAND_CMD_RESET);
-+	rc = wait_for_completion_timeout(&host->nfi.complete, timeout);
-+	if (!rc) {
-+		chip = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
-+		dev_err(dev, "device(%d) reset timeout\n", chip);
-+	}
-+}
-+
-+static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
-+{
-+	struct nand_chip *nand = mtd_to_nand(mtd);
-+	struct mtk_nfc_host *host = nand_get_controller_data(nand);
-+
-+	if (chip < 0)
-+		return;
-+
-+	mtk_nfi_writel(host, chip, MTKSDG1_NFI_CSEL);
-+}
-+
-+static inline bool mtk_nfc_cmd_supported(unsigned command)
-+{
-+	switch (command) {
-+	case NAND_CMD_RESET:
-+	case NAND_CMD_READID:
-+	case NAND_CMD_STATUS:
-+	case NAND_CMD_READOOB:
-+	case NAND_CMD_ERASE1:
-+	case NAND_CMD_ERASE2:
-+	case NAND_CMD_SEQIN:
-+	case NAND_CMD_PAGEPROG:
-+	case NAND_CMD_CACHEDPROG:
-+	case NAND_CMD_READ0:
-+		return true;
-+	default:
-+		return false;
-+	}
-+}
-+
-+static void mtk_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
-+		int page_addr)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(mtd_to_nand(mtd));
-+	unsigned long const cmd_timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	struct completion *p = &host->nfi.complete;
-+	u32 val;
-+	int rc;
-+
-+	if (mtk_nfc_cmd_supported(command))
-+		mtk_nfc_hw_reset(host);
-+
-+	switch (command) {
-+	case NAND_CMD_RESET:
-+		mtk_nfc_device_reset(host);
-+		break;
-+	case NAND_CMD_READID:
-+		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
-+		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_READID);
-+		mtk_nfc_set_address(host, column, 0, 1, 0);
-+		mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
-+		break;
-+	case NAND_CMD_STATUS:
-+		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_SRD;
-+		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_STATUS);
-+		mtk_nfi_writel(host, CON_SRD, MTKSDG1_NFI_CON);
-+		break;
-+	case NAND_CMD_READOOB:
-+		val = CNFG_READ_EN | CNFG_BYTE_RW | CNFG_OP_READ;
-+		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_READ0);
-+		column += mtd->writesize;
-+		mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
-+		val = CON_BRD | (1 << CON_SEC_SHIFT);
-+		mtk_nfi_writel(host, val, MTKSDG1_NFI_CON);
-+		break;
-+	case NAND_CMD_ERASE1:
-+		mtk_nfi_writew(host, INTR_ERS_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+		mtk_nfi_writew(host, CNFG_OP_ERASE, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_ERASE1);
-+		mtk_nfc_set_address(host, 0, page_addr, 0, host->row_nob);
-+		break;
-+	case NAND_CMD_ERASE2:
-+		init_completion(p);
-+		mtk_nfc_set_command(host, NAND_CMD_ERASE2);
-+		rc = wait_for_completion_timeout(p, cmd_timeout);
-+		if (!rc)
-+			dev_err(host->dev, "erase command timeout\n");
-+		break;
-+	case NAND_CMD_SEQIN:
-+		mtk_nfi_writew(host, CNFG_OP_PRGM, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_SEQIN);
-+		mtk_nfc_set_address(host, column, page_addr, 2, host->row_nob);
-+		break;
-+	case NAND_CMD_PAGEPROG:
-+	case NAND_CMD_CACHEDPROG:
-+		mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
-+		init_completion(p);
-+		mtk_nfc_set_command(host, command);
-+		rc = wait_for_completion_timeout(p, cmd_timeout);
-+		if (!rc)
-+			dev_err(host->dev, "pageprogr command timeout\n");
-+		break;
-+	case NAND_CMD_READ0:
-+		val = CNFG_OP_READ | CNFG_READ_EN;
-+		mtk_nfi_writew(host, val, MTKSDG1_NFI_CNFG);
-+		mtk_nfc_set_command(host, NAND_CMD_READ0);
-+		break;
-+	default:
-+		dev_warn(host->dev, "command 0x%x not supported\n", command);
-+		break;
-+	}
-+}
-+
-+static uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
-+{
-+	struct nand_chip *chip = mtd_to_nand(mtd);
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	int rc;
-+
-+	rc = mtk_nfc_data_ready(host);
-+	if (rc < 0) {
-+		dev_err(host->dev, "data not ready\n");
-+		return NAND_STATUS_FAIL;
-+	}
-+
-+	return mtk_nfi_readb(host, MTKSDG1_NFI_DATAR);
-+}
-+
-+static void mtk_nfc_write_fdm(struct nand_chip *chip, u32 sectors)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	u8 *src, *dst;
-+	int i, j, reg;
-+
-+	for (i = 0; i < sectors ; i++) {
-+		/* read FDM from OOB into private area */
-+		src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+		dst = (u8 *)host->fdm_reg;
-+		memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+
-+		/* write FDM to registers */
-+		for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
-+			reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+			reg += j * sizeof(host->fdm_reg[0]);
-+			mtk_nfi_writel(host, host->fdm_reg[j], reg);
-+		}
-+	}
-+}
-+
-+static int mtk_nfc_write_page(struct mtd_info *mtd,
-+			struct nand_chip *chip, const uint8_t *buf,
-+			int oob_on, int page, int raw)
-+{
-+
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	struct completion *nfi = &host->nfi.complete;
-+	struct device *dev = host->dev;
-+	const bool use_ecc = !raw;
-+	void *q = (void *) buf;
-+	dma_addr_t dma_addr;
-+	size_t dmasize;
-+	u32 reg;
-+	int ret;
-+
-+	dmasize = mtd->writesize + (raw ? mtd->oobsize : 0);
-+
-+	dma_addr = dma_map_single(dev, q, dmasize, DMA_TO_DEVICE);
-+	if (dma_mapping_error(host->dev, dma_addr)) {
-+		dev_err(host->dev, "dma mapping error\n");
-+		return -EINVAL;
-+	}
-+
-+	reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
-+	reg |= CNFG_AHB | CNFG_DMA_BURST_EN;
-+	if (use_ecc) {
-+		/**
-+		 * OOB will be generated
-+		 *  - FDM: from register
-+		 *  - ECC: from HW
-+		 */
-+		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+		mtk_ecc_encoder_idle(host);
-+		mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
-+
-+		/* write OOB into the FDM registers (OOB area in MTK NAND) */
-+		if (oob_on)
-+			mtk_nfc_write_fdm(chip, chip->ecc.steps);
-+	} else {
-+		/* OOB is part of the DMA transfer */
-+		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+	}
-+
-+	mtk_nfi_writel(host, chip->ecc.steps << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
-+	mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
-+	mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+
-+	init_completion(nfi);
-+
-+	/* start DMA */
-+	reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BWR;
-+	mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
-+
-+	ret = wait_for_completion_timeout(nfi, msecs_to_jiffies(MTK_TIMEOUT));
-+	if (!ret) {
-+		dev_err(dev, "program ahb done timeout\n");
-+		mtk_nfi_writew(host, 0, MTKSDG1_NFI_INTR_EN);
-+		ret = -ETIMEDOUT;
-+		goto timeout;
-+	}
-+
-+	ret = mtk_nfc_transfer_done(host, chip->ecc.steps);
-+	if (ret < 0)
-+		dev_err(dev, "hwecc write timeout\n");
-+timeout:
-+	dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
-+
-+	if (use_ecc) {
-+		mtk_ecc_encoder_idle(host);
-+		mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
-+	}
-+
-+	mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
-+
-+	return ret;
-+}
-+
-+static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
-+			struct nand_chip *chip, const uint8_t *buf,
-+			int oob_on, int page)
-+{
-+	return mtk_nfc_write_page(mtd, chip, buf, oob_on, page, MTK_ECC_ON);
-+}
-+
-+static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+					const uint8_t *buf, int oob_on, int pg)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	uint8_t *src, *dst;
-+	size_t len;
-+	u32 i;
-+
-+	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
-+
-+	/* MTK internal 4KB page data layout:
-+	 * ----------------------------------
-+	 * PAGE = 4KB, SECTOR = 1KB, OOB=128B
-+	 * page = sector_oob1 + sector_oob2 + sector_oob3 + sector_oob4
-+	 * sector_oob = data (1KB) + FDM (8B) + ECC parity (21B) + free (3B)
-+	 *
-+	 */
-+	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+	for (i = 0; i < chip->ecc.steps; i++) {
-+
-+		if (buf) {
-+			src = (uint8_t *) buf + i * SECTOR_SIZE;
-+			dst = host->buffer + i * len;
-+			memcpy(dst, src, SECTOR_SIZE);
-+		}
-+
-+		if (oob_on) {
-+			src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+			dst = host->buffer + i * len + SECTOR_SIZE;
-+			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+		}
-+	}
-+
-+	return mtk_nfc_write_page(mtd, chip, host->buffer, MTK_OOB_OFF, pg,
-+				MTK_ECC_OFF);
-+}
-+
-+static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	struct completion *ecc = &host->ecc.complete;
-+	u32 reg, parity_bytes, i;
-+	dma_addr_t dma_addr;
-+	u32 *parity_region;
-+	int rc, ret = 0;
-+	size_t dmasize;
-+
-+	dmasize = SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE;
-+	dma_addr = dma_map_single(host->dev, data, dmasize, DMA_TO_DEVICE);
-+	if (dma_mapping_error(host->dev, dma_addr)) {
-+		dev_err(host->dev, "dma mapping error\n");
-+		return -EINVAL;
-+	}
-+
-+	/* enable the encoder in DMA mode to calculate the ECC bytes  */
-+	reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+	reg &= (~ECC_ENC_MODE_MASK);
-+	reg |= ECC_DMA_MODE;
-+	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+	mtk_ecc_writel(host, ENC_IRQEN, MTKSDG1_ECC_ENCIRQ_EN);
-+	mtk_ecc_writel(host, lower_32_bits(dma_addr), MTKSDG1_ECC_ENCDIADDR);
-+
-+	init_completion(ecc);
-+	mtk_ecc_writew(host, ENC_EN, MTKSDG1_ECC_ENCCON);
-+
-+	rc = wait_for_completion_timeout(ecc, msecs_to_jiffies(MTK_TIMEOUT));
-+	if (!rc) {
-+		dev_err(host->dev, "ecc encode done timeout\n");
-+		mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
-+		ret = -ETIMEDOUT;
-+		goto timeout;
-+	}
-+
-+	mtk_ecc_encoder_idle(host);
-+
-+	/**
-+	 * Program ECC bytes to OOB
-+	 *	per sector oob = FDM + ECC + SPARE
-+	 */
-+
-+	parity_region = (u32 *) (data + SECTOR_SIZE + MTKSDG1_NFI_FDM_REG_SIZE);
-+	parity_bytes = (chip->ecc.strength * MTK_ECC_PARITY_BITS + 7) >> 3;
-+
-+	/* write the parity bytes generated by the ECC back to the OOB region */
-+	for (i = 0; i < parity_bytes; i += sizeof(u32))
-+		*parity_region++ = mtk_ecc_readl(host, MTKSDG1_ECC_ENCPAR0 + i);
-+
-+timeout:
-+
-+	dma_unmap_single(host->dev, dma_addr, dmasize, DMA_TO_DEVICE);
-+
-+	mtk_ecc_writew(host, 0, MTKSDG1_ECC_ENCCON);
-+	reg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+	reg &= (~ECC_ENC_MODE_MASK);
-+	reg |= ECC_NFI_MODE;
-+	mtk_ecc_writel(host, reg, MTKSDG1_ECC_ENCCNFG);
-+
-+	return ret;
-+}
-+
-+static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
-+		struct nand_chip *chip, uint32_t offset, uint32_t data_len,
-+		const uint8_t *buf, int oob_on, int pg)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	uint8_t *src, *dst;
-+	u32 start, end;
-+	size_t len;
-+	int i, ret;
-+
-+	start = BYTES_TO_SECTORS(offset);
-+	end = BYTES_TO_SECTORS(offset + data_len + SECTOR_SIZE - 1);
-+
-+	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
-+	for (i = 0; i < chip->ecc.steps; i++) {
-+
-+		/* write data */
-+		src = (uint8_t *) buf + i * SECTOR_SIZE;
-+		dst = host->buffer + i * len;
-+		memcpy(dst, src, SECTOR_SIZE);
-+
-+		if (i < start)
-+			continue;
-+
-+		if (i >= end)
-+			continue;
-+
-+		/* write fdm */
-+		if (oob_on) {
-+			src = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+			dst = host->buffer + i * len + SECTOR_SIZE;
-+			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+		}
-+
-+		/* point to the start of data */
-+		src = host->buffer + i * len;
-+
-+		/* program the CRC back to the OOB */
-+		ret = mtk_nfc_sector_encode(chip, src);
-+		if (ret < 0)
-+			return ret;
-+	}
-+
-+	/* use the data in the private buffer (now with FDM and CRC) to perform
-+	 * a raw write
-+	 */
-+	src = host->buffer;
-+	return mtk_nfc_write_page(mtd, chip, src, MTK_OOB_OFF, pg, MTK_ECC_OFF);
-+}
-+
-+static int mtk_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+				int page)
-+{
-+	u8 *buf = chip->buffers->databuf;
-+	int ret;
-+
-+	memset(buf, 0xff, mtd->writesize);
-+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-+	ret = mtk_nfc_write_page_hwecc(mtd, chip, buf, MTK_OOB_ON, page);
-+	if (ret < 0)
-+		return -EIO;
-+
-+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+	ret = chip->waitfunc(mtd, chip);
-+
-+	return ret & NAND_STATUS_FAIL ? -EIO : 0;
-+}
-+
-+static int mtk_nfc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+					int page)
-+{
-+	int ret;
-+
-+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-+	ret = mtk_nfc_write_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
-+	if (ret < 0)
-+		return -EIO;
-+
-+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+	ret = chip->waitfunc(mtd, chip);
-+
-+	return ret & NAND_STATUS_FAIL ? -EIO : 0;
-+}
-+
-+static int mtk_nfc_ecc_check(struct mtd_info *mtd, struct nand_chip *chip,
-+				u32 sectors)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	u32 offset, i, err, max_bitflip;
-+
-+	max_bitflip = 0;
-+
-+	for (i = 0; i < sectors; i++) {
-+		offset = (i >> 2) << 2;
-+		err = mtk_ecc_readl(host, MTKSDG1_ECC_DECENUM0 + offset);
-+		err = err >> ((i % 4) * 8);
-+		err &= ERR_MASK;
-+		if (err == ERR_MASK) {
-+			/* uncorrectable errors */
-+			mtd->ecc_stats.failed++;
-+			continue;
-+		}
-+
-+		mtd->ecc_stats.corrected += err;
-+		max_bitflip = max_t(u32, max_bitflip, err);
-+	}
-+
-+	return max_bitflip;
-+}
-+
-+static void mtk_nfc_read_fdm(struct nand_chip *chip, u32 sectors)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	int i, j, reg;
-+	u8 *dst, *src;
-+
-+	for (i = 0; i < sectors; i++) {
-+		/* read FDM register into host memory */
-+		for (j = 0; j < ARRAY_SIZE(host->fdm_reg); j++) {
-+			reg = MTKSDG1_NFI_FDM0L + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+			reg += j * sizeof(host->fdm_reg[0]);
-+			host->fdm_reg[j] = mtk_nfi_readl(host, reg);
-+		}
-+
-+		/* copy FDM register from host to OOB */
-+		src = (u8 *)host->fdm_reg;
-+		dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+		memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+	}
-+}
-+
-+static int mtk_nfc_update_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+				u8 *buf, u32 sectors)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	int i, bitflips = 0;
-+
-+	/* if the page is empty, no bitflips and clear data and oob */
-+	if (mtk_nfi_readl(host, MTKSDG1_NFI_STA) & STA_EMP_PAGE) {
-+		memset(buf, 0xff, SECTORS_TO_BYTES(sectors));
-+
-+		/* empty page: update OOB with 0xFF */
-+		for (i = 0; i < sectors; i++) {
-+			memset(chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE,
-+				0xff, MTKSDG1_NFI_FDM_REG_SIZE);
-+		}
-+	} else {
-+		/* update OOB with HW info */
-+		mtk_nfc_read_fdm(chip, sectors);
-+
-+		/* return the bitflips */
-+		bitflips = mtk_nfc_ecc_check(mtd, chip, sectors);
-+	}
-+
-+	return bitflips;
-+}
-+
-+static int mtk_nfc_block_markbad(struct mtd_info *mtd, loff_t ofs)
-+{
-+	struct nand_chip *chip = mtd_to_nand(mtd);
-+	u8 *buf = chip->buffers->databuf;
-+	int rc, i, pg;
-+
-+	/* block_markbad writes 0x00 at data and OOB */
-+	memset(buf, 0x00, mtd->writesize + mtd->oobsize);
-+
-+	/* Write to first/last page(s) if necessary */
-+	if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
-+		ofs += mtd->erasesize - mtd->writesize;
-+
-+	i = 0;
-+	do {
-+		pg = (int)(ofs >> chip->page_shift);
-+
-+		/**
-+		 *  write 0x00 to DATA & OOB in flash
-+		 *  No need to reorganize the page since it is all 0x00
-+		 */
-+		chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, pg);
-+		rc = mtk_nfc_write_page(mtd, chip, buf, MTK_OOB_OFF, pg,
-+			MTK_ECC_OFF);
-+		if (rc < 0)
-+			return rc;
-+
-+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-+		rc = chip->waitfunc(mtd, chip);
-+		rc = rc & NAND_STATUS_FAIL ? -EIO : 0;
-+		if (rc < 0)
-+			return rc;
-+
-+		ofs += mtd->writesize;
-+		i++;
-+
-+	} while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
-+
-+	return 0;
-+}
-+
-+static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
-+		uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
-+		int page, int raw)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	unsigned long timeout = msecs_to_jiffies(MTK_TIMEOUT);
-+	u32 reg, column, spare, sectors, start, end;
-+	struct completion *nfi, *ecc;
-+	const bool use_ecc = !raw;
-+	int bitflips = -EIO;
-+	dma_addr_t dma_addr;
-+	size_t len;
-+	u8 *buf;
-+	int rc;
-+
-+	nfi = &host->nfi.complete;
-+	ecc = &host->ecc.complete;
-+
-+	start = BYTES_TO_SECTORS(data_offs);
-+	end = BYTES_TO_SECTORS(data_offs + readlen + SECTOR_SIZE - 1);
-+	sectors = end - start;
-+
-+	spare = mtd->oobsize / chip->ecc.steps;
-+	column =  start * (SECTOR_SIZE + spare);
-+
-+	len = SECTORS_TO_BYTES(sectors) + (raw ? sectors * spare : 0);
-+	buf = bufpoi + SECTORS_TO_BYTES(start);
-+
-+	/* map the device memory */
-+	dma_addr = dma_map_single(host->dev, buf, len, DMA_FROM_DEVICE);
-+	if (dma_mapping_error(host->dev, dma_addr)) {
-+		dev_err(host->dev, "dma mapping error\n");
-+		return -EINVAL;
-+	}
-+
-+	/* configure the transfer  */
-+	reg = mtk_nfi_readw(host, MTKSDG1_NFI_CNFG);
-+	reg |= CNFG_DMA_BURST_EN | CNFG_AHB;
-+	if (use_ecc) {
-+		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+		/* enable encoder */
-+		mtk_ecc_decoder_idle(host);
-+		mtk_ecc_writel(host, DEC_EN, MTKSDG1_ECC_DECCON);
-+	} else
-+		mtk_nfi_writew(host, reg, MTKSDG1_NFI_CNFG);
-+
-+	mtk_nfi_writel(host, sectors << CON_SEC_SHIFT, MTKSDG1_NFI_CON);
-+	mtk_nfi_writew(host, INTR_BUSY_RT_EN, MTKSDG1_NFI_INTR_EN);
-+
-+	init_completion(nfi);
-+
-+	mtk_nfc_set_address(host, column, page, 2, host->row_nob);
-+	mtk_nfc_set_command(host, NAND_CMD_READSTART);
-+	rc = wait_for_completion_timeout(nfi, timeout);
-+	if (!rc) {
-+		dev_err(host->dev, "read busy return timeout\n");
-+		goto error;
-+	}
-+
-+	mtk_nfi_writew(host, INTR_AHB_DONE_EN, MTKSDG1_NFI_INTR_EN);
-+	mtk_nfi_writel(host, lower_32_bits(dma_addr), MTKSDG1_NFI_STRADDR);
-+
-+	if (use_ecc) {
-+		/* program ECC with sector count */
-+		host->ecc.dec_sec = sectors;
-+		init_completion(ecc);
-+		mtk_ecc_writew(host, DEC_IRQEN, MTKSDG1_ECC_DECIRQ_EN);
-+	}
-+
-+	init_completion(nfi);
-+
-+	/* start DMA */
-+	reg = mtk_nfi_readl(host, MTKSDG1_NFI_CON) | CON_BRD;
-+	mtk_nfi_writel(host, reg, MTKSDG1_NFI_CON);
-+
-+	rc = wait_for_completion_timeout(nfi, timeout);
-+	if (!rc)
-+		dev_warn(host->dev, "read ahb/dma done timeout\n");
-+
-+	/* DMA interrupt didn't trigger, check page done just in case */
-+	rc = mtk_nfc_subpage_done(host, sectors);
-+	if (rc < 0) {
-+		dev_err(host->dev, "subpage done timeout\n");
-+		goto error;
-+	}
-+
-+	/* raw transfer successful */
-+	bitflips = 0;
-+
-+	if (use_ecc) {
-+		rc = wait_for_completion_timeout(ecc, timeout);
-+		if (!rc) {
-+			dev_err(host->dev, "ecc decode timeout\n");
-+			host->ecc.dec_sec = 0;
-+			bitflips = -ETIMEDOUT;
-+			goto error;
-+		}
-+		bitflips = mtk_nfc_update_oob(mtd, chip, buf, sectors);
-+	}
-+
-+error:
-+	dma_unmap_single(host->dev, dma_addr, len, DMA_FROM_DEVICE);
-+
-+	if (use_ecc) {
-+		/* make sure the ECC dec irq  is disabled */
-+		mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
-+		mtk_ecc_decoder_idle(host);
-+
-+		/* disable ECC dec */
-+		mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECCON);
-+	}
-+
-+	mtk_nfi_writel(host, 0, MTKSDG1_NFI_CON);
-+
-+	return bitflips;
-+}
-+
-+static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
-+				struct nand_chip *chip, uint32_t data_offs,
-+				uint32_t readlen, uint8_t *bufpoi, int page)
-+{
-+	return mtk_nfc_read_subpage(mtd, chip, data_offs, readlen,
-+					bufpoi, page, MTK_ECC_ON);
-+}
-+
-+static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
-+				uint8_t *buf, int oob_on, int page)
-+{
-+	return mtk_nfc_read_subpage_hwecc(mtd, chip, 0, mtd->writesize,
-+						buf, page);
-+}
-+
-+static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+				uint8_t *buf, int oob_on, int page)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	uint8_t *src, *dst;
-+	int i, ret;
-+	size_t len;
-+
-+	dst = host->buffer;
-+	memset(dst, 0xff, mtd->writesize + mtd->oobsize);
-+	ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, dst, page, 1);
-+	if (ret < 0)
-+		return ret;
-+
-+	len = SECTOR_SIZE + mtd->oobsize / chip->ecc.steps;
-+
-+	/* copy to the output buffer */
-+	for (i = 0; i < chip->ecc.steps; i++) {
-+
-+		/* copy sector data */
-+		if (buf) {
-+			src = host->buffer + i * len;
-+			dst = buf + i * SECTOR_SIZE;
-+			memcpy(dst, src, SECTOR_SIZE);
-+		}
-+
-+		/* copy FDM data to OOB */
-+		if (oob_on) {
-+			src = host->buffer + i * len + SECTOR_SIZE;
-+			dst = chip->oob_poi + i * MTKSDG1_NFI_FDM_REG_SIZE;
-+			memcpy(dst, src, MTKSDG1_NFI_FDM_REG_SIZE);
-+		}
-+	}
-+
-+	return ret;
-+}
-+
-+static void mtk_nfc_switch_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+					uint8_t *buf)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	size_t spare;
-+	u32 sectors;
-+	u8 *bufpoi;
-+	int len;
-+
-+	spare = mtd->oobsize / chip->ecc.steps;
-+	sectors = mtd->writesize / (SECTOR_SIZE + spare);
-+
-+	/**
-+	 * MTK: DATA+oob1, DATA+oob2, DATA+oob3 ...
-+	 * LNX: DATA+OOB
-+	 */
-+	/* point to the last oob_i from the NAND device*/
-+	bufpoi = buf + mtd->writesize - (sectors * spare);
-+	len = sizeof(host->fdm_reg);
-+
-+	/* copy NAND oob to private area */
-+	memcpy(host->fdm_reg, bufpoi, len);
-+
-+	/* copy oob_poi to NAND */
-+	memcpy(bufpoi, chip->oob_poi, len);
-+
-+	/* copy NAND oob to oob_poi */
-+	memcpy(chip->oob_poi, host->fdm_reg, sizeof(host->fdm_reg));
-+	memset(host->fdm_reg, 0x00, len);
-+}
-+
-+static int mtk_nfc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
-+				int page)
-+{
-+	struct mtk_nfc_host *host = nand_get_controller_data(chip);
-+	u8 *buf = chip->buffers->databuf;
-+	struct mtd_ecc_stats stats;
-+	int ret;
-+
-+	stats = mtd->ecc_stats;
-+
-+	memset(buf, 0xff, mtd->writesize);
-+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-+
-+	ret = mtk_nfc_read_page_hwecc(mtd, chip, buf, 1, page);
-+
-+	if (host->switch_oob)
-+		mtk_nfc_switch_oob(mtd, chip, buf);
-+
-+	if (ret < mtd->bitflip_threshold)
-+		mtd->ecc_stats.corrected = stats.corrected;
-+
-+	return ret;
-+}
-+
-+static int mtk_nfc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
-+				int page)
-+{
-+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-+
-+	return mtk_nfc_read_page_raw(mtd, chip, NULL, MTK_OOB_ON, page);
-+}
-+
-+static inline void mtk_nfc_hw_init(struct mtk_nfc_host *host)
-+{
-+	mtk_nfi_writel(host, 0x10804211, MTKSDG1_NFI_ACCCON);
-+	mtk_nfi_writew(host, 0xf1, MTKSDG1_NFI_CNRNB);
-+	mtk_nfc_hw_reset(host);
-+
-+	/* clear interrupt */
-+	mtk_nfi_readl(host, MTKSDG1_NFI_INTR_STA);
-+	mtk_nfi_writel(host, 0, MTKSDG1_NFI_INTR_EN);
-+
-+	/* ECC encoder init */
-+	mtk_ecc_encoder_idle(host);
-+	mtk_ecc_writew(host, ENC_DE, MTKSDG1_ECC_ENCCON);
-+
-+	/* ECC decoder init */
-+	mtk_ecc_decoder_idle(host);
-+	mtk_ecc_writel(host, DEC_DE, MTKSDG1_ECC_DECCON);
-+}
-+
-+static irqreturn_t mtk_nfi_irq(int irq, void *devid)
-+{
-+	struct mtk_nfc_host *host = devid;
-+	u16 sta, ien;
-+
-+	sta = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_STA);
-+	ien = mtk_nfi_readw(host, MTKSDG1_NFI_INTR_EN);
-+
-+	if (!(sta & ien))
-+		return IRQ_NONE;
-+
-+	mtk_nfi_writew(host, ~sta & ien, MTKSDG1_NFI_INTR_EN);
-+	complete(&host->nfi.complete);
-+
-+	return IRQ_HANDLED;
-+}
-+
-+static irqreturn_t mtk_ecc_irq(int irq, void *devid)
-+{
-+	struct mtk_nfc_host *host = devid;
-+	u32 reg_val, mask;
-+
-+	reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECIRQ_STA);
-+	if (reg_val & DEC_IRQEN) {
-+		if (host->ecc.dec_sec) {
-+			mask = 1 << (host->ecc.dec_sec - 1);
-+			reg_val = mtk_ecc_readw(host, MTKSDG1_ECC_DECDONE);
-+			if (mask & reg_val) {
-+				host->ecc.dec_sec = 0;
-+				complete(&host->ecc.complete);
-+				mtk_ecc_writew(host, 0, MTKSDG1_ECC_DECIRQ_EN);
-+			}
-+		} else
-+			dev_warn(host->dev, "spurious DEC_IRQ\n");
-+
-+		return IRQ_HANDLED;
-+	}
-+
-+	reg_val = mtk_ecc_readl(host, MTKSDG1_ECC_ENCIRQ_STA);
-+	if (reg_val & ENC_IRQEN) {
-+		complete(&host->ecc.complete);
-+		mtk_ecc_writel(host, 0, MTKSDG1_ECC_ENCIRQ_EN);
-+
-+		return IRQ_HANDLED;
-+	}
-+
-+	return IRQ_NONE;
-+}
-+
-+static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
-+{
-+	int ret;
-+
-+	ret = clk_prepare_enable(clk->nfi_clk);
-+	if (ret) {
-+		dev_err(dev, "failed to enable nfi clk\n");
-+		return ret;
-+	}
-+
-+	ret = clk_prepare_enable(clk->nfiecc_clk);
-+	if (ret) {
-+		dev_err(dev, "failed to enable nfiecc clk\n");
-+		goto out_nfiecc_clk_disable;
-+	}
-+
-+	ret = clk_prepare_enable(clk->pad_clk);
-+	if (ret) {
-+		dev_err(dev, "failed to enable pad clk\n");
-+		goto out_pad_clk_disable;
-+	}
-+
-+	return 0;
-+
-+out_pad_clk_disable:
-+	clk_disable_unprepare(clk->nfiecc_clk);
-+
-+out_nfiecc_clk_disable:
-+	clk_disable_unprepare(clk->nfi_clk);
-+
-+	return ret;
-+}
-+
-+static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
-+{
-+	clk_disable_unprepare(clk->nfi_clk);
-+	clk_disable_unprepare(clk->nfiecc_clk);
-+	clk_disable_unprepare(clk->pad_clk);
-+}
-+
-+static int mtk_nfc_probe(struct platform_device *pdev)
-+{
-+	struct device *dev = &pdev->dev;
-+	struct device_node *np = dev->of_node;
-+	struct mtk_nfc_host *host;
-+	struct nand_chip *chip;
-+	struct mtd_info *mtd;
-+	struct resource *res;
-+	int ret, irq;
-+	size_t len;
-+
-+	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
-+	if (!host)
-+		return -ENOMEM;
-+
-+	chip = &host->chip;
-+	mtd = nand_to_mtd(chip);
-+	host->dev = dev;
-+
-+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+	host->nfi.base = devm_ioremap_resource(dev, res);
-+	if (IS_ERR(host->nfi.base)) {
-+		ret = PTR_ERR(host->nfi.base);
-+		dev_err(dev, "no nfi base\n");
-+		return ret;
-+	}
-+
-+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-+	host->ecc.base = devm_ioremap_resource(dev, res);
-+	if (IS_ERR(host->ecc.base)) {
-+		ret = PTR_ERR(host->ecc.base);
-+		dev_err(dev, "no ecc base\n");
-+		return ret;
-+	}
-+
-+	host->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
-+	if (IS_ERR(host->clk.nfi_clk)) {
-+		dev_err(dev, "no clk\n");
-+		ret = PTR_ERR(host->clk.nfi_clk);
-+		return ret;
-+	}
-+
-+	host->clk.nfiecc_clk = devm_clk_get(dev, "nfiecc_clk");
-+	if (IS_ERR(host->clk.nfiecc_clk)) {
-+		dev_err(dev, "no ecc clk\n");
-+		ret = PTR_ERR(host->clk.nfiecc_clk);
-+		return ret;
-+	}
-+
-+	host->clk.pad_clk = devm_clk_get(dev, "pad_clk");
-+	if (IS_ERR(host->clk.pad_clk)) {
-+		dev_err(dev, "no pad clk\n");
-+		ret = PTR_ERR(host->clk.pad_clk);
-+		return ret;
-+	}
-+
-+	ret = mtk_nfc_enable_clk(dev, &host->clk);
-+	if (ret)
-+		return ret;
-+
-+	irq = platform_get_irq(pdev, 0);
-+	if (irq < 0) {
-+		dev_err(dev, "no nfi irq resource\n");
-+		ret = -EINVAL;
-+		goto clk_disable;
-+	}
-+
-+	ret = devm_request_irq(dev, irq, mtk_nfi_irq, 0x0, MTK_IRQ_NFI, host);
-+	if (ret) {
-+		dev_err(dev, "failed to request nfi irq\n");
-+		goto clk_disable;
-+	}
-+
-+	irq = platform_get_irq(pdev, 1);
-+	if (irq < 0) {
-+		dev_err(dev, "no ecc irq resource\n");
-+		ret = -EINVAL;
-+		goto clk_disable;
-+	}
-+
-+	ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, MTK_IRQ_ECC, host);
-+	if (ret) {
-+		dev_err(dev, "failed to request ecc irq\n");
-+		goto clk_disable;
-+	}
-+
-+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
-+	if (ret) {
-+		dev_err(dev, "failed to set dma mask\n");
-+		goto clk_disable;
-+	}
-+
-+	platform_set_drvdata(pdev, host);
-+
-+	mtd_set_of_node(mtd, np);
-+	mtd->owner = THIS_MODULE;
-+	mtd->dev.parent = dev;
-+	mtd->name = MTK_NAME;
-+
-+	nand_set_controller_data(chip, host);
-+	chip->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
-+	chip->block_markbad = mtk_nfc_block_markbad;
-+	chip->select_chip = mtk_nfc_select_chip;
-+	chip->read_byte = mtk_nfc_read_byte;
-+	chip->cmdfunc = mtk_nfc_cmdfunc;
-+	chip->ecc.mode = NAND_ECC_HW;
-+	chip->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
-+	chip->ecc.write_page_raw = mtk_nfc_write_page_raw;
-+	chip->ecc.write_page = mtk_nfc_write_page_hwecc;
-+	chip->ecc.write_oob_raw = mtk_nfc_write_oob_raw;
-+	chip->ecc.write_oob = mtk_nfc_write_oob;
-+	chip->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
-+	chip->ecc.read_page_raw = mtk_nfc_read_page_raw;
-+	chip->ecc.read_oob_raw = mtk_nfc_read_oob_raw;
-+	chip->ecc.read_page = mtk_nfc_read_page_hwecc;
-+	chip->ecc.read_oob = mtk_nfc_read_oob;
-+
-+	mtk_nfc_hw_init(host);
-+
-+	ret = nand_scan_ident(mtd, MTK_NAND_MAX_CHIP, NULL);
-+	if (ret) {
-+		ret = -ENODEV;
-+		goto clk_disable;
-+	}
-+
-+	ret = mtk_nfc_hw_runtime_config(mtd);
-+	if (ret < 0) {
-+		dev_err(dev, "nand device not supported\n");
-+		goto clk_disable;
-+	}
-+
-+	len = mtd->writesize + mtd->oobsize;
-+	host->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
-+	if (!host->buffer) {
-+		ret = -ENOMEM;
-+		goto clk_disable;
-+	}
-+
-+	/* required to create bbt table if not present */
-+	host->switch_oob = true;
-+	ret = nand_scan_tail(mtd);
-+	if (ret) {
-+		ret = -ENODEV;
-+		goto clk_disable;
-+	}
-+	host->switch_oob = false;
-+
-+	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
-+	if (ret) {
-+		dev_err(dev, "mtd parse partition error\n");
-+		goto nand_free;
-+	}
-+
-+	return 0;
-+
-+nand_free:
-+	nand_release(mtd);
-+
-+clk_disable:
-+	mtk_nfc_disable_clk(&host->clk);
-+
-+	return ret;
-+}
-+
-+static int mtk_nfc_remove(struct platform_device *pdev)
-+{
-+	struct mtk_nfc_host *host = platform_get_drvdata(pdev);
-+	struct mtd_info *mtd = nand_to_mtd(&host->chip);
-+
-+	nand_release(mtd);
-+	mtk_nfc_disable_clk(&host->clk);
-+
-+	return 0;
-+}
-+
-+#ifdef CONFIG_PM_SLEEP
-+static int mtk_nfc_suspend(struct device *dev)
-+{
-+	struct mtk_nfc_host *host = dev_get_drvdata(dev);
-+	struct mtk_nfc_saved_reg *reg = &host->saved_reg;
-+
-+	reg->nfi.emp_thresh = mtk_nfi_readl(host, MTKSDG1_NFI_EMPTY_THRESH);
-+	reg->ecc.enccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_ENCCNFG);
-+	reg->ecc.deccnfg = mtk_ecc_readl(host, MTKSDG1_ECC_DECCNFG);
-+	reg->nfi.pagefmt = mtk_nfi_readw(host, MTKSDG1_NFI_PAGEFMT);
-+	reg->nfi.acccon = mtk_nfi_readl(host, MTKSDG1_NFI_ACCCON);
-+	reg->nfi.cnrnb = mtk_nfi_readw(host, MTKSDG1_NFI_CNRNB);
-+	reg->nfi.csel = mtk_nfi_readw(host, MTKSDG1_NFI_CSEL);
-+
-+	mtk_nfc_disable_clk(&host->clk);
-+
-+	return 0;
-+}
-+
-+static int mtk_nfc_resume(struct device *dev)
-+{
-+	struct mtk_nfc_host *host = dev_get_drvdata(dev);
-+	struct mtk_nfc_saved_reg *reg = &host->saved_reg;
-+	struct nand_chip *chip = &host->chip;
-+	struct mtd_info *mtd = nand_to_mtd(chip);
-+	int ret;
-+	u32 i;
-+
-+	udelay(200);
-+
-+	ret = mtk_nfc_enable_clk(dev, &host->clk);
-+	if (ret)
-+		return ret;
-+
-+	for (i = 0; i < chip->numchips; i++) {
-+		chip->select_chip(mtd, i);
-+		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-+	}
-+
-+	mtk_nfi_writel(host, reg->nfi.emp_thresh, MTKSDG1_NFI_EMPTY_THRESH);
-+	mtk_nfi_writew(host, reg->nfi.pagefmt, MTKSDG1_NFI_PAGEFMT);
-+	mtk_ecc_writel(host, reg->ecc.enccnfg, MTKSDG1_ECC_ENCCNFG);
-+	mtk_ecc_writel(host, reg->ecc.deccnfg, MTKSDG1_ECC_DECCNFG);
-+	mtk_nfi_writel(host, reg->nfi.acccon, MTKSDG1_NFI_ACCCON);
-+	mtk_nfi_writew(host, reg->nfi.cnrnb, MTKSDG1_NFI_CNRNB);
-+	mtk_nfi_writew(host, reg->nfi.csel, MTKSDG1_NFI_CSEL);
-+
-+	return 0;
-+}
-+
-+static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
-+#endif
-+
-+static const struct of_device_id mtk_nfc_id_table[] = {
-+	{ .compatible = "mediatek,mt2701-nfc" },
-+	{}
-+};
-+MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
-+
-+static struct platform_driver mtk_nfc_driver = {
-+	.probe  = mtk_nfc_probe,
-+	.remove = mtk_nfc_remove,
-+	.driver = {
-+		.name  = MTK_NAME,
-+		.of_match_table = mtk_nfc_id_table,
-+#ifdef CONFIG_PM_SLEEP
-+		.pm = &mtk_nfc_pm_ops,
-+#endif
-+	},
-+};
-+
-+module_platform_driver(mtk_nfc_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>");
-+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
-+
-diff --git a/drivers/mtd/nand/mtksdg1_nand_ecc.h b/drivers/mtd/nand/mtksdg1_nand_ecc.h
-new file mode 100644
-index 0000000..d90b196
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand_ecc.h
-@@ -0,0 +1,75 @@
-+/*
-+ * MTK smart device ECC engine register.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
-+ *
-+ * 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 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 the
-+ * GNU General Public License for more details.
-+ */
-+
-+#ifndef MTKSDG1_NAND_ECC_H
-+#define MTKSDG1_NAND_ECC_H
-+
-+/* ECC engine register definition */
-+#define MTKSDG1_ECC_ENCCON		(0x00)
-+#define		ENC_EN			(1)
-+#define		ENC_DE			(0)
-+
-+#define MTKSDG1_ECC_ENCCNFG		(0x04)
-+#define		ECC_CNFG_4BIT		(0)
-+#define		ECC_CNFG_12BIT		(4)
-+#define		ECC_NFI_MODE		BIT(5)
-+#define		ECC_DMA_MODE		(0)
-+#define		ECC_ENC_MODE_MASK	(0x3 << 5)
-+#define		ECC_MS_SHIFT		(16)
-+
-+#define MTKSDG1_ECC_ENCDIADDR		(0x08)
-+
-+#define MTKSDG1_ECC_ENCIDLE		(0x0C)
-+#define		ENC_IDLE		BIT(0)
-+
-+#define MTKSDG1_ECC_ENCPAR0		(0x10)
-+#define MTKSDG1_ECC_ENCSTA		(0x7C)
-+
-+#define MTKSDG1_ECC_ENCIRQ_EN		(0x80)
-+#define		ENC_IRQEN		BIT(0)
-+
-+#define MTKSDG1_ECC_ENCIRQ_STA		(0x84)
-+
-+#define MTKSDG1_ECC_DECCON		(0x100)
-+#define		DEC_EN			(1)
-+#define		DEC_DE			(0)
-+
-+#define MTKSDG1_ECC_DECCNFG		(0x104)
-+#define		DEC_EMPTY_EN		BIT(31)
-+#define		DEC_CNFG_FER		(0x1 << 12)
-+#define		DEC_CNFG_EL		(0x2 << 12)
-+#define		DEC_CNFG_CORRECT	(0x3 << 12)
-+
-+#define MTKSDG1_ECC_DECIDLE		(0x10C)
-+#define		DEC_IDLE		BIT(0)
-+
-+#define MTKSDG1_ECC_DECFER		(0x110)
-+
-+#define MTKSDG1_ECC_DECENUM0		(0x114)
-+#define		ERR_MASK		(0x3f)
-+
-+#define MTKSDG1_ECC_DECDONE		(0x124)
-+
-+#define MTKSDG1_ECC_DECEL0		(0x128)
-+
-+#define MTKSDG1_ECC_DECIRQ_EN		(0x200)
-+#define		DEC_IRQEN		BIT(0)
-+
-+#define MTKSDG1_ECC_DECIRQ_STA		(0x204)
-+
-+#define MTKSDG1_ECC_DECFSM		(0x208)
-+#define		DECFSM_MASK		(0x7f0f0f0f)
-+#define		DECFSM_IDLE		(0x01010101)
-+#endif
-diff --git a/drivers/mtd/nand/mtksdg1_nand_nfi.h b/drivers/mtd/nand/mtksdg1_nand_nfi.h
-new file mode 100644
-index 0000000..a9aa6f6
---- /dev/null
-+++ b/drivers/mtd/nand/mtksdg1_nand_nfi.h
-@@ -0,0 +1,119 @@
-+/*
-+ * MTK smart device NAND Flash controller register.
-+ * Copyright (C) 2015-2016 MediaTek Inc.
-+ * Author: Xiaolei.Li <xiaolei.li@mediatek.com>
-+ *
-+ * 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 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 the
-+ * GNU General Public License for more details.
-+ */
-+
-+#ifndef MTKSDG1_NAND_NFI_H
-+#define MTKSDG1_NAND_NFI_H
-+
-+/* NAND controller register definition */
-+#define MTKSDG1_NFI_CNFG		(0x00)
-+#define		CNFG_AHB		BIT(0)
-+#define		CNFG_READ_EN		BIT(1)
-+#define		CNFG_DMA_BURST_EN	BIT(2)
-+#define		CNFG_BYTE_RW		BIT(6)
-+#define		CNFG_HW_ECC_EN		BIT(8)
-+#define		CNFG_AUTO_FMT_EN	BIT(9)
-+#define		CNFG_OP_IDLE		(0 << 12)
-+#define		CNFG_OP_READ		(1 << 12)
-+#define		CNFG_OP_SRD		(2 << 12)
-+#define		CNFG_OP_PRGM		(3 << 12)
-+#define		CNFG_OP_ERASE		(4 << 12)
-+#define		CNFG_OP_RESET		(5 << 12)
-+#define		CNFG_OP_CUST		(6 << 12)
-+
-+#define MTKSDG1_NFI_PAGEFMT		(0x04)
-+#define		PAGEFMT_FDM_ECC_SHIFT	(12)
-+#define		PAGEFMT_FDM_SHIFT	(8)
-+#define		PAGEFMT_SPARE_16	(0)
-+#define		PAGEFMT_SPARE_32	(4)
-+#define		PAGEFMT_SPARE_SHIFT	(4)
-+#define		PAGEFMT_SEC_SEL_512	BIT(2)
-+#define		PAGEFMT_512_2K		(0)
-+#define		PAGEFMT_2K_4K		(1)
-+#define		PAGEFMT_4K_8K		(2)
-+
-+/* NFI control */
-+#define MTKSDG1_NFI_CON			(0x08)
-+#define		CON_FIFO_FLUSH		BIT(0)
-+#define		CON_NFI_RST		BIT(1)
-+#define		CON_SRD			BIT(4)	/* single read */
-+#define		CON_BRD			BIT(8)  /* burst  read */
-+#define		CON_BWR			BIT(9)	/* burst  write */
-+#define		CON_SEC_SHIFT		(12)
-+
-+/* Timming control register */
-+#define MTKSDG1_NFI_ACCCON		(0x0C)
-+
-+#define MTKSDG1_NFI_INTR_EN		(0x10)
-+#define		INTR_RD_DONE_EN		BIT(0)
-+#define		INTR_WR_DONE_EN		BIT(1)
-+#define		INTR_RST_DONE_EN	BIT(2)
-+#define		INTR_ERS_DONE_EN	BIT(3)
-+#define		INTR_BUSY_RT_EN		BIT(4)
-+#define		INTR_AHB_DONE_EN	BIT(6)
-+
-+#define MTKSDG1_NFI_INTR_STA		(0x14)
-+
-+#define MTKSDG1_NFI_CMD			(0x20)
-+
-+#define MTKSDG1_NFI_ADDRNOB		(0x30)
-+#define		ADDR_ROW_NOB_SHIFT	(4)
-+
-+#define MTKSDG1_NFI_COLADDR		(0x34)
-+#define MTKSDG1_NFI_ROWADDR		(0x38)
-+#define MTKSDG1_NFI_STRDATA		(0x40)
-+#define MTKSDG1_NFI_CNRNB		(0x44)
-+#define MTKSDG1_NFI_DATAW		(0x50)
-+#define MTKSDG1_NFI_DATAR		(0x54)
-+#define MTKSDG1_NFI_PIO_DIRDY		(0x58)
-+#define		PIO_DI_RDY		(0x01)
-+
-+/* NFI state*/
-+#define MTKSDG1_NFI_STA			(0x60)
-+#define		STA_CMD			BIT(0)
-+#define		STA_ADDR		BIT(1)
-+#define		STA_DATAR		BIT(2)
-+#define		STA_DATAW		BIT(3)
-+#define		STA_EMP_PAGE		BIT(12)
-+
-+#define MTKSDG1_NFI_FIFOSTA		(0x64)
-+
-+#define MTKSDG1_NFI_ADDRCNTR		(0x70)
-+#define		CNTR_MASK		GENMASK(16, 12)
-+
-+#define MTKSDG1_NFI_STRADDR		(0x80)
-+#define MTKSDG1_NFI_BYTELEN		(0x84)
-+#define MTKSDG1_NFI_CSEL		(0x90)
-+#define MTKSDG1_NFI_IOCON		(0x94)
-+
-+/* FDM data for sector: FDM0[L,H] - FDMF[L,H] */
-+#define MTKSDG1_NFI_FDM_MAX_SEC		(0x10)
-+#define MTKSDG1_NFI_FDM_REG_SIZE	(8)
-+#define MTKSDG1_NFI_FDM0L		(0xA0)
-+#define MTKSDG1_NFI_FDM0M		(0xA4)
-+
-+
-+#define MTKSDG1_NFI_FIFODATA0		(0x190)
-+#define MTKSDG1_NFI_DEBUG_CON1		(0x220)
-+#define MTKSDG1_NFI_MASTER_STA		(0x224)
-+#define		MASTER_STA_MASK		(0x0FFF)
-+
-+#define MTKSDG1_NFI_RANDOM_CNFG		(0x238)
-+#define MTKSDG1_NFI_EMPTY_THRESH	(0x23C)
-+#define MTKSDG1_NFI_NAND_TYPE		(0x240)
-+#define MTKSDG1_NFI_ACCCON1		(0x244)
-+#define MTKSDG1_NFI_DELAY_CTRL		(0x248)
-+
-+#endif
-+
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0065-net-mediatek-watchdog_timeo-was-not-set.patch b/target/linux/mediatek/patches-4.4/0065-net-mediatek-watchdog_timeo-was-not-set.patch
new file mode 100644
index 0000000000..0a7c61566f
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0065-net-mediatek-watchdog_timeo-was-not-set.patch
@@ -0,0 +1,28 @@
+From 103d950e6201ab54a8b64402bb0b32a35831b028 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Mar 2016 03:18:17 +0200
+Subject: [PATCH 65/78] net: mediatek: watchdog_timeo was not set
+
+The original commit failed to set watchdog_timeo. This patch sets
+watchdog_timeo to HZ.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 7f2126b..7e6d2e2 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1645,6 +1645,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ 	mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+ 
+ 	SET_NETDEV_DEV(eth->netdev[id], eth->dev);
++	eth->netdev[id]->watchdog_timeo = HZ;
+ 	eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ 	eth->netdev[id]->base_addr = (unsigned long)eth->base;
+ 	eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0066-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch b/target/linux/mediatek/patches-4.4/0066-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch
new file mode 100644
index 0000000000..fbb744440b
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0066-net-mediatek-mtk_cal_txd_req-returns-bad-value.patch
@@ -0,0 +1,30 @@
+From 1212704e8270f0e673e10a49960318ade0096cf9 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 22 Mar 2016 04:42:27 +0100
+Subject: [PATCH 66/78] net: mediatek: mtk_cal_txd_req() returns bad value
+
+The code used to also support the PDMA engine, which had 2 packet pointers
+per descriptor. Because of this we have to divide the result by 2 and round
+it up. This is no longer needed as the code only supports QDMA.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 7e6d2e2..4d8d0a3 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -681,7 +681,7 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
+ 		nfrags += skb_shinfo(skb)->nr_frags;
+ 	}
+ 
+-	return DIV_ROUND_UP(nfrags, 2);
++	return nfrags;
+ }
+ 
+ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0066-net-next-mediatek-mtk_cal_txd_req-returns-bad-value.patch b/target/linux/mediatek/patches-4.4/0066-net-next-mediatek-mtk_cal_txd_req-returns-bad-value.patch
deleted file mode 100644
index 28899c46da..0000000000
--- a/target/linux/mediatek/patches-4.4/0066-net-next-mediatek-mtk_cal_txd_req-returns-bad-value.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From a160c7846e1f81b5cd6c5d0fbe5c1f8757e8884b Mon Sep 17 00:00:00 2001
-From: John Crispin <blogic@openwrt.org>
-Date: Tue, 22 Mar 2016 04:42:27 +0100
-Subject: [PATCH 66/66] net-next: mediatek: mtk_cal_txd_req() returns bad
- value
-
-The code used to also support the PDMA engine, which had 2 packet pointers
-per descriptor. Because of this we have to divide the result by 2 and round
-it up. This is no longer needed as the code only supports QDMA.
-
-Signed-off-by: John Crispin <blogic@openwrt.org>
----
- drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-index dd7f6e3..da9968ae 100644
---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
-@@ -693,7 +693,7 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
- 		nfrags += skb_shinfo(skb)->nr_frags;
- 	}
- 
--	return DIV_ROUND_UP(nfrags, 2);
-+	return nfrags;
- }
- 
- static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
--- 
-1.7.10.4
-
diff --git a/target/linux/mediatek/patches-4.4/0067-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch b/target/linux/mediatek/patches-4.4/0067-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch
new file mode 100644
index 0000000000..5f02dd5820
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0067-net-mediatek-update-the-IRQ-part-of-the-binding-docu.patch
@@ -0,0 +1,45 @@
+From 429b5becfb1e4aacf392c4b246a17b83faad3072 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 14:32:07 +0200
+Subject: [PATCH 67/78] net: mediatek: update the IRQ part of the binding
+ document
+
+The current binding document only describes a single interrupt. Update the
+document by adding the 2 other interrupts.
+
+The driver currently only uses a single interrupt. The HW is however able
+to using IRQ grouping to split TX and RX onto separate GIC irqs.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+ Acked-by: Rob Herring <robh@kernel.org>
+---
+ Documentation/devicetree/bindings/net/mediatek-net.txt |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/net/mediatek-net.txt b/Documentation/devicetree/bindings/net/mediatek-net.txt
+index 5ca7929..2f142be 100644
+--- a/Documentation/devicetree/bindings/net/mediatek-net.txt
++++ b/Documentation/devicetree/bindings/net/mediatek-net.txt
+@@ -9,7 +9,7 @@ have dual GMAC each represented by a child node..
+ Required properties:
+ - compatible: Should be "mediatek,mt7623-eth"
+ - reg: Address and length of the register set for the device
+-- interrupts: Should contain the frame engines interrupt
++- interrupts: Should contain the three frame engines interrupts
+ - clocks: the clock used by the core
+ - clock-names: the names of the clock listed in the clocks property. These are
+ 	"ethif", "esw", "gp2", "gp1"
+@@ -42,7 +42,9 @@ eth: ethernet@1b100000 {
+ 		 <&ethsys CLK_ETHSYS_GP2>,
+ 		 <&ethsys CLK_ETHSYS_GP1>;
+ 	clock-names = "ethif", "esw", "gp2", "gp1";
+-	interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW>;
++	interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
++		      GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
++		      GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
+ 	power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
+ 	resets = <&ethsys MT2701_ETHSYS_ETH_RST>;
+ 	reset-names = "eth";
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-superflous-reset-call.patch b/target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-superflous-reset-call.patch
new file mode 100644
index 0000000000..2f940d34fc
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0068-net-mediatek-remove-superflous-reset-call.patch
@@ -0,0 +1,31 @@
+From 3c8781211140cc23750544a52b7310edb3b57f00 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Fri, 25 Mar 2016 04:24:27 +0100
+Subject: [PATCH 68/78] net: mediatek: remove superflous reset call
+
+HW reset is triggered int he mtk_hw_init() function. There is no need to
+reset the core during probe.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ----
+ 1 file changed, 4 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 4d8d0a3..293ea59 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1679,10 +1679,6 @@ static int mtk_probe(struct platform_device *pdev)
+ 	struct mtk_eth *eth;
+ 	int err;
+ 
+-	err = device_reset(&pdev->dev);
+-	if (err)
+-		return err;
+-
+ 	match = of_match_device(of_mtk_match, &pdev->dev);
+ 	soc = (struct mtk_soc_data *)match->data;
+ 
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0069-net-mediatek-fix-stop-and-wakeup-of-queue.patch b/target/linux/mediatek/patches-4.4/0069-net-mediatek-fix-stop-and-wakeup-of-queue.patch
new file mode 100644
index 0000000000..170266227c
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0069-net-mediatek-fix-stop-and-wakeup-of-queue.patch
@@ -0,0 +1,89 @@
+From 6f8bfdfe2984467177d2a88982517659ec09ab5d Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 16:41:07 +0200
+Subject: [PATCH 69/78] net: mediatek: fix stop and wakeup of queue
+
+The driver supports 2 MACs. Both run on the same DMA ring. If we go
+above/below the TX rings thershold value, we always need to wake/stop
+the queu of both devices. Not doing to can cause TX stalls and packet
+drops on one of the devices.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |   37 +++++++++++++++++++--------
+ 1 file changed, 27 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 293ea59..04bdb9d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -684,6 +684,28 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
+ 	return nfrags;
+ }
+ 
++static void mtk_wake_queue(struct mtk_eth *eth)
++{
++	int i;
++
++	for (i = 0; i < MTK_MAC_COUNT; i++) {
++		if (!eth->netdev[i])
++			continue;
++		netif_wake_queue(eth->netdev[i]);
++	}
++}
++
++static void mtk_stop_queue(struct mtk_eth *eth)
++{
++	int i;
++
++	for (i = 0; i < MTK_MAC_COUNT; i++) {
++		if (!eth->netdev[i])
++			continue;
++		netif_stop_queue(eth->netdev[i]);
++	}
++}
++
+ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ {
+ 	struct mtk_mac *mac = netdev_priv(dev);
+@@ -695,7 +717,7 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ 
+ 	tx_num = mtk_cal_txd_req(skb);
+ 	if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+-		netif_stop_queue(dev);
++		mtk_stop_queue(eth);
+ 		netif_err(eth, tx_queued, dev,
+ 			  "Tx Ring full when queue awake!\n");
+ 		return NETDEV_TX_BUSY;
+@@ -720,10 +742,10 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ 		goto drop;
+ 
+ 	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+-		netif_stop_queue(dev);
++		mtk_stop_queue(eth);
+ 		if (unlikely(atomic_read(&ring->free_count) >
+ 			     ring->thresh))
+-			netif_wake_queue(dev);
++			mtk_wake_queue(eth);
+ 	}
+ 
+ 	return NETDEV_TX_OK;
+@@ -897,13 +919,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ 	if (!total)
+ 		return 0;
+ 
+-	for (i = 0; i < MTK_MAC_COUNT; i++) {
+-		if (!eth->netdev[i] ||
+-		    unlikely(!netif_queue_stopped(eth->netdev[i])))
+-			continue;
+-		if (atomic_read(&ring->free_count) > ring->thresh)
+-			netif_wake_queue(eth->netdev[i]);
+-	}
++	if (atomic_read(&ring->free_count) > ring->thresh)
++		mtk_wake_queue(eth);
+ 
+ 	return total;
+ }
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0070-net-mediatek-fix-mtk_pending_work.patch b/target/linux/mediatek/patches-4.4/0070-net-mediatek-fix-mtk_pending_work.patch
new file mode 100644
index 0000000000..27f282b393
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0070-net-mediatek-fix-mtk_pending_work.patch
@@ -0,0 +1,63 @@
+From a2dfb33c8a0dc03fe2ec2121490df2b9352febef Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:00:47 +0200
+Subject: [PATCH 70/78] net: mediatek: fix mtk_pending_work
+
+The driver supports 2 MACs. Both run on the same DMA ring. If we hit a TX
+timeout we need to stop both netdevs before retarting them again. If we
+dont do thsi, mtk_stop() wont shutdown DMA and the consecutive call to
+mtk_open() wont restart DMA and enable IRQs.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |   30 +++++++++++++++++++--------
+ 1 file changed, 21 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 04bdb9d..26eeb1a 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1430,19 +1430,31 @@ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ 
+ static void mtk_pending_work(struct work_struct *work)
+ {
+-	struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
+-	struct mtk_eth *eth = mac->hw;
+-	struct net_device *dev = eth->netdev[mac->id];
+-	int err;
++	struct mtk_eth *eth = container_of(work, struct mtk_eth, pending_work);
++	int err, i;
++	unsigned long restart = 0;
+ 
+ 	rtnl_lock();
+-	mtk_stop(dev);
+ 
+-	err = mtk_open(dev);
+-	if (err) {
+-		netif_alert(eth, ifup, dev,
++	/* stop all devices to make sure that dma is properly shut down */
++	for (i = 0; i < MTK_MAC_COUNT; i++) {
++		if (!netif_oper_up(eth->netdev[i]))
++			continue;
++		mtk_stop(eth->netdev[i]);
++		__set_bit(i, &restart);
++	}
++
++
++	/* restart DMA and enable IRQs */
++	for (i = 0; i < MTK_MAC_COUNT; i++) {
++		if (!test_bit(i, &restart))
++			continue;
++		err = mtk_open(eth->netdev[i]);
++		if (err) {
++			netif_alert(eth, ifup, eth->netdev[i],
+ 			    "Driver up/down cycle failed, closing device.\n");
+-		dev_close(dev);
++			dev_close(eth->netdev[i]);
++		}
+ 	}
+ 	rtnl_unlock();
+ }
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0071-net-mediatek-fix-TX-locking.patch b/target/linux/mediatek/patches-4.4/0071-net-mediatek-fix-TX-locking.patch
new file mode 100644
index 0000000000..011fad2e61
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0071-net-mediatek-fix-TX-locking.patch
@@ -0,0 +1,98 @@
+From b9df14f712866925856c0ffb2d899511c21e1b8a Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:20:01 +0200
+Subject: [PATCH 71/78] net: mediatek: fix TX locking
+
+Inside the TX path there is a lock inside the tx_map function. This is
+however too late. The patch moves the lock to the start of the xmit
+function right before the free count check of the DMA ring happens.
+If we do not do this, the code becomes racy leading to TX stalls and
+dropped packets. This happens as there are 2 netdevs running on the
+same physical DMA ring.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |   20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 26eeb1a..67b18f9 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -536,7 +536,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ 	struct mtk_eth *eth = mac->hw;
+ 	struct mtk_tx_dma *itxd, *txd;
+ 	struct mtk_tx_buf *tx_buf;
+-	unsigned long flags;
+ 	dma_addr_t mapped_addr;
+ 	unsigned int nr_frags;
+ 	int i, n_desc = 1;
+@@ -568,11 +567,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ 	if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ 		return -ENOMEM;
+ 
+-	/* normally we can rely on the stack not calling this more than once,
+-	 * however we have 2 queues running ont he same ring so we need to lock
+-	 * the ring access
+-	 */
+-	spin_lock_irqsave(&eth->page_lock, flags);
+ 	WRITE_ONCE(itxd->txd1, mapped_addr);
+ 	tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ 	dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+@@ -632,8 +626,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ 	WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ 				(!nr_frags * TX_DMA_LS0)));
+ 
+-	spin_unlock_irqrestore(&eth->page_lock, flags);
+-
+ 	netdev_sent_queue(dev, skb->len);
+ 	skb_tx_timestamp(skb);
+ 
+@@ -661,8 +653,6 @@ err_dma:
+ 		itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2);
+ 	} while (itxd != txd);
+ 
+-	spin_unlock_irqrestore(&eth->page_lock, flags);
+-
+ 	return -ENOMEM;
+ }
+ 
+@@ -712,14 +702,22 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ 	struct mtk_eth *eth = mac->hw;
+ 	struct mtk_tx_ring *ring = &eth->tx_ring;
+ 	struct net_device_stats *stats = &dev->stats;
++	unsigned long flags;
+ 	bool gso = false;
+ 	int tx_num;
+ 
++	/* normally we can rely on the stack not calling this more than once,
++	 * however we have 2 queues running ont he same ring so we need to lock
++	 * the ring access
++	 */
++	spin_lock_irqsave(&eth->page_lock, flags);
++
+ 	tx_num = mtk_cal_txd_req(skb);
+ 	if (unlikely(atomic_read(&ring->free_count) <= tx_num)) {
+ 		mtk_stop_queue(eth);
+ 		netif_err(eth, tx_queued, dev,
+ 			  "Tx Ring full when queue awake!\n");
++		spin_unlock_irqrestore(&eth->page_lock, flags);
+ 		return NETDEV_TX_BUSY;
+ 	}
+ 
+@@ -747,10 +745,12 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+ 			     ring->thresh))
+ 			mtk_wake_queue(eth);
+ 	}
++	spin_unlock_irqrestore(&eth->page_lock, flags);
+ 
+ 	return NETDEV_TX_OK;
+ 
+ drop:
++	spin_unlock_irqrestore(&eth->page_lock, flags);
+ 	stats->tx_dropped++;
+ 	dev_kfree_skb(skb);
+ 	return NETDEV_TX_OK;
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0072-net-mediatek-move-the-pending_work-struct-to-the-dev.patch b/target/linux/mediatek/patches-4.4/0072-net-mediatek-move-the-pending_work-struct-to-the-dev.patch
new file mode 100644
index 0000000000..1b9f221588
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0072-net-mediatek-move-the-pending_work-struct-to-the-dev.patch
@@ -0,0 +1,109 @@
+From 147dab4408adcccf702f8c5143e9c6b91f746790 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Tue, 29 Mar 2016 17:24:24 +0200
+Subject: [PATCH 72/78] net: mediatek: move the pending_work struct to the
+ device generic struct
+
+The worker always touches both netdevs. It is ethernet core and not MAC
+specific. We only need one worker, which belongs into the ethernets core struct.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |   10 ++++------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |    4 ++--
+ 2 files changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 67b18f9..bbcd607 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -1193,7 +1193,7 @@ static void mtk_tx_timeout(struct net_device *dev)
+ 	eth->netdev[mac->id]->stats.tx_errors++;
+ 	netif_err(eth, tx_err, dev,
+ 		  "transmit timed out\n");
+-	schedule_work(&mac->pending_work);
++	schedule_work(&eth->pending_work);
+ }
+ 
+ static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+@@ -1438,7 +1438,7 @@ static void mtk_pending_work(struct work_struct *work)
+ 
+ 	/* stop all devices to make sure that dma is properly shut down */
+ 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+-		if (!netif_oper_up(eth->netdev[i]))
++		if (!eth->netdev[i])
+ 			continue;
+ 		mtk_stop(eth->netdev[i]);
+ 		__set_bit(i, &restart);
+@@ -1464,15 +1464,13 @@ static int mtk_cleanup(struct mtk_eth *eth)
+ 	int i;
+ 
+ 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+-		struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
+-
+ 		if (!eth->netdev[i])
+ 			continue;
+ 
+ 		unregister_netdev(eth->netdev[i]);
+ 		free_netdev(eth->netdev[i]);
+-		cancel_work_sync(&mac->pending_work);
+ 	}
++	cancel_work_sync(&eth->pending_work);
+ 
+ 	return 0;
+ }
+@@ -1660,7 +1658,6 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ 	mac->id = id;
+ 	mac->hw = eth;
+ 	mac->of_node = np;
+-	INIT_WORK(&mac->pending_work, mtk_pending_work);
+ 
+ 	mac->hw_stats = devm_kzalloc(eth->dev,
+ 				     sizeof(*mac->hw_stats),
+@@ -1762,6 +1759,7 @@ static int mtk_probe(struct platform_device *pdev)
+ 
+ 	eth->dev = &pdev->dev;
+ 	eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
++	INIT_WORK(&eth->pending_work, mtk_pending_work);
+ 
+ 	err = mtk_hw_init(eth);
+ 	if (err)
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 48a5292..eed626d 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -363,6 +363,7 @@ struct mtk_rx_ring {
+  * @clk_gp1:		The gmac1 clock
+  * @clk_gp2:		The gmac2 clock
+  * @mii_bus:		If there is a bus we need to create an instance for it
++ * @pending_work:	The workqueue used to reset the dma ring
+  */
+ 
+ struct mtk_eth {
+@@ -389,6 +390,7 @@ struct mtk_eth {
+ 	struct clk			*clk_gp1;
+ 	struct clk			*clk_gp2;
+ 	struct mii_bus			*mii_bus;
++	struct work_struct		pending_work;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+@@ -398,7 +400,6 @@ struct mtk_eth {
+  * @hw:			Backpointer to our main datastruture
+  * @hw_stats:		Packet statistics counter
+  * @phy_dev:		The attached PHY if available
+- * @pending_work:	The workqueue used to reset the dma ring
+  */
+ struct mtk_mac {
+ 	int				id;
+@@ -406,7 +407,6 @@ struct mtk_mac {
+ 	struct mtk_eth			*hw;
+ 	struct mtk_hw_stats		*hw_stats;
+ 	struct phy_device		*phy_dev;
+-	struct work_struct		pending_work;
+ };
+ 
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0073-net-next-mediatek-add-support-for-IRQ-grouping.patch b/target/linux/mediatek/patches-4.4/0073-net-next-mediatek-add-support-for-IRQ-grouping.patch
new file mode 100644
index 0000000000..6457deba0a
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0073-net-next-mediatek-add-support-for-IRQ-grouping.patch
@@ -0,0 +1,314 @@
+From e301da64a9fd2ebc24d4c9b2d184681ec833fd72 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 23 Mar 2016 18:31:48 +0100
+Subject: [PATCH 73/78] net-next: mediatek: add support for IRQ grouping
+
+The ethernet core has 3 IRQs. using the IRQ grouping registers we are able
+to separate TX and RX IRQs, which allows us to service them on separate
+cores. This patch splits the irq handler into 2 separate functiosn, one for
+TX and another for RX. The TX housekeeping is split out of the NAPI handler.
+Instead we use a tasklet to handle housekeeping.
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |  115 +++++++++++++++++----------
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |   12 ++-
+ 2 files changed, 86 insertions(+), 41 deletions(-)
+
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index bbcd607..2097ae1 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -757,7 +757,7 @@ drop:
+ }
+ 
+ static int mtk_poll_rx(struct napi_struct *napi, int budget,
+-		       struct mtk_eth *eth, u32 rx_intr)
++		       struct mtk_eth *eth)
+ {
+ 	struct mtk_rx_ring *ring = &eth->rx_ring;
+ 	int idx = ring->calc_idx;
+@@ -843,12 +843,12 @@ release_desc:
+ 	}
+ 
+ 	if (done < budget)
+-		mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
++		mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
+ 
+ 	return done;
+ }
+ 
+-static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
++static int mtk_poll_tx(struct mtk_eth *eth, int budget)
+ {
+ 	struct mtk_tx_ring *ring = &eth->tx_ring;
+ 	struct mtk_tx_dma *desc;
+@@ -911,9 +911,7 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ 	}
+ 
+ 	/* read hw index again make sure no new tx packet */
+-	if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
+-		*tx_again = true;
+-	else
++	if (cpu == dma && cpu == mtk_r32(eth, MTK_QTX_DRX_PTR))
+ 		mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
+ 
+ 	if (!total)
+@@ -925,27 +923,27 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+ 	return total;
+ }
+ 
++static void mtk_clean_tx_tasklet(unsigned long arg)
++{
++	struct mtk_eth *eth = (struct mtk_eth *)arg;
++
++	if (mtk_poll_tx(eth, MTK_NAPI_WEIGHT) > 0)
++		tasklet_schedule(&eth->tx_clean_tasklet);
++	else
++		mtk_irq_enable(eth, MTK_TX_DONE_INT);
++}
++
+ static int mtk_poll(struct napi_struct *napi, int budget)
+ {
+ 	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+-	u32 status, status2, mask, tx_intr, rx_intr, status_intr;
+-	int tx_done, rx_done;
+-	bool tx_again = false;
++	u32 status, status2, mask, status_intr;
++	int rx_done = 0;
+ 
+ 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+ 	status2 = mtk_r32(eth, MTK_INT_STATUS2);
+-	tx_intr = MTK_TX_DONE_INT;
+-	rx_intr = MTK_RX_DONE_INT;
+ 	status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
+-	tx_done = 0;
+-	rx_done = 0;
+-	tx_again = 0;
+ 
+-	if (status & tx_intr)
+-		tx_done = mtk_poll_tx(eth, budget, &tx_again);
+-
+-	if (status & rx_intr)
+-		rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
++	rx_done = mtk_poll_rx(napi, budget, eth);
+ 
+ 	if (unlikely(status2 & status_intr)) {
+ 		mtk_stats_update(eth);
+@@ -954,20 +952,20 @@ static int mtk_poll(struct napi_struct *napi, int budget)
+ 
+ 	if (unlikely(netif_msg_intr(eth))) {
+ 		mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
+-		netdev_info(eth->netdev[0],
+-			    "done tx %d, rx %d, intr 0x%08x/0x%x\n",
+-			    tx_done, rx_done, status, mask);
++		dev_info(eth->dev,
++			 "done rx %d, intr 0x%08x/0x%x\n",
++			 rx_done, status, mask);
+ 	}
+ 
+-	if (tx_again || rx_done == budget)
++	if (rx_done == budget)
+ 		return budget;
+ 
+ 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+-	if (status & (tx_intr | rx_intr))
++	if (status & MTK_RX_DONE_INT)
+ 		return budget;
+ 
+ 	napi_complete(napi);
+-	mtk_irq_enable(eth, tx_intr | rx_intr);
++	mtk_irq_enable(eth, MTK_RX_DONE_INT);
+ 
+ 	return rx_done;
+ }
+@@ -1196,22 +1194,43 @@ static void mtk_tx_timeout(struct net_device *dev)
+ 	schedule_work(&eth->pending_work);
+ }
+ 
+-static irqreturn_t mtk_handle_irq(int irq, void *_eth)
++static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
+ {
+ 	struct mtk_eth *eth = _eth;
+ 	u32 status;
+ 
+ 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
++	status &= ~MTK_TX_DONE_INT;
++
+ 	if (unlikely(!status))
+ 		return IRQ_NONE;
+ 
+-	if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
++	if (status & MTK_RX_DONE_INT) {
+ 		if (likely(napi_schedule_prep(&eth->rx_napi)))
+ 			__napi_schedule(&eth->rx_napi);
+-	} else {
+-		mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
++		mtk_irq_disable(eth, MTK_RX_DONE_INT);
+ 	}
+-	mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
++	mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
++
++	return IRQ_HANDLED;
++}
++
++static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
++{
++	struct mtk_eth *eth = _eth;
++	u32 status;
++
++	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
++	status &= ~MTK_RX_DONE_INT;
++
++	if (unlikely(!status))
++		return IRQ_NONE;
++
++	if (status & MTK_TX_DONE_INT) {
++		tasklet_schedule(&eth->tx_clean_tasklet);
++		mtk_irq_disable(eth, MTK_TX_DONE_INT);
++	}
++	mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
+ 
+ 	return IRQ_HANDLED;
+ }
+@@ -1224,7 +1243,7 @@ static void mtk_poll_controller(struct net_device *dev)
+ 	u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
+ 
+ 	mtk_irq_disable(eth, int_mask);
+-	mtk_handle_irq(dev->irq, dev);
++	mtk_handle_irq(dev->irq[0], dev);
+ 	mtk_irq_enable(eth, int_mask);
+ }
+ #endif
+@@ -1345,7 +1364,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ 	/* Enable RX VLan Offloading */
+ 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+ 
+-	err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
++	err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
++			       dev_name(eth->dev), eth);
++	if (err)
++		return err;
++	err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
+ 			       dev_name(eth->dev), eth);
+ 	if (err)
+ 		return err;
+@@ -1361,7 +1384,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ 	mtk_w32(eth, 0, MTK_RST_GL);
+ 
+ 	/* FE int grouping */
+-	mtk_w32(eth, 0, MTK_FE_INT_GRP);
++	mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
++	mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
++	mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
++	mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
++	mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
+ 
+ 	for (i = 0; i < 2; i++) {
+ 		u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
+@@ -1409,7 +1436,9 @@ static void mtk_uninit(struct net_device *dev)
+ 	phy_disconnect(mac->phy_dev);
+ 	mtk_mdio_cleanup(eth);
+ 	mtk_irq_disable(eth, ~0);
+-	free_irq(dev->irq, dev);
++	free_irq(eth->irq[0], dev);
++	free_irq(eth->irq[1], dev);
++	free_irq(eth->irq[2], dev);
+ }
+ 
+ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+@@ -1684,10 +1713,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+ 		dev_err(eth->dev, "error bringing up device\n");
+ 		goto free_netdev;
+ 	}
+-	eth->netdev[id]->irq = eth->irq;
++	eth->netdev[id]->irq = eth->irq[0];
+ 	netif_info(eth, probe, eth->netdev[id],
+ 		   "mediatek frame engine at 0x%08lx, irq %d\n",
+-		   eth->netdev[id]->base_addr, eth->netdev[id]->irq);
++		   eth->netdev[id]->base_addr, eth->irq[0]);
+ 
+ 	return 0;
+ 
+@@ -1704,6 +1733,7 @@ static int mtk_probe(struct platform_device *pdev)
+ 	struct mtk_soc_data *soc;
+ 	struct mtk_eth *eth;
+ 	int err;
++	int i;
+ 
+ 	match = of_match_device(of_mtk_match, &pdev->dev);
+ 	soc = (struct mtk_soc_data *)match->data;
+@@ -1738,10 +1768,12 @@ static int mtk_probe(struct platform_device *pdev)
+ 		return PTR_ERR(eth->rstc);
+ 	}
+ 
+-	eth->irq = platform_get_irq(pdev, 0);
+-	if (eth->irq < 0) {
+-		dev_err(&pdev->dev, "no IRQ resource found\n");
+-		return -ENXIO;
++	for (i = 0; i < 3; i++) {
++		eth->irq[i] = platform_get_irq(pdev, i);
++		if (eth->irq[i] < 0) {
++			dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
++			return -ENXIO;
++		}
+ 	}
+ 
+ 	eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
+@@ -1785,6 +1817,9 @@ static int mtk_probe(struct platform_device *pdev)
+ 	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
+ 		       MTK_NAPI_WEIGHT);
+ 
++	tasklet_init(&eth->tx_clean_tasklet,
++		mtk_clean_tx_tasklet, (unsigned long)eth);
++
+ 	platform_set_drvdata(pdev, eth);
+ 
+ 	return 0;
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index eed626d..4cfb40c 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -68,6 +68,10 @@
+ /* Unicast Filter MAC Address Register - High */
+ #define MTK_GDMA_MAC_ADRH(x)	(0x50C + (x * 0x1000))
+ 
++/* PDMA Interrupt grouping registers */
++#define MTK_PDMA_INT_GRP1	0xa50
++#define MTK_PDMA_INT_GRP2	0xa54
++
+ /* QDMA TX Queue Configuration Registers */
+ #define MTK_QTX_CFG(x)		(0x1800 + (x * 0x10))
+ #define QDMA_RES_THRES		4
+@@ -124,6 +128,11 @@
+ #define MTK_TX_DONE_INT		(MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+ 				 MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
+ 
++/* QDMA Interrupt grouping registers */
++#define MTK_QDMA_INT_GRP1	0x1a20
++#define MTK_QDMA_INT_GRP2	0x1a24
++#define MTK_RLS_DONE_INT	BIT(0)
++
+ /* QDMA Interrupt Status Register */
+ #define MTK_QDMA_INT_MASK	0x1A1C
+ 
+@@ -374,7 +383,7 @@ struct mtk_eth {
+ 	struct net_device		dummy_dev;
+ 	struct net_device		*netdev[MTK_MAX_DEVS];
+ 	struct mtk_mac			*mac[MTK_MAX_DEVS];
+-	int				irq;
++	int				irq[3];
+ 	u32				msg_enable;
+ 	unsigned long			sysclk;
+ 	struct regmap			*ethsys;
+@@ -391,6 +400,7 @@ struct mtk_eth {
+ 	struct clk			*clk_gp2;
+ 	struct mii_bus			*mii_bus;
+ 	struct work_struct		pending_work;
++	struct tasklet_struct		tx_clean_tasklet;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0074-net-mediatek-out-of-tree-fixes.patch b/target/linux/mediatek/patches-4.4/0074-net-mediatek-out-of-tree-fixes.patch
new file mode 100644
index 0000000000..878def40a9
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0074-net-mediatek-out-of-tree-fixes.patch
@@ -0,0 +1,2600 @@
+From 42fa01d00a7d14b4db1ff1e5176469d349e03d8a Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Mon, 21 Mar 2016 16:36:22 +0100
+Subject: [PATCH 74/78] net: mediatek: out of tree fixes
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ arch/arm/boot/dts/mt7623-evb.dts            |    1 -
+ arch/arm/boot/dts/mt7623.dtsi               |   40 +-
+ drivers/net/ethernet/mediatek/Makefile      |    2 +-
+ drivers/net/ethernet/mediatek/gsw_mt7620.h  |  250 +++++++
+ drivers/net/ethernet/mediatek/gsw_mt7623.c  | 1070 +++++++++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mt7530.c      |  808 ++++++++++++++++++++
+ drivers/net/ethernet/mediatek/mt7530.h      |   20 +
+ drivers/net/ethernet/mediatek/mtk_eth_soc.c |  107 ++-
+ drivers/net/ethernet/mediatek/mtk_eth_soc.h |    5 +
+ 9 files changed, 2230 insertions(+), 73 deletions(-)
+ create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7620.h
+ create mode 100644 drivers/net/ethernet/mediatek/gsw_mt7623.c
+ create mode 100644 drivers/net/ethernet/mediatek/mt7530.c
+ create mode 100644 drivers/net/ethernet/mediatek/mt7530.h
+
+diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
+index 5e9381d..bc2b3f1 100644
+--- a/arch/arm/boot/dts/mt7623-evb.dts
++++ b/arch/arm/boot/dts/mt7623-evb.dts
+@@ -425,7 +425,6 @@
+ &usb1 {
+ 	vusb33-supply = <&mt6323_vusb_reg>;
+ 	vbus-supply = <&usb_p1_vbus>;
+-//	mediatek,wakeup-src = <1>;
+ 	status = "okay";
+ };
+ 
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+index c8c802d..f405ec7 100644
+--- a/arch/arm/boot/dts/mt7623.dtsi
++++ b/arch/arm/boot/dts/mt7623.dtsi
+@@ -453,25 +453,32 @@
+ 	};
+ 
+ 	ethsys: syscon@1b000000 {
+-		#address-cells = <1>;
+-		#size-cells = <1>;
+ 		compatible = "mediatek,mt2701-ethsys", "syscon";
+ 		reg = <0 0x1b000000 0 0x1000>;
++		#reset-cells = <1>;
+ 		#clock-cells = <1>;
+ 	};
+ 
+ 	eth: ethernet@1b100000 {
+ 		compatible = "mediatek,mt7623-eth";
+-		reg = <0 0x1b100000 0 0x10000>;
++		reg = <0 0x1b100000 0 0x20000>;
+ 	
+-		clocks = <&topckgen CLK_TOP_ETHIF_SEL>;
+-		clock-names = "ethif";
++		clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
++			 <&ethsys CLK_ETHSYS_ESW>,
++			 <&ethsys CLK_ETHSYS_GP2>,
++			 <&ethsys CLK_ETHSYS_GP1>;
++		clock-names = "ethif", "esw", "gp2", "gp1";
+ 		interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_LOW
+ 			      GIC_SPI 199 IRQ_TYPE_LEVEL_LOW
+ 			      GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>;
+ 		power-domains = <&scpsys MT2701_POWER_DOMAIN_ETH>;
+ 
++		resets = <&ethsys 6>;
++		reset-names = "eth";
++
+ 		mediatek,ethsys = <&ethsys>;
++		mediatek,pctl = <&syscfg_pctl_a>;
++
+ 		mediatek,switch = <&gsw>;
+ 
+ 		#address-cells = <1>;
+@@ -482,7 +489,7 @@
+ 		gmac1: mac@0 {
+ 			compatible = "mediatek,eth-mac";
+ 			reg = <0>;
+-
++			
+ 			status = "disabled";
+ 		};
+ 
+@@ -490,6 +497,7 @@
+ 			compatible = "mediatek,eth-mac";
+ 			reg = <1>;
+ 
++			phy-handle = <&phy5>;
+ 			status = "disabled";
+ 		};
+ 	
+@@ -497,6 +505,16 @@
+ 			#address-cells = <1>;
+ 			#size-cells = <0>;
+ 
++			phy4: ethernet-phy@4 {
++				reg = <4>;
++				phy-mode = "rgmii";
++			};
++
++			phy5: ethernet-phy@5 {
++				reg = <5>;
++				phy-mode = "rgmii";
++			};
++
+ 			phy1f: ethernet-phy@1f {
+ 				reg = <0x1f>;
+ 				phy-mode = "rgmii";
+@@ -506,14 +524,12 @@
+ 
+ 	gsw: switch@1b100000 {
+ 		compatible = "mediatek,mt7623-gsw";
+-		reg = <0 0x1b110000 0 0x300000>;
+ 		interrupt-parent = <&pio>;
+ 		interrupts = <168 IRQ_TYPE_EDGE_RISING>;
+-		clocks = <&apmixedsys CLK_APMIXED_TRGPLL>,
+-			 <&ethsys CLK_ETHSYS_ESW>,
+-			 <&ethsys CLK_ETHSYS_GP2>,
+-			 <&ethsys CLK_ETHSYS_GP1>;
+-		clock-names = "trgpll", "esw", "gp2", "gp1";
++		resets = <&ethsys 2>;
++		reset-names = "eth";
++		clocks = <&apmixedsys CLK_APMIXED_TRGPLL>;
++		clock-names = "trgpll";
+ 		mt7530-supply = <&mt6323_vpa_reg>;
+ 		mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ 		mediatek,ethsys = <&ethsys>;
+diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
+index aa3f1c8..82001c4 100644
+--- a/drivers/net/ethernet/mediatek/Makefile
++++ b/drivers/net/ethernet/mediatek/Makefile
+@@ -2,4 +2,4 @@
+ # Makefile for the Mediatek SoCs built-in ethernet macs
+ #
+ 
+-obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mtk_eth_soc.o
++obj-$(CONFIG_NET_MEDIATEK_SOC)			+= mt7530.o gsw_mt7623.o mtk_eth_soc.o
+diff --git a/drivers/net/ethernet/mediatek/gsw_mt7620.h b/drivers/net/ethernet/mediatek/gsw_mt7620.h
+new file mode 100644
+index 0000000..7013803
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/gsw_mt7620.h
+@@ -0,0 +1,250 @@
++/*   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; version 2 of the License
++ *
++ *   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 the
++ *   GNU General Public License for more details.
++ *
++ *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
++ *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
++ *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
++ */
++
++#ifndef _RALINK_GSW_MT7620_H__
++#define _RALINK_GSW_MT7620_H__
++
++#define GSW_REG_PHY_TIMEOUT	(5 * HZ)
++
++#define MT7620_GSW_REG_PIAC	0x0004
++
++#define GSW_NUM_VLANS		16
++#define GSW_NUM_VIDS		4096
++#define GSW_NUM_PORTS		7
++#define GSW_PORT6		6
++
++#define GSW_MDIO_ACCESS		BIT(31)
++#define GSW_MDIO_READ		BIT(19)
++#define GSW_MDIO_WRITE		BIT(18)
++#define GSW_MDIO_START		BIT(16)
++#define GSW_MDIO_ADDR_SHIFT	20
++#define GSW_MDIO_REG_SHIFT	25
++
++#define GSW_REG_PORT_PMCR(x)	(0x3000 + (x * 0x100))
++#define GSW_REG_PORT_STATUS(x)	(0x3008 + (x * 0x100))
++#define GSW_REG_SMACCR0		0x3fE4
++#define GSW_REG_SMACCR1		0x3fE8
++#define GSW_REG_CKGCR		0x3ff0
++
++#define GSW_REG_IMR		0x7008
++#define GSW_REG_ISR		0x700c
++#define GSW_REG_GPC1		0x7014
++
++#define SYSC_REG_CHIP_REV_ID	0x0c
++#define SYSC_REG_CFG		0x10
++#define SYSC_REG_CFG1		0x14
++#define RST_CTRL_MCM		BIT(2)
++#define SYSC_PAD_RGMII2_MDIO	0x58
++#define SYSC_GPIO_MODE		0x60
++
++#define PORT_IRQ_ST_CHG		0x7f
++
++#define MT7621_ESW_PHY_POLLING	0x0000
++#define MT7620_ESW_PHY_POLLING	0x7000
++
++#define	PMCR_IPG		BIT(18)
++#define	PMCR_MAC_MODE		BIT(16)
++#define	PMCR_FORCE		BIT(15)
++#define	PMCR_TX_EN		BIT(14)
++#define	PMCR_RX_EN		BIT(13)
++#define	PMCR_BACKOFF		BIT(9)
++#define	PMCR_BACKPRES		BIT(8)
++#define	PMCR_RX_FC		BIT(5)
++#define	PMCR_TX_FC		BIT(4)
++#define	PMCR_SPEED(_x)		(_x << 2)
++#define	PMCR_DUPLEX		BIT(1)
++#define	PMCR_LINK		BIT(0)
++
++#define PHY_AN_EN		BIT(31)
++#define PHY_PRE_EN		BIT(30)
++#define PMY_MDC_CONF(_x)	((_x & 0x3f) << 24)
++
++/* ethernet subsystem config register */
++#define ETHSYS_SYSCFG0		0x14
++/* ethernet subsystem clock register */
++#define ETHSYS_CLKCFG0		0x2c
++#define ETHSYS_TRGMII_CLK_SEL362_5	BIT(11)
++
++/* p5 RGMII wrapper TX clock control register */
++#define MT7530_P5RGMIITXCR	0x7b04
++/* p5 RGMII wrapper RX clock control register */
++#define MT7530_P5RGMIIRXCR	0x7b00
++/* TRGMII TDX ODT registers */
++#define MT7530_TRGMII_TD0_ODT	0x7a54
++#define MT7530_TRGMII_TD1_ODT	0x7a5c
++#define MT7530_TRGMII_TD2_ODT	0x7a64
++#define MT7530_TRGMII_TD3_ODT	0x7a6c
++#define MT7530_TRGMII_TD4_ODT	0x7a74
++#define MT7530_TRGMII_TD5_ODT	0x7a7c
++/* TRGMII TCK ctrl register */
++#define MT7530_TRGMII_TCK_CTRL	0x7a78
++/* TRGMII Tx ctrl register */
++#define MT7530_TRGMII_TXCTRL	0x7a40
++/* port 6 extended control register */
++#define MT7530_P6ECR            0x7830
++/* IO driver control register */
++#define MT7530_IO_DRV_CR	0x7810
++/* top signal control register */
++#define MT7530_TOP_SIG_CTRL	0x7808
++/* modified hwtrap register */
++#define MT7530_MHWTRAP		0x7804
++/* hwtrap status register */
++#define MT7530_HWTRAP		0x7800
++/* status interrupt register */
++#define MT7530_SYS_INT_STS	0x700c
++/* system nterrupt register */
++#define MT7530_SYS_INT_EN	0x7008
++/* system control register */
++#define MT7530_SYS_CTRL		0x7000
++/* port MAC status register */
++#define MT7530_PMSR_P(x)	(0x3008 + (x * 0x100))
++/* port MAC control register */
++#define MT7530_PMCR_P(x)	(0x3000 + (x * 0x100))
++
++#define MT7621_XTAL_SHIFT	6
++#define MT7621_XTAL_MASK	0x7
++#define MT7621_XTAL_25		6
++#define MT7621_XTAL_40		3
++#define MT7621_MDIO_DRV_MASK	(3 << 4)
++#define MT7621_GE1_MODE_MASK	(3 << 12)
++
++#define TRGMII_TXCTRL_TXC_INV	BIT(30)
++#define P6ECR_INTF_MODE_RGMII	BIT(1)
++#define P5RGMIIRXCR_C_ALIGN	BIT(8)
++#define P5RGMIIRXCR_DELAY_2	BIT(1)
++#define P5RGMIITXCR_DELAY_2	(BIT(8) | BIT(2))
++
++/* TOP_SIG_CTRL bits */
++#define TOP_SIG_CTRL_NORMAL	(BIT(17) | BIT(16))
++
++/* MHWTRAP bits */
++#define MHWTRAP_MANUAL		BIT(16)
++#define MHWTRAP_P5_MAC_SEL	BIT(13)
++#define MHWTRAP_P6_DIS		BIT(8)
++#define MHWTRAP_P5_RGMII_MODE	BIT(7)
++#define MHWTRAP_P5_DIS		BIT(6)
++#define MHWTRAP_PHY_ACCESS	BIT(5)
++
++/* HWTRAP bits */
++#define HWTRAP_XTAL_SHIFT	9
++#define HWTRAP_XTAL_MASK	0x3
++
++/* SYS_CTRL bits */
++#define SYS_CTRL_SW_RST		BIT(1)
++#define SYS_CTRL_REG_RST	BIT(0)
++
++/* PMCR bits */
++#define PMCR_IFG_XMIT_96	BIT(18)
++#define PMCR_MAC_MODE		BIT(16)
++#define PMCR_FORCE_MODE		BIT(15)
++#define PMCR_TX_EN		BIT(14)
++#define PMCR_RX_EN		BIT(13)
++#define PMCR_BACK_PRES_EN	BIT(9)
++#define PMCR_BACKOFF_EN		BIT(8)
++#define PMCR_TX_FC_EN		BIT(5)
++#define PMCR_RX_FC_EN		BIT(4)
++#define PMCR_FORCE_SPEED_1000	BIT(3)
++#define PMCR_FORCE_FDX		BIT(1)
++#define PMCR_FORCE_LNK		BIT(0)
++#define PMCR_FIXED_LINK		(PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
++				 PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
++				 PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
++				 PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
++				 PMCR_FORCE_LNK)
++
++#define PMCR_FIXED_LINK_FC	(PMCR_FIXED_LINK | \
++				 PMCR_TX_FC_EN | PMCR_RX_FC_EN)
++
++/* TRGMII control registers */
++#define GSW_INTF_MODE		0x390
++#define GSW_TRGMII_TD0_ODT	0x354
++#define GSW_TRGMII_TD1_ODT	0x35c
++#define GSW_TRGMII_TD2_ODT	0x364
++#define GSW_TRGMII_TD3_ODT	0x36c
++#define GSW_TRGMII_TXCTL_ODT	0x374
++#define GSW_TRGMII_TCK_ODT	0x37c
++#define GSW_TRGMII_RCK_CTRL	0x300
++
++#define INTF_MODE_TRGMII	BIT(1)
++#define TRGMII_RCK_CTRL_RX_RST	BIT(31)
++
++
++/* possible XTAL speed */
++#define	MT7623_XTAL_40		0
++#define MT7623_XTAL_20		1
++#define MT7623_XTAL_25		3
++
++/* GPIO port control registers */
++#define	GPIO_OD33_CTRL8		0x4c0
++#define	GPIO_BIAS_CTRL		0xed0
++#define GPIO_DRV_SEL10		0xf00
++
++/* on MT7620 the functio of port 4 can be software configured */
++enum {
++	PORT4_EPHY = 0,
++	PORT4_EXT,
++};
++
++/* struct mt7620_gsw -	the structure that holds the SoC specific data
++ * @dev:		The Device struct
++ * @base:		The base address
++ * @piac_offset:	The PIAC base may change depending on SoC
++ * @irq:		The IRQ we are using
++ * @port4:		The port4 mode on MT7620
++ * @autopoll:		Is MDIO autopolling enabled
++ * @ethsys:		The ethsys register map
++ * @pctl:		The pin control register map
++ * @clk_trgpll:		The trgmii pll clock
++ */
++struct mt7620_gsw {
++	struct mtk_eth		*eth;
++	struct device		*dev;
++	void __iomem		*base;
++	u32			piac_offset;
++	int			irq;
++	int			port4;
++	unsigned long int	autopoll;
++
++	struct regmap		*ethsys;
++	struct regmap		*pctl;
++
++	struct clk		*clk_trgpll;
++
++	int			trgmii_force;
++};
++
++/* switch register I/O wrappers */
++void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
++u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
++
++/* the callback used by the driver core to bringup the switch */
++int mtk_gsw_init(struct mtk_eth *eth);
++
++/* MDIO access wrappers */
++int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
++int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
++void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
++int mt7620_has_carrier(struct mtk_eth *eth);
++void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
++			     int speed, int duplex);
++void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
++u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
++void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
++
++u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
++		      u32 phy_register, u32 write_data);
++u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg);
++void mt7620_handle_carrier(struct mtk_eth *eth);
++
++#endif
+diff --git a/drivers/net/ethernet/mediatek/gsw_mt7623.c b/drivers/net/ethernet/mediatek/gsw_mt7623.c
+new file mode 100644
+index 0000000..873a525
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/gsw_mt7623.c
+@@ -0,0 +1,1070 @@
++/*   This program is free software; you can redistribute it and/or modify
++ *   it under the terms of the GNU General Public License as published by
++ *   the Free Software Foundation; version 2 of the License
++ *
++ *   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 the
++ *   GNU General Public License for more details.
++ *
++ *   Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
++ *   Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
++ *   Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/platform_device.h>
++#include <linux/of_device.h>
++#include <linux/of_irq.h>
++#include <linux/of_gpio.h>
++#include <linux/clk.h>
++#include <linux/mfd/syscon.h>
++#include <linux/regulator/consumer.h>
++#include <linux/pm_runtime.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++#include <linux/mii.h>
++#include <linux/interrupt.h>
++#include <linux/netdevice.h>
++#include <linux/dma-mapping.h>
++#include <linux/phy.h>
++#include <linux/ethtool.h>
++#include <linux/version.h>
++#include <linux/atomic.h>
++
++#include "mtk_eth_soc.h"
++#include "gsw_mt7620.h"
++#include "mt7530.h"
++
++#define ETHSYS_CLKCFG0			0x2c
++#define ETHSYS_TRGMII_CLK_SEL362_5	BIT(11)
++
++void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
++{
++	_mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++	_mtk_mdio_write(gsw->eth, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
++	_mtk_mdio_write(gsw->eth, 0x1f, 0x10, val >> 16);
++}
++
++u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
++{
++	u16 high, low;
++
++	_mtk_mdio_write(gsw->eth, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++	low = _mtk_mdio_read(gsw->eth, 0x1f, (reg >> 2) & 0xf);
++	high = _mtk_mdio_read(gsw->eth, 0x1f, 0x10);
++
++	return (high << 16) | (low & 0xffff);
++}
++
++void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
++{
++	u32 val = mt7530_mdio_r32(gsw, reg);
++
++	val &= mask;
++	val |= set;
++	mt7530_mdio_w32(gsw, reg, val);
++}
++
++void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
++{
++	mtk_w32(gsw->eth, val, reg + 0x10000);
++}
++
++u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
++{
++	return mtk_r32(gsw->eth, reg + 0x10000);
++}
++
++void mtk_switch_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, unsigned reg)
++{
++	u32 val = mtk_switch_r32(gsw, reg);
++
++	val &= mask;
++	val |= set;
++
++	mtk_switch_w32(gsw, val, reg);
++}
++
++int mt7623_gsw_config(struct mtk_eth *eth)
++{
++	if (eth->mii_bus && eth->mii_bus->phy_map[0x1f])
++		mt7530_probe(eth->dev, NULL, eth->mii_bus, 1);
++
++	return 0;
++}
++
++static irqreturn_t gsw_interrupt_mt7623(int irq, void *_eth)
++{
++	struct mtk_eth *eth = (struct mtk_eth *)_eth;
++	struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
++	u32 reg, i;
++
++	reg = mt7530_mdio_r32(gsw, 0x700c);
++
++	for (i = 0; i < 5; i++)
++		if (reg & BIT(i)) {
++			unsigned int link;
++
++			link = mt7530_mdio_r32(gsw,
++					       0x3008 + (i * 0x100)) & 0x1;
++
++			if (link)
++				dev_info(gsw->dev,
++					 "port %d link up\n", i);
++			else
++				dev_info(gsw->dev,
++					 "port %d link down\n", i);
++		}
++
++//	mt7620_handle_carrier(eth);
++	mt7530_mdio_w32(gsw, 0x700c, 0x1f);
++
++	return IRQ_HANDLED;
++}
++
++static void wait_loop(struct mt7620_gsw *gsw)
++{
++	int i;
++	int read_data;
++
++	for (i = 0; i < 320; i = i + 1)
++		read_data = mtk_switch_r32(gsw, 0x610);
++}
++
++static void trgmii_calibration_7623(struct mt7620_gsw *gsw)
++{
++
++	unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };	/* minumum delay for all correct */
++	unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };	/* maximum delay for all correct */
++	unsigned int final_tap[5];
++	unsigned int rxc_step_size;
++	unsigned int rxd_step_size;
++	unsigned int read_data;
++	unsigned int tmp;
++	unsigned int rd_wd;
++	int i;
++	unsigned int err_cnt[5];
++	unsigned int init_toggle_data;
++	unsigned int err_flag[5];
++	unsigned int err_total_flag;
++	unsigned int training_word;
++	unsigned int rd_tap;
++	u32 val;
++
++	u32 TRGMII_7623_base;
++	u32 TRGMII_7623_RD_0;
++	u32 TRGMII_RCK_CTRL;
++
++	TRGMII_7623_base = 0x300;	/* 0xFB110300 */
++	TRGMII_7623_RD_0 = TRGMII_7623_base + 0x10;
++	TRGMII_RCK_CTRL = TRGMII_7623_base;
++	rxd_step_size = 0x1;
++	rxc_step_size = 0x4;
++	init_toggle_data = 0x00000055;
++	training_word = 0x000000AC;
++
++	/* RX clock gating in MT7623 */
++	mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
++
++	/* Assert RX  reset in MT7623 */
++	mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_base + 0x00);
++
++	/* Set TX OE edge in  MT7623 */
++	mtk_switch_m32(gsw, 0, 0x00002000, TRGMII_7623_base + 0x78);
++
++	/* Disable RX clock gating in MT7623 */
++	mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
++
++	/* Release RX reset in MT7623 */
++	mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base);
++
++	for (i = 0; i < 5; i++)
++		mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
++
++	pr_err("Enable Training Mode in MT7530\n");
++	read_data = mt7530_mdio_r32(gsw, 0x7A40);
++	read_data |= 0xC0000000;
++	mt7530_mdio_w32(gsw, 0x7A40, read_data);	/* Enable Training Mode in MT7530 */
++	err_total_flag = 0;
++	pr_err("Adjust RXC delay in MT7623\n");
++	read_data = 0x0;
++	while (err_total_flag == 0 && read_data != 0x68) {
++		pr_err("2nd Enable EDGE CHK in MT7623\n");
++		/* Enable EDGE CHK in MT7623 */
++		for (i = 0; i < 5; i++)
++			    mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++
++		wait_loop(gsw);
++		err_total_flag = 1;
++		for (i = 0; i < 5; i++) {
++			err_cnt[i] =
++			    mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 8;
++			err_cnt[i] &= 0x0000000f;
++			rd_wd = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) >> 16;
++			rd_wd &= 0x000000ff;
++			val = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++			pr_err("ERR_CNT = %d, RD_WD =%x, TRGMII_7623_RD_0=%x\n",
++			       err_cnt[i], rd_wd, val);
++			if (err_cnt[i] != 0) {
++				err_flag[i] = 1;
++			} else if (rd_wd != 0x55) {
++				err_flag[i] = 1;
++			} else {
++				err_flag[i] = 0;
++			}
++			err_total_flag = err_flag[i] & err_total_flag;
++		}
++
++		pr_err("2nd Disable EDGE CHK in MT7623\n");
++		/* Disable EDGE CHK in MT7623 */
++		for (i = 0; i < 5; i++)
++			    mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++		wait_loop(gsw);
++		pr_err("2nd Disable EDGE CHK in MT7623\n");
++		/* Adjust RXC delay */
++		/* RX clock gating in MT7623 */
++		mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x04);
++		read_data = mtk_switch_r32(gsw, TRGMII_7623_base);
++		if (err_total_flag == 0) {
++			tmp = (read_data & 0x0000007f) + rxc_step_size;
++			pr_err(" RXC delay = %d\n", tmp); 
++			read_data >>= 8;
++			read_data &= 0xffffff80;
++			read_data |= tmp;
++			read_data <<= 8;
++			read_data &= 0xffffff80;
++			read_data |= tmp;
++			mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
++		} else {
++			tmp = (read_data & 0x0000007f) + 16;
++			pr_err(" RXC delay = %d\n", tmp); 
++			read_data >>= 8;
++			read_data &= 0xffffff80;
++			read_data |= tmp;
++			read_data <<= 8;
++			read_data &= 0xffffff80;
++			read_data |= tmp;
++			mtk_switch_w32(gsw, read_data, TRGMII_7623_base);
++		}
++		read_data &= 0x000000ff;
++
++		/* Disable RX clock gating in MT7623 */
++		mtk_switch_m32(gsw, 0, 0xC0000000, TRGMII_7623_base + 0x04);
++		for (i = 0; i < 5; i++)
++			mtk_switch_m32(gsw, 0, 0x80000000, TRGMII_7623_RD_0 + i * 8);
++	}
++
++	/* Read RD_WD MT7623 */
++	for (i = 0; i < 5; i++) {
++		rd_tap = 0;
++		while (err_flag[i] != 0 && rd_tap != 128) {
++			/* Enable EDGE CHK in MT7623 */
++			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++			wait_loop(gsw);
++
++			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++			err_cnt[i] = (read_data >> 8) & 0x0000000f;	/* Read MT7623 Errcnt */
++			rd_wd = (read_data >> 16) & 0x000000ff;
++			if (err_cnt[i] != 0 || rd_wd != 0x55) {
++				err_flag[i] = 1;
++			} else {
++				err_flag[i] = 0;
++			}
++			/* Disable EDGE CHK in MT7623 */
++			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++			wait_loop(gsw);
++			if (err_flag[i] != 0) {
++				rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7623 */
++				read_data = (read_data & 0xffffff80) | rd_tap;
++				mtk_switch_w32(gsw, read_data,
++					TRGMII_7623_RD_0 + i * 8);
++				tap_a[i] = rd_tap;
++			} else {
++				rd_tap = (read_data & 0x0000007f) + 48;
++				read_data = (read_data & 0xffffff80) | rd_tap;
++				mtk_switch_w32(gsw, read_data,
++					TRGMII_7623_RD_0 + i * 8);
++			}
++
++		}
++		pr_err("MT7623 %dth bit  Tap_a = %d\n", i, tap_a[i]);
++	}
++	/* pr_err("Last While Loop\n"); */
++	for (i = 0; i < 5; i++) {
++		while ((err_flag[i] == 0) && (rd_tap != 128)) {
++			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++			rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7623 */
++			read_data = (read_data & 0xffffff80) | rd_tap;
++			mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
++			/* Enable EDGE CHK in MT7623 */
++			val =
++			    mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8) | 0x40000000;
++			val &= 0x4fffffff;
++			mtk_switch_w32(gsw, val, TRGMII_7623_RD_0 + i * 8);
++			wait_loop(gsw);
++			read_data = mtk_switch_r32(gsw, TRGMII_7623_RD_0 + i * 8);
++			err_cnt[i] = (read_data >> 8) & 0x0000000f;	/* Read MT7623 Errcnt */
++			rd_wd = (read_data >> 16) & 0x000000ff;
++			if (err_cnt[i] != 0 || rd_wd != 0x55) {
++				err_flag[i] = 1;
++			} else {
++				err_flag[i] = 0;
++			}
++
++			/* Disable EDGE CHK in MT7623 */
++			mtk_switch_m32(gsw, 0x4fffffff, 0x40000000, TRGMII_7623_RD_0 + i * 8);
++			wait_loop(gsw);
++
++		}
++
++		tap_b[i] = rd_tap;	/* -rxd_step_size; */
++		pr_err("MT7623 %dth bit  Tap_b = %d\n", i, tap_b[i]);
++		final_tap[i] = (tap_a[i] + tap_b[i]) / 2;	/* Calculate RXD delay = (TAP_A + TAP_B)/2 */
++		read_data = (read_data & 0xffffff80) | final_tap[i];
++		mtk_switch_w32(gsw, read_data, TRGMII_7623_RD_0 + i * 8);
++	}
++
++	read_data = mt7530_mdio_r32(gsw, 0x7A40);
++	read_data &= 0x3fffffff;
++	mt7530_mdio_w32(gsw, 0x7A40, read_data);
++}
++
++static void trgmii_calibration_7530(struct mt7620_gsw *gsw)
++{
++
++	unsigned int tap_a[5] = { 0, 0, 0, 0, 0 };
++	unsigned int tap_b[5] = { 0, 0, 0, 0, 0 };
++	unsigned int final_tap[5];
++	unsigned int rxc_step_size;
++	unsigned int rxd_step_size;
++	unsigned int read_data;
++	unsigned int tmp = 0;
++	int i;
++	unsigned int err_cnt[5];
++	unsigned int rd_wd;
++	unsigned int init_toggle_data;
++	unsigned int err_flag[5];
++	unsigned int err_total_flag;
++	unsigned int training_word;
++	unsigned int rd_tap;
++
++	u32 TRGMII_7623_base;
++	u32 TRGMII_7530_RD_0;
++	u32 TRGMII_RCK_CTRL;
++	u32 TRGMII_7530_base;
++	u32 TRGMII_7530_TX_base;
++	u32 val;
++
++	TRGMII_7623_base = 0x300;
++	TRGMII_7530_base = 0x7A00;
++	TRGMII_7530_RD_0 = TRGMII_7530_base + 0x10;
++	TRGMII_RCK_CTRL = TRGMII_7623_base;
++	rxd_step_size = 0x1;
++	rxc_step_size = 0x8;
++	init_toggle_data = 0x00000055;
++	training_word = 0x000000AC;
++
++	TRGMII_7530_TX_base = TRGMII_7530_base + 0x50;
++
++	/* pr_err("Calibration begin ........\n"); */
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++	read_data = mt7530_mdio_r32(gsw, 0x7a10);
++	/* pr_err("TRGMII_7530_RD_0 is %x\n", read_data); */
++
++	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++	read_data &= 0x3fffffff;
++	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* RX clock gating in MT7530 */
++
++	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x78);
++	read_data |= 0x00002000;
++	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x78, read_data);	/* Set TX OE edge in  MT7530 */
++
++	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++	read_data |= 0x80000000;
++	mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Assert RX  reset in MT7530 */
++
++	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++	read_data &= 0x7fffffff;
++	mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Release RX reset in MT7530 */
++
++	read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++	read_data |= 0xC0000000;
++	mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* Disable RX clock gating in MT7530 */
++
++	/* pr_err("Enable Training Mode in MT7623\n"); */
++	/*Enable Training Mode in MT7623 */
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++	if (gsw->trgmii_force == 2000) {
++		val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0xC0000000;
++		mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++	} else {
++		val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x40) | 0x80000000;
++		mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x40);
++	}
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x078) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x078);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x50) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x50);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x58) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x58);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x60) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x60);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x68) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x68);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x70) & 0xfffff0ff;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x70);
++	val = mtk_switch_r32(gsw, TRGMII_7623_base + 0x78) & 0x00000800;
++	mtk_switch_w32(gsw, val, TRGMII_7623_base + 0x78);
++	err_total_flag = 0;
++	/* pr_err("Adjust RXC delay in MT7530\n"); */
++	read_data = 0x0;
++	while (err_total_flag == 0 && (read_data != 0x68)) {
++		/* pr_err("2nd Enable EDGE CHK in MT7530\n"); */
++		/* Enable EDGE CHK in MT7530 */
++		for (i = 0; i < 5; i++) {
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++			/* pr_err("2nd Disable EDGE CHK in MT7530\n"); */
++			err_cnt[i] =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			/* pr_err("***** MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
++			/* pr_err("MT7530 %dth bit ERR_CNT =%x\n",i, err_cnt[i]); */
++			err_cnt[i] >>= 8;
++			err_cnt[i] &= 0x0000ff0f;
++			rd_wd = err_cnt[i] >> 8;
++			rd_wd &= 0x000000ff;
++			err_cnt[i] &= 0x0000000f;
++			/* read_data = mt7530_mdio_r32(gsw,0x7a10,&read_data); */
++			if (err_cnt[i] != 0) {
++				err_flag[i] = 1;
++			} else if (rd_wd != 0x55) {
++				err_flag[i] = 1;
++			} else {
++				err_flag[i] = 0;
++			}
++			if (i == 0) {
++				err_total_flag = err_flag[i];
++			} else {
++				err_total_flag = err_flag[i] & err_total_flag;
++			}
++			/* Disable EDGE CHK in MT7530 */
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++		}
++		/*Adjust RXC delay */
++		if (err_total_flag == 0) {
++			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++			read_data |= 0x80000000;
++			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Assert RX  reset in MT7530 */
++
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++			read_data &= 0x3fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* RX clock gating in MT7530 */
++
++			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++			tmp = read_data;
++			tmp &= 0x0000007f;
++			tmp += rxc_step_size;
++			/* pr_err("Current rxc delay = %d\n", tmp); */
++			read_data &= 0xffffff80;
++			read_data |= tmp;
++			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);
++			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++			/* pr_err("Current RXC delay = %x\n", read_data); */
++
++			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_base);
++			read_data &= 0x7fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_base, read_data);	/* Release RX reset in MT7530 */
++
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_base + 0x04);
++			read_data |= 0xc0000000;
++			mt7530_mdio_w32(gsw, TRGMII_7530_base + 0x04, read_data);	/* Disable RX clock gating in MT7530 */
++			pr_err("####### MT7530 RXC delay is %d\n", tmp);
++		}
++		read_data = tmp;
++	}
++	pr_err("Finish RXC Adjustment while loop\n");
++
++	/* pr_err("Read RD_WD MT7530\n"); */
++	/* Read RD_WD MT7530 */
++	for (i = 0; i < 5; i++) {
++		rd_tap = 0;
++		while (err_flag[i] != 0 && rd_tap != 128) {
++			/* Enable EDGE CHK in MT7530 */
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++			err_cnt[i] = (read_data >> 8) & 0x0000000f;
++			rd_wd = (read_data >> 16) & 0x000000ff;
++			if (err_cnt[i] != 0 || rd_wd != 0x55) {
++				err_flag[i] = 1;
++			} else {
++				err_flag[i] = 0;
++			}
++			if (err_flag[i] != 0) {
++				rd_tap = (read_data & 0x0000007f) + rxd_step_size;	/* Add RXD delay in MT7530 */
++				read_data = (read_data & 0xffffff80) | rd_tap;
++				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++						read_data);
++				tap_a[i] = rd_tap;
++			} else {
++				tap_a[i] = (read_data & 0x0000007f);	/* Record the min delay TAP_A */
++				rd_tap = tap_a[i] + 0x4;
++				read_data = (read_data & 0xffffff80) | rd_tap;
++				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++						read_data);
++			}
++
++			/* Disable EDGE CHK in MT7530 */
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++
++		}
++		pr_err("MT7530 %dth bit  Tap_a = %d\n", i, tap_a[i]);
++	}
++
++	/* pr_err("Last While Loop\n"); */
++	for (i = 0; i < 5; i++) {
++		rd_tap = 0;
++		while (err_flag[i] == 0 && (rd_tap != 128)) {
++			/* Enable EDGE CHK in MT7530 */
++			read_data = mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++			err_cnt[i] = (read_data >> 8) & 0x0000000f;
++			rd_wd = (read_data >> 16) & 0x000000ff;
++			if (err_cnt[i] != 0 || rd_wd != 0x55)
++				err_flag[i] = 1;
++			else
++				err_flag[i] = 0;
++
++			if (err_flag[i] == 0 && (rd_tap != 128)) {
++				/* Add RXD delay in MT7530 */
++				rd_tap = (read_data & 0x0000007f) + rxd_step_size;
++				read_data = (read_data & 0xffffff80) | rd_tap;
++				mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++						read_data);
++			}
++			/* Disable EDGE CHK in MT7530 */
++			read_data =
++			    mt7530_mdio_r32(gsw, TRGMII_7530_RD_0 + i * 8);
++			read_data |= 0x40000000;
++			read_data &= 0x4fffffff;
++			mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8,
++					read_data);
++			wait_loop(gsw);
++		}
++		tap_b[i] = rd_tap;	/* - rxd_step_size; */
++		pr_err("MT7530 %dth bit  Tap_b = %d\n", i, tap_b[i]);
++		/* Calculate RXD delay = (TAP_A + TAP_B)/2 */
++		final_tap[i] = (tap_a[i] + tap_b[i]) / 2;	
++		/* pr_err("########****** MT7530 %dth bit Final Tap = %d\n", i, final_tap[i]); */
++
++		read_data = (read_data & 0xffffff80) | final_tap[i];
++		mt7530_mdio_w32(gsw, TRGMII_7530_RD_0 + i * 8, read_data);
++	}
++
++	if (gsw->trgmii_force == 2000)
++		mtk_switch_m32(gsw, 0x7fffffff, 0, TRGMII_7623_base + 0x40);
++	else
++		mtk_switch_m32(gsw, 0x3fffffff, 0, TRGMII_7623_base + 0x40);
++
++}
++
++static void mt7530_trgmii_clock_setting(struct mt7620_gsw *gsw, u32 xtal_mode)
++{
++
++	u32 regValue;
++
++	/* TRGMII Clock */
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x1);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x404);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++
++	if (xtal_mode == 1) {
++		/* 25MHz */
++		if (gsw->trgmii_force == 2600)
++			/* 325MHz */
++			_mtk_mdio_write(gsw->eth, 0, 14, 0x1a00);
++		else if (gsw->trgmii_force == 2000)
++			/* 250MHz */
++			_mtk_mdio_write(gsw->eth, 0, 14, 0x1400);
++	} else if (xtal_mode == 2) {
++		/* 40MHz */
++		if (gsw->trgmii_force == 2600)
++			/* 325MHz */
++			_mtk_mdio_write(gsw->eth, 0, 14, 0x1040);
++		else if (gsw->trgmii_force == 2000)
++			/* 250MHz */
++			_mtk_mdio_write(gsw->eth, 0, 14, 0x0c80);
++	}
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x405);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x0);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x409);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	if (xtal_mode == 1)
++		/* 25MHz */
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
++	else
++		/* 40MHz */
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x40a);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	if (xtal_mode == 1)
++		/* 25MHz */
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x0057);
++	else
++		/* 40MHz */
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x0087);
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x403);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x1800);
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x403);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x1c00);
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x401);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0xc020);
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x406);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0xa030);
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x406);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0xa038);
++
++//	udelay(120);		/* for MT7623 bring up test */
++
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++	_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x3);
++
++	regValue = mt7530_mdio_r32(gsw, 0x7830);
++	regValue &= 0xFFFFFFFC;
++	regValue |= 0x00000001;
++	mt7530_mdio_w32(gsw, 0x7830, regValue);
++
++	regValue = mt7530_mdio_r32(gsw, 0x7a40);
++	regValue &= ~(0x1 << 30);
++	regValue &= ~(0x1 << 28);
++	mt7530_mdio_w32(gsw, 0x7a40, regValue);
++
++	mt7530_mdio_w32(gsw, 0x7a78, 0x55);
++//	udelay(100);		/* for mt7623 bring up test */
++
++	mtk_switch_m32(gsw, 0x7fffffff, 0, 0x300);
++
++	trgmii_calibration_7623(gsw);
++	trgmii_calibration_7530(gsw);
++
++	mtk_switch_m32(gsw, 0, 0x80000000, 0x300);
++	mtk_switch_m32(gsw, 0, 0x7fffffff, 0x300);
++
++	/*MT7530 RXC reset */
++	regValue = mt7530_mdio_r32(gsw, 0x7a00);
++	regValue |= (0x1 << 31);
++	mt7530_mdio_w32(gsw, 0x7a00, regValue);
++	mdelay(1);
++	regValue &= ~(0x1 << 31);
++	mt7530_mdio_w32(gsw, 0x7a00, regValue);
++	mdelay(100);
++}
++
++static void mt7623_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw, struct device_node *np)
++{
++       u32     i;
++       u32     val;
++       u32     xtal_mode;
++
++	regmap_update_bits(gsw->ethsys, ETHSYS_CLKCFG0,
++			   ETHSYS_TRGMII_CLK_SEL362_5,
++			   ETHSYS_TRGMII_CLK_SEL362_5);
++
++	/* reset the TRGMII core */
++	mtk_switch_m32(gsw, 0, INTF_MODE_TRGMII, GSW_INTF_MODE);
++	mtk_switch_m32(gsw, 0, TRGMII_RCK_CTRL_RX_RST, GSW_TRGMII_RCK_CTRL);
++
++	/* Hardware reset Switch */
++	device_reset(eth->dev);
++
++	/* Wait for Switch Reset Completed*/
++	for (i = 0; i < 100; i++) {
++		mdelay(10);
++		if (mt7530_mdio_r32(gsw, MT7530_HWTRAP))
++			break;
++	}
++
++	/* turn off all PHYs */
++	for (i = 0; i <= 4; i++) {
++		val = _mtk_mdio_read(gsw->eth, i, 0x0);
++		val |= BIT(11);
++		_mtk_mdio_write(gsw->eth, i, 0x0, val);
++	}
++
++	/* reset the switch */
++	mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
++			SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
++	udelay(100);
++
++	/* GE1, Force 1000M/FD, FC ON */
++	mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
++
++	/* GE2, Force 1000M/FD, FC ON */
++	mt7530_mdio_w32(gsw, MT7530_PMCR_P(5), PMCR_FIXED_LINK_FC);
++
++	/* Enable Port 6, P5 as GMAC5, P5 disable */
++	val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
++	/* Enable Port 6 */
++	val &= ~MHWTRAP_P6_DIS;
++	/* Enable Port 5 */
++	val &= ~MHWTRAP_P5_DIS;
++	/* Port 5 as GMAC */
++	val |= MHWTRAP_P5_MAC_SEL;
++	/* Port 5 Interface mode */
++	val |= MHWTRAP_P5_RGMII_MODE;
++	/* Set MT7530 phy direct access mode**/
++	val &= ~MHWTRAP_PHY_ACCESS;
++	val |= MHWTRAP_PHY_ACCESS;
++	/* manual override of HW-Trap */
++	val |= MHWTRAP_MANUAL;
++	mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
++
++	val = mt7530_mdio_r32(gsw, 0x7800);
++	val = (val >> 9) & 0x3;
++	pr_err("!!%s: Mhz value= %d\n", __func__, val);
++	if (val == 0x3) {
++		xtal_mode = 1;
++		/* 25Mhz Xtal - do nothing */
++	} else if (val == 0x2) {
++		/* 40Mhz */
++		xtal_mode = 2;
++
++		/* disable MT7530 core clock */
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x0);
++
++		/* disable MT7530 PLL */
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x2020);
++
++		/* for MT7530 core clock = 500Mhz */
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x40e);
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x119);
++
++		/* enable MT7530 PLL */
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x40d);
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x2820);
++
++		udelay(20);
++
++		/* enable MT7530 core clock */
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x1f);
++		_mtk_mdio_write(gsw->eth, 0, 14, 0x410);
++		_mtk_mdio_write(gsw->eth, 0, 13, 0x401f);
++	} else {
++		xtal_mode = 3;
++		/* 20Mhz Xtal - TODO */
++	}
++
++	/* RGMII */
++	_mtk_mdio_write(gsw->eth, 0, 14, 0x1);
++
++	/* set MT7530 central align */
++	val = mt7530_mdio_r32(gsw, 0x7830);
++	val &= ~1;
++	val |= 1<<1;
++	mt7530_mdio_w32(gsw, 0x7830, val);
++
++	val = mt7530_mdio_r32(gsw, 0x7a40);
++	val &= ~(1<<30);
++	mt7530_mdio_w32(gsw, 0x7a40, val);
++
++	mt7530_mdio_w32(gsw, 0x7a78, 0x855);
++
++	/* delay setting for 10/1000M */
++	mt7530_mdio_w32(gsw, 0x7b00, 0x104);
++	mt7530_mdio_w32(gsw, 0x7b04, 0x10);
++
++	/* lower Tx Driving */
++	mt7530_mdio_w32(gsw, 0x7a54, 0x88);
++	mt7530_mdio_w32(gsw, 0x7a5c, 0x88);
++	mt7530_mdio_w32(gsw, 0x7a64, 0x88);
++	mt7530_mdio_w32(gsw, 0x7a6c, 0x88);
++	mt7530_mdio_w32(gsw, 0x7a74, 0x88);
++	mt7530_mdio_w32(gsw, 0x7a7c, 0x88);
++	mt7530_mdio_w32(gsw, 0x7810, 0x11);
++
++	/* Set MT7623/MT7683 TX Driving */
++	mtk_switch_w32(gsw, 0x88, 0x354);
++	mtk_switch_w32(gsw, 0x88, 0x35c);
++	mtk_switch_w32(gsw, 0x88, 0x364);
++	mtk_switch_w32(gsw, 0x88, 0x36c);
++	mtk_switch_w32(gsw, 0x88, 0x374);
++	mtk_switch_w32(gsw, 0x88, 0x37c);
++
++	/* Set GE2 driving and slew rate */
++	regmap_write(gsw->pctl, 0xF00, 0xe00);
++	/* set GE2 TDSEL */
++	regmap_write(gsw->pctl, 0x4C0, 0x5);
++	/* set GE2 TUNE */
++	regmap_write(gsw->pctl, 0xED0, 0x0);
++
++
++	mt7530_trgmii_clock_setting(gsw, xtal_mode);
++
++	//LANWANPartition(gsw);
++
++	/* disable EEE */
++	for (i = 0; i <= 4; i++) {
++		_mtk_mdio_write(gsw->eth, i, 13, 0x7);
++		_mtk_mdio_write(gsw->eth, i, 14, 0x3C);
++		_mtk_mdio_write(gsw->eth, i, 13, 0x4007);
++		_mtk_mdio_write(gsw->eth, i, 14, 0x0);
++
++		/* Increase SlvDPSready time */
++		_mtk_mdio_write(gsw->eth, i, 31, 0x52b5);
++		_mtk_mdio_write(gsw->eth, i, 16, 0xafae);
++		_mtk_mdio_write(gsw->eth, i, 18, 0x2f);
++		_mtk_mdio_write(gsw->eth, i, 16, 0x8fae);
++
++		/* Incease post_update_timer */
++		_mtk_mdio_write(gsw->eth, i, 31, 0x3);
++		_mtk_mdio_write(gsw->eth, i, 17, 0x4b);
++
++		/* Adjust 100_mse_threshold */
++		_mtk_mdio_write(gsw->eth, i, 13, 0x1e);
++		_mtk_mdio_write(gsw->eth, i, 14, 0x123);
++		_mtk_mdio_write(gsw->eth, i, 13, 0x401e);
++		_mtk_mdio_write(gsw->eth, i, 14, 0xffff);
++
++		/* Disable mcc */
++		_mtk_mdio_write(gsw->eth, i, 13, 0x1e);
++		_mtk_mdio_write(gsw->eth, i, 14, 0xa6);
++		_mtk_mdio_write(gsw->eth, i, 13, 0x401e);
++		_mtk_mdio_write(gsw->eth, i, 14, 0x300);
++
++		/* Disable HW auto downshift*/
++		_mtk_mdio_write(gsw->eth, i, 31, 0x1);
++		val = _mtk_mdio_read(gsw->eth, i, 0x14);
++		val &= ~(1<<4);
++		_mtk_mdio_write(gsw->eth, i, 0x14, val);
++	}
++
++	/* turn on all PHYs */
++	for (i = 0; i <= 4; i++) {
++		val = _mtk_mdio_read(gsw->eth, i, 0);
++		val &= ~BIT(11);
++		_mtk_mdio_write(gsw->eth, i, 0, val);
++	}
++
++	/* enable irq */
++	mt7530_mdio_m32(gsw, 0, TOP_SIG_CTRL_NORMAL, MT7530_TOP_SIG_CTRL);
++
++	/* enable autopolling */
++	if (gsw->eth->mac[1] && gsw->eth->mac[1]->phy_dev) {
++		val = mtk_switch_r32(gsw, 0);
++		val |= (1<<31);
++		val &= ~(0x1f);
++		val &= ~(0x1f<<8);
++		val |= 4;
++		val |= 5 << 8;
++		mtk_switch_w32(gsw, val, 0);
++
++		val = _mtk_mdio_read(gsw->eth, 5, 4);
++		val |= BIT(10);
++		_mtk_mdio_write(gsw->eth, 5, 4, val);
++		val = _mtk_mdio_read(gsw->eth, 5, 0);
++		val |= BIT(9);
++		_mtk_mdio_write(gsw->eth, 5, 0, val);
++	}
++}
++
++static const struct of_device_id mediatek_gsw_match[] = {
++	{ .compatible = "mediatek,mt7623-gsw" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
++
++int mtk_gsw_init(struct mtk_eth *eth)
++{
++	struct device_node *np = eth->switch_np;
++	struct platform_device *pdev = of_find_device_by_node(np);
++	struct mt7620_gsw *gsw;
++
++	if (!pdev)
++		return -ENODEV;
++
++	if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
++		return -EINVAL;
++
++	gsw = platform_get_drvdata(pdev);
++	if (!gsw)
++		return -ENODEV;
++	gsw->eth = eth;
++	eth->sw_priv = gsw;
++
++	mt7623_hw_init(eth, gsw, np);
++
++	request_threaded_irq(gsw->irq, gsw_interrupt_mt7623, NULL, 0,
++			    "gsw", eth);
++	mt7530_mdio_w32(gsw, 0x7008, 0x1f);
++
++	return 0;
++}
++
++static int mt7623_gsw_probe(struct platform_device *pdev)
++{
++	struct device_node *np = pdev->dev.of_node;
++	struct device_node *pctl;
++	int reset_pin, ret;
++	struct mt7620_gsw *gsw;
++	struct regulator *supply;
++
++	gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
++	if (!gsw)
++		return -ENOMEM;
++
++	gsw->dev = &pdev->dev;
++	gsw->trgmii_force = 2000;
++	gsw->irq = irq_of_parse_and_map(np, 0);
++	if (gsw->irq < 0)
++		return -EINVAL;
++
++	gsw->ethsys = syscon_regmap_lookup_by_phandle(np, "mediatek,ethsys");
++	if (IS_ERR(gsw->ethsys))
++		return PTR_ERR(gsw->ethsys);
++
++	reset_pin = of_get_named_gpio(np, "mediatek,reset-pin", 0);
++	if (reset_pin < 0)
++		return reset_pin;
++
++	pctl = of_parse_phandle(np, "mediatek,pctl-regmap", 0);
++	if (IS_ERR(pctl))
++		return PTR_ERR(pctl);
++
++	gsw->pctl = syscon_node_to_regmap(pctl);
++	if (IS_ERR(pctl))
++		return PTR_ERR(pctl);
++
++	ret = devm_gpio_request(&pdev->dev, reset_pin, "mt7530-reset");
++	if (ret)
++		return ret;
++
++	gsw->clk_trgpll = devm_clk_get(&pdev->dev, "trgpll");
++
++	if (IS_ERR(gsw->clk_trgpll))
++		return -ENODEV;
++
++	supply = devm_regulator_get(&pdev->dev, "mt7530");
++	if (IS_ERR(supply))
++		return PTR_ERR(supply);
++
++	regulator_set_voltage(supply, 1000000, 1000000);
++	ret = regulator_enable(supply);
++	if (ret) {
++		dev_err(&pdev->dev, "Failed to enable reg-7530: %d\n", ret);
++		return ret;
++	}
++	pm_runtime_enable(&pdev->dev);
++	pm_runtime_get_sync(&pdev->dev);
++
++	ret = clk_set_rate(gsw->clk_trgpll, 500000000);
++	if (ret)
++		return ret;
++
++	clk_prepare_enable(gsw->clk_trgpll);
++
++	gpio_direction_output(reset_pin, 0);
++	udelay(1000);
++	gpio_set_value(reset_pin, 1);
++	mdelay(100);
++
++	platform_set_drvdata(pdev, gsw);
++
++	return 0;
++}
++
++static int mt7623_gsw_remove(struct platform_device *pdev)
++{
++	struct mt7620_gsw *gsw = platform_get_drvdata(pdev);
++
++	clk_disable_unprepare(gsw->clk_trgpll);
++
++	pm_runtime_put_sync(&pdev->dev);
++        pm_runtime_disable(&pdev->dev);
++
++	platform_set_drvdata(pdev, NULL);
++
++	return 0;
++}
++
++static struct platform_driver gsw_driver = {
++	.probe = mt7623_gsw_probe,
++	.remove = mt7623_gsw_remove,
++	.driver = {
++		.name = "mt7623-gsw",
++		.owner = THIS_MODULE,
++		.of_match_table = mediatek_gsw_match,
++	},
++};
++
++module_platform_driver(gsw_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
++MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7623 SoC");
+diff --git a/drivers/net/ethernet/mediatek/mt7530.c b/drivers/net/ethernet/mediatek/mt7530.c
+new file mode 100644
+index 0000000..2e9d280
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mt7530.c
+@@ -0,0 +1,808 @@
++/*
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * 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 the
++ * GNU General Public License for more details.
++ *
++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
++ */
++
++#include <linux/if.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/if_ether.h>
++#include <linux/skbuff.h>
++#include <linux/netdevice.h>
++#include <linux/netlink.h>
++#include <linux/bitops.h>
++#include <net/genetlink.h>
++#include <linux/switch.h>
++#include <linux/delay.h>
++#include <linux/phy.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/lockdep.h>
++#include <linux/workqueue.h>
++#include <linux/of_device.h>
++
++#include "mt7530.h"
++
++#define MT7530_CPU_PORT		6
++#define MT7530_NUM_PORTS	8
++#define MT7530_NUM_VLANS	16
++#define MT7530_MAX_VID		4095
++#define MT7530_MIN_VID		0
++
++/* registers */
++#define REG_ESW_VLAN_VTCR		0x90
++#define REG_ESW_VLAN_VAWD1		0x94
++#define REG_ESW_VLAN_VAWD2		0x98
++#define REG_ESW_VLAN_VTIM(x)	(0x100 + 4 * ((x) / 2))
++
++#define REG_ESW_VLAN_VAWD1_IVL_MAC	BIT(30)
++#define REG_ESW_VLAN_VAWD1_VTAG_EN	BIT(28)
++#define REG_ESW_VLAN_VAWD1_VALID	BIT(0)
++
++/* vlan egress mode */
++enum {
++	ETAG_CTRL_UNTAG	= 0,
++	ETAG_CTRL_TAG	= 2,
++	ETAG_CTRL_SWAP	= 1,
++	ETAG_CTRL_STACK	= 3,
++};
++
++#define REG_ESW_PORT_PCR(x)	(0x2004 | ((x) << 8))
++#define REG_ESW_PORT_PVC(x)	(0x2010 | ((x) << 8))
++#define REG_ESW_PORT_PPBV1(x)	(0x2014 | ((x) << 8))
++
++#define REG_HWTRAP		0x7804
++
++#define MIB_DESC(_s , _o, _n)   \
++	{                       \
++		.size = (_s),   \
++		.offset = (_o), \
++		.name = (_n),   \
++	}
++
++struct mt7xxx_mib_desc {
++	unsigned int size;
++	unsigned int offset;
++	const char *name;
++};
++
++#define MT7621_MIB_COUNTER_BASE	0x4000
++#define MT7621_MIB_COUNTER_PORT_OFFSET	0x100
++#define MT7621_STATS_TDPC	0x00
++#define MT7621_STATS_TCRC	0x04
++#define MT7621_STATS_TUPC	0x08
++#define MT7621_STATS_TMPC	0x0C
++#define MT7621_STATS_TBPC	0x10
++#define MT7621_STATS_TCEC	0x14
++#define MT7621_STATS_TSCEC	0x18
++#define MT7621_STATS_TMCEC	0x1C
++#define MT7621_STATS_TDEC	0x20
++#define MT7621_STATS_TLCEC	0x24
++#define MT7621_STATS_TXCEC	0x28
++#define MT7621_STATS_TPPC	0x2C
++#define MT7621_STATS_TL64PC	0x30
++#define MT7621_STATS_TL65PC	0x34
++#define MT7621_STATS_TL128PC	0x38
++#define MT7621_STATS_TL256PC	0x3C
++#define MT7621_STATS_TL512PC	0x40
++#define MT7621_STATS_TL1024PC	0x44
++#define MT7621_STATS_TOC	0x48
++#define MT7621_STATS_RDPC	0x60
++#define MT7621_STATS_RFPC	0x64
++#define MT7621_STATS_RUPC	0x68
++#define MT7621_STATS_RMPC	0x6C
++#define MT7621_STATS_RBPC	0x70
++#define MT7621_STATS_RAEPC	0x74
++#define MT7621_STATS_RCEPC	0x78
++#define MT7621_STATS_RUSPC	0x7C
++#define MT7621_STATS_RFEPC	0x80
++#define MT7621_STATS_ROSPC	0x84
++#define MT7621_STATS_RJEPC	0x88
++#define MT7621_STATS_RPPC	0x8C
++#define MT7621_STATS_RL64PC	0x90
++#define MT7621_STATS_RL65PC	0x94
++#define MT7621_STATS_RL128PC	0x98
++#define MT7621_STATS_RL256PC	0x9C
++#define MT7621_STATS_RL512PC	0xA0
++#define MT7621_STATS_RL1024PC	0xA4
++#define MT7621_STATS_ROC	0xA8
++#define MT7621_STATS_RDPC_CTRL	0xB0
++#define MT7621_STATS_RDPC_ING	0xB4
++#define MT7621_STATS_RDPC_ARL	0xB8
++
++static const struct mt7xxx_mib_desc mt7621_mibs[] = {
++	MIB_DESC(1, MT7621_STATS_TDPC, "TxDrop"),
++	MIB_DESC(1, MT7621_STATS_TCRC, "TxCRC"),
++	MIB_DESC(1, MT7621_STATS_TUPC, "TxUni"),
++	MIB_DESC(1, MT7621_STATS_TMPC, "TxMulti"),
++	MIB_DESC(1, MT7621_STATS_TBPC, "TxBroad"),
++	MIB_DESC(1, MT7621_STATS_TCEC, "TxCollision"),
++	MIB_DESC(1, MT7621_STATS_TSCEC, "TxSingleCol"),
++	MIB_DESC(1, MT7621_STATS_TMCEC, "TxMultiCol"),
++	MIB_DESC(1, MT7621_STATS_TDEC, "TxDefer"),
++	MIB_DESC(1, MT7621_STATS_TLCEC, "TxLateCol"),
++	MIB_DESC(1, MT7621_STATS_TXCEC, "TxExcCol"),
++	MIB_DESC(1, MT7621_STATS_TPPC, "TxPause"),
++	MIB_DESC(1, MT7621_STATS_TL64PC, "Tx64Byte"),
++	MIB_DESC(1, MT7621_STATS_TL65PC, "Tx65Byte"),
++	MIB_DESC(1, MT7621_STATS_TL128PC, "Tx128Byte"),
++	MIB_DESC(1, MT7621_STATS_TL256PC, "Tx256Byte"),
++	MIB_DESC(1, MT7621_STATS_TL512PC, "Tx512Byte"),
++	MIB_DESC(1, MT7621_STATS_TL1024PC, "Tx1024Byte"),
++	MIB_DESC(2, MT7621_STATS_TOC, "TxByte"),
++	MIB_DESC(1, MT7621_STATS_RDPC, "RxDrop"),
++	MIB_DESC(1, MT7621_STATS_RFPC, "RxFiltered"),
++	MIB_DESC(1, MT7621_STATS_RUPC, "RxUni"),
++	MIB_DESC(1, MT7621_STATS_RMPC, "RxMulti"),
++	MIB_DESC(1, MT7621_STATS_RBPC, "RxBroad"),
++	MIB_DESC(1, MT7621_STATS_RAEPC, "RxAlignErr"),
++	MIB_DESC(1, MT7621_STATS_RCEPC, "RxCRC"),
++	MIB_DESC(1, MT7621_STATS_RUSPC, "RxUnderSize"),
++	MIB_DESC(1, MT7621_STATS_RFEPC, "RxFragment"),
++	MIB_DESC(1, MT7621_STATS_ROSPC, "RxOverSize"),
++	MIB_DESC(1, MT7621_STATS_RJEPC, "RxJabber"),
++	MIB_DESC(1, MT7621_STATS_RPPC, "RxPause"),
++	MIB_DESC(1, MT7621_STATS_RL64PC, "Rx64Byte"),
++	MIB_DESC(1, MT7621_STATS_RL65PC, "Rx65Byte"),
++	MIB_DESC(1, MT7621_STATS_RL128PC, "Rx128Byte"),
++	MIB_DESC(1, MT7621_STATS_RL256PC, "Rx256Byte"),
++	MIB_DESC(1, MT7621_STATS_RL512PC, "Rx512Byte"),
++	MIB_DESC(1, MT7621_STATS_RL1024PC, "Rx1024Byte"),
++	MIB_DESC(2, MT7621_STATS_ROC, "RxByte"),
++	MIB_DESC(1, MT7621_STATS_RDPC_CTRL, "RxCtrlDrop"),
++	MIB_DESC(1, MT7621_STATS_RDPC_ING, "RxIngDrop"),
++	MIB_DESC(1, MT7621_STATS_RDPC_ARL, "RxARLDrop")
++};
++
++enum {
++	/* Global attributes. */
++	MT7530_ATTR_ENABLE_VLAN,
++};
++
++struct mt7530_port_entry {
++	u16	pvid;
++};
++
++struct mt7530_vlan_entry {
++	u16	vid;
++	u8	member;
++	u8	etags;
++};
++
++struct mt7530_priv {
++	void __iomem		*base;
++	struct mii_bus		*bus;
++	struct switch_dev	swdev;
++
++	bool			global_vlan_enable;
++	struct mt7530_vlan_entry	vlan_entries[MT7530_NUM_VLANS];
++	struct mt7530_port_entry	port_entries[MT7530_NUM_PORTS];
++};
++
++struct mt7530_mapping {
++	char	*name;
++	u16	pvids[MT7530_NUM_PORTS];
++	u8	members[MT7530_NUM_VLANS];
++	u8	etags[MT7530_NUM_VLANS];
++	u16	vids[MT7530_NUM_VLANS];
++} mt7530_defaults[] = {
++	{
++		.name = "llllw",
++		.pvids = { 1, 1, 1, 1, 2, 1, 1 },
++		.members = { 0, 0x6f, 0x50 },
++		.etags = { 0, 0x40, 0x40 },
++		.vids = { 0, 1, 2 },
++	}, {
++		.name = "wllll",
++		.pvids = { 2, 1, 1, 1, 1, 1, 1 },
++		.members = { 0, 0x7e, 0x41 },
++		.etags = { 0, 0x40, 0x40 },
++		.vids = { 0, 1, 2 },
++	},
++};
++
++struct mt7530_mapping*
++mt7530_find_mapping(struct device_node *np)
++{
++	const char *map;
++	int i;
++
++	if (of_property_read_string(np, "mediatek,portmap", &map))
++		return NULL;
++
++	for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++)
++		if (!strcmp(map, mt7530_defaults[i].name))
++			return &mt7530_defaults[i];
++
++	return NULL;
++}
++
++static void
++mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map)
++{
++	int i = 0;
++
++	for (i = 0; i < MT7530_NUM_PORTS; i++)
++		mt7530->port_entries[i].pvid = map->pvids[i];
++
++	for (i = 0; i < MT7530_NUM_VLANS; i++) {
++		mt7530->vlan_entries[i].member = map->members[i];
++		mt7530->vlan_entries[i].etags = map->etags[i];
++		mt7530->vlan_entries[i].vid = map->vids[i];
++	}
++}
++
++static int
++mt7530_reset_switch(struct switch_dev *dev)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	int i;
++
++	memset(eth->port_entries, 0, sizeof(eth->port_entries));
++	memset(eth->vlan_entries, 0, sizeof(eth->vlan_entries));
++
++	/* set default vid of each vlan to the same number of vlan, so the vid
++	 * won't need be set explicitly.
++	 */
++	for (i = 0; i < MT7530_NUM_VLANS; i++) {
++		eth->vlan_entries[i].vid = i;
++	}
++
++	return 0;
++}
++
++static int
++mt7530_get_vlan_enable(struct switch_dev *dev,
++			   const struct switch_attr *attr,
++			   struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++
++	val->value.i = eth->global_vlan_enable;
++
++	return 0;
++}
++
++static int
++mt7530_set_vlan_enable(struct switch_dev *dev,
++			   const struct switch_attr *attr,
++			   struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++
++	eth->global_vlan_enable = val->value.i != 0;
++
++	return 0;
++}
++
++static u32
++mt7530_r32(struct mt7530_priv *eth, u32 reg)
++{
++	u32 val;
++	if (eth->bus) {
++		u16 high, low;
++
++		mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++		low = mdiobus_read(eth->bus, 0x1f, (reg >> 2) & 0xf);
++		high = mdiobus_read(eth->bus, 0x1f, 0x10);
++
++		return (high << 16) | (low & 0xffff);
++	}
++
++	val = ioread32(eth->base + reg);
++	pr_debug("MT7530 MDIO Read [%04x]=%08x\n", reg, val);
++
++	return val;
++}
++
++static void
++mt7530_w32(struct mt7530_priv *eth, u32 reg, u32 val)
++{
++	if (eth->bus) {
++		mdiobus_write(eth->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
++		mdiobus_write(eth->bus, 0x1f, (reg >> 2) & 0xf,  val & 0xffff);
++		mdiobus_write(eth->bus, 0x1f, 0x10, val >> 16);
++		return;
++	}
++
++	pr_debug("MT7530 MDIO Write[%04x]=%08x\n", reg, val);
++	iowrite32(val, eth->base + reg);
++}
++
++static void
++mt7530_vtcr(struct mt7530_priv *eth, u32 cmd, u32 val)
++{
++	int i;
++
++	mt7530_w32(eth, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val);
++
++	for (i = 0; i < 20; i++) {
++		u32 val = mt7530_r32(eth, REG_ESW_VLAN_VTCR);
++
++		if ((val & BIT(31)) == 0)
++			break;
++
++		udelay(1000);
++	}
++	if (i == 20)
++		printk("mt7530: vtcr timeout\n");
++}
++
++static int
++mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++
++	if (port >= MT7530_NUM_PORTS)
++		return -EINVAL;
++
++	*val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(port));
++	*val &= 0xfff;
++
++	return 0;
++}
++
++static int
++mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++
++	if (port >= MT7530_NUM_PORTS)
++		return -EINVAL;
++
++	if (pvid < MT7530_MIN_VID || pvid > MT7530_MAX_VID)
++		return -EINVAL;
++
++	eth->port_entries[port].pvid = pvid;
++
++	return 0;
++}
++
++static int
++mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	u32 member;
++	u32 etags;
++	int i;
++
++	val->len = 0;
++
++	if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS)
++		return -EINVAL;
++
++	mt7530_vtcr(eth, 0, val->port_vlan);
++
++	member = mt7530_r32(eth, REG_ESW_VLAN_VAWD1);
++	member >>= 16;
++	member &= 0xff;
++
++	etags = mt7530_r32(eth, REG_ESW_VLAN_VAWD2);
++
++	for (i = 0; i < MT7530_NUM_PORTS; i++) {
++		struct switch_port *p;
++		int etag;
++
++		if (!(member & BIT(i)))
++			continue;
++
++		p = &val->value.ports[val->len++];
++		p->id = i;
++
++		etag = (etags >> (i * 2)) & 0x3;
++
++		if (etag == ETAG_CTRL_TAG)
++			p->flags |= BIT(SWITCH_PORT_FLAG_TAGGED);
++		else if (etag != ETAG_CTRL_UNTAG)
++			printk("vlan egress tag control neither untag nor tag.\n");
++	}
++
++	return 0;
++}
++
++static int
++mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	u8 member = 0;
++	u8 etags = 0;
++	int i;
++
++	if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VLANS ||
++			val->len > MT7530_NUM_PORTS)
++		return -EINVAL;
++
++	for (i = 0; i < val->len; i++) {
++		struct switch_port *p = &val->value.ports[i];
++
++		if (p->id >= MT7530_NUM_PORTS)
++			return -EINVAL;
++
++		member |= BIT(p->id);
++
++		if (p->flags & BIT(SWITCH_PORT_FLAG_TAGGED))
++			etags |= BIT(p->id);
++	}
++	eth->vlan_entries[val->port_vlan].member = member;
++	eth->vlan_entries[val->port_vlan].etags = etags;
++
++	return 0;
++}
++
++static int
++mt7530_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
++		struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	int vlan;
++	u16 vid;
++
++	vlan = val->port_vlan;
++	vid = (u16)val->value.i;
++
++	if (vlan < 0 || vlan >= MT7530_NUM_VLANS)
++		return -EINVAL;
++
++	if (vid < MT7530_MIN_VID || vid > MT7530_MAX_VID)
++		return -EINVAL;
++
++	eth->vlan_entries[vlan].vid = vid;
++	return 0;
++}
++
++static int
++mt7530_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
++		struct switch_val *val)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	u32 vid;
++	int vlan;
++
++	vlan = val->port_vlan;
++
++	vid = mt7530_r32(eth, REG_ESW_VLAN_VTIM(vlan));
++	if (vlan & 1)
++		vid = vid >> 12;
++	vid &= 0xfff;
++
++	val->value.i = vid;
++	return 0;
++}
++
++static int
++mt7530_apply_config(struct switch_dev *dev)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	int i, j;
++	u8 tag_ports;
++	u8 untag_ports;
++
++	if (!eth->global_vlan_enable) {
++		for (i = 0; i < MT7530_NUM_PORTS; i++)
++			mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0000);
++
++		for (i = 0; i < MT7530_NUM_PORTS; i++)
++			mt7530_w32(eth, REG_ESW_PORT_PVC(i), 0x810000c0);
++
++		return 0;
++	}
++
++	/* set all ports as security mode */
++	for (i = 0; i < MT7530_NUM_PORTS; i++)
++		mt7530_w32(eth, REG_ESW_PORT_PCR(i), 0x00ff0003);
++
++	/* check if a port is used in tag/untag vlan egress mode */
++	tag_ports = 0;
++	untag_ports = 0;
++
++	for (i = 0; i < MT7530_NUM_VLANS; i++) {
++		u8 member = eth->vlan_entries[i].member;
++		u8 etags = eth->vlan_entries[i].etags;
++
++		if (!member)
++			continue;
++
++		for (j = 0; j < MT7530_NUM_PORTS; j++) {
++			if (!(member & BIT(j)))
++				continue;
++
++			if (etags & BIT(j))
++				tag_ports |= 1u << j;
++			else
++				untag_ports |= 1u << j;
++		}
++	}
++
++	/* set all untag-only ports as transparent and the rest as user port */
++	for (i = 0; i < MT7530_NUM_PORTS; i++) {
++		u32 pvc_mode = 0x81000000;
++
++		if (untag_ports & BIT(i) && !(tag_ports & BIT(i)))
++			pvc_mode = 0x810000c0;
++
++		mt7530_w32(eth, REG_ESW_PORT_PVC(i), pvc_mode);
++	}
++
++	for (i = 0; i < MT7530_NUM_VLANS; i++) {
++		u16 vid = eth->vlan_entries[i].vid;
++		u8 member = eth->vlan_entries[i].member;
++		u8 etags = eth->vlan_entries[i].etags;
++		u32 val;
++
++		/* vid of vlan */
++		val = mt7530_r32(eth, REG_ESW_VLAN_VTIM(i));
++		if (i % 2 == 0) {
++			val &= 0xfff000;
++			val |= vid;
++		} else {
++			val &= 0xfff;
++			val |= (vid << 12);
++		}
++		mt7530_w32(eth, REG_ESW_VLAN_VTIM(i), val);
++
++		/* vlan port membership */
++		if (member)
++			mt7530_w32(eth, REG_ESW_VLAN_VAWD1, REG_ESW_VLAN_VAWD1_IVL_MAC |
++				REG_ESW_VLAN_VAWD1_VTAG_EN | (member << 16) |
++				REG_ESW_VLAN_VAWD1_VALID);
++		else
++			mt7530_w32(eth, REG_ESW_VLAN_VAWD1, 0);
++
++		/* egress mode */
++		val = 0;
++		for (j = 0; j < MT7530_NUM_PORTS; j++) {
++			if (etags & BIT(j))
++				val |= ETAG_CTRL_TAG << (j * 2);
++			else
++				val |= ETAG_CTRL_UNTAG << (j * 2);
++		}
++		mt7530_w32(eth, REG_ESW_VLAN_VAWD2, val);
++
++		/* write to vlan table */
++		mt7530_vtcr(eth, 1, i);
++	}
++
++	/* Port Default PVID */
++	for (i = 0; i < MT7530_NUM_PORTS; i++) {
++		u32 val;
++		val = mt7530_r32(eth, REG_ESW_PORT_PPBV1(i));
++		val &= ~0xfff;
++		val |= eth->port_entries[i].pvid;
++		mt7530_w32(eth, REG_ESW_PORT_PPBV1(i), val);
++	}
++
++	return 0;
++}
++
++static int
++mt7530_get_port_link(struct switch_dev *dev,  int port,
++			struct switch_port_link *link)
++{
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	u32 speed, pmsr;
++
++	if (port < 0 || port >= MT7530_NUM_PORTS)
++		return -EINVAL;
++
++	pmsr = mt7530_r32(eth, 0x3008 + (0x100 * port));
++
++	link->link = pmsr & 1;
++	link->duplex = (pmsr >> 1) & 1;
++	speed = (pmsr >> 2) & 3;
++
++	switch (speed) {
++	case 0:
++		link->speed = SWITCH_PORT_SPEED_10;
++		break;
++	case 1:
++		link->speed = SWITCH_PORT_SPEED_100;
++		break;
++	case 2:
++	case 3: /* forced gige speed can be 2 or 3 */
++		link->speed = SWITCH_PORT_SPEED_1000;
++		break;
++	default:
++		link->speed = SWITCH_PORT_SPEED_UNKNOWN;
++		break;
++	}
++
++	return 0;
++}
++
++static const struct switch_attr mt7530_global[] = {
++	{
++		.type = SWITCH_TYPE_INT,
++		.name = "enable_vlan",
++		.description = "VLAN mode (1:enabled)",
++		.max = 1,
++		.id = MT7530_ATTR_ENABLE_VLAN,
++		.get = mt7530_get_vlan_enable,
++		.set = mt7530_set_vlan_enable,
++	},
++};
++
++static u64 get_mib_counter(struct mt7530_priv *eth, int i, int port)
++{
++	unsigned int port_base;
++	u64 t;
++
++	port_base = MT7621_MIB_COUNTER_BASE +
++		    MT7621_MIB_COUNTER_PORT_OFFSET * port;
++
++	t = mt7530_r32(eth, port_base + mt7621_mibs[i].offset);
++	if (mt7621_mibs[i].size == 2) {
++		u64 hi;
++
++		hi = mt7530_r32(eth, port_base + mt7621_mibs[i].offset + 4);
++		t |= hi << 32;
++	}
++
++	return t;
++}
++
++static int mt7621_sw_get_port_mib(struct switch_dev *dev,
++				  const struct switch_attr *attr,
++				  struct switch_val *val)
++{
++	static char buf[4096];
++	struct mt7530_priv *eth = container_of(dev, struct mt7530_priv, swdev);
++	int i, len = 0;
++
++	if (val->port_vlan >= MT7530_NUM_PORTS)
++		return -EINVAL;
++
++	len += snprintf(buf + len, sizeof(buf) - len,
++			"Port %d MIB counters\n", val->port_vlan);
++
++	for (i = 0; i < sizeof(mt7621_mibs) / sizeof(*mt7621_mibs); ++i) {
++		u64 counter;
++		len += snprintf(buf + len, sizeof(buf) - len,
++				"%-11s: ", mt7621_mibs[i].name);
++		counter = get_mib_counter(eth, i, val->port_vlan);
++		len += snprintf(buf + len, sizeof(buf) - len, "%llu\n",
++				counter);
++	}
++
++	val->value.s = buf;
++	val->len = len;
++	return 0;
++}
++
++static const struct switch_attr mt7621_port[] = {
++	{
++		.type = SWITCH_TYPE_STRING,
++		.name = "mib",
++		.description = "Get MIB counters for port",
++		.get = mt7621_sw_get_port_mib,
++		.set = NULL,
++	},
++};
++
++static const struct switch_attr mt7530_port[] = {
++};
++
++static const struct switch_attr mt7530_vlan[] = {
++	{
++		.type = SWITCH_TYPE_INT,
++		.name = "vid",
++		.description = "VLAN ID (0-4094)",
++		.set = mt7530_set_vid,
++		.get = mt7530_get_vid,
++		.max = 4094,
++	},
++};
++
++static const struct switch_dev_ops mt7621_ops = {
++	.attr_global = {
++		.attr = mt7530_global,
++		.n_attr = ARRAY_SIZE(mt7530_global),
++	},
++/*	.attr_port = {
++		.attr = mt7621_port,
++		.n_attr = ARRAY_SIZE(mt7621_port),
++	},*/
++	.attr_vlan = {
++		.attr = mt7530_vlan,
++		.n_attr = ARRAY_SIZE(mt7530_vlan),
++	},
++	.get_vlan_ports = mt7530_get_vlan_ports,
++	.set_vlan_ports = mt7530_set_vlan_ports,
++	.get_port_pvid = mt7530_get_port_pvid,
++	.set_port_pvid = mt7530_set_port_pvid,
++	.get_port_link = mt7530_get_port_link,
++	.apply_config = mt7530_apply_config,
++	.reset_switch = mt7530_reset_switch,
++};
++
++static const struct switch_dev_ops mt7530_ops = {
++	.attr_global = {
++		.attr = mt7530_global,
++		.n_attr = ARRAY_SIZE(mt7530_global),
++	},
++	.attr_port = {
++		.attr = mt7530_port,
++		.n_attr = ARRAY_SIZE(mt7530_port),
++	},
++	.attr_vlan = {
++		.attr = mt7530_vlan,
++		.n_attr = ARRAY_SIZE(mt7530_vlan),
++	},
++	.get_vlan_ports = mt7530_get_vlan_ports,
++	.set_vlan_ports = mt7530_set_vlan_ports,
++	.get_port_pvid = mt7530_get_port_pvid,
++	.set_port_pvid = mt7530_set_port_pvid,
++	.get_port_link = mt7530_get_port_link,
++	.apply_config = mt7530_apply_config,
++	.reset_switch = mt7530_reset_switch,
++};
++
++int
++mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan)
++{
++	struct switch_dev *swdev;
++	struct mt7530_priv *mt7530;
++	struct mt7530_mapping *map;
++	int ret;
++
++	mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL);
++	if (!mt7530)
++		return -ENOMEM;
++
++	mt7530->base = base;
++	mt7530->bus = bus;
++	mt7530->global_vlan_enable = vlan;
++
++	swdev = &mt7530->swdev;
++	if (bus) {
++		swdev->alias = "mt7530";
++		swdev->name = "mt7530";
++	} else if (IS_ENABLED(CONFIG_MACH_MT7623)) {
++		swdev->alias = "mt7623";
++		swdev->name = "mt7623";
++	} else if (IS_ENABLED(CONFIG_SOC_MT7621)) {
++		swdev->alias = "mt7621";
++		swdev->name = "mt7621";
++	} else {
++		swdev->alias = "mt7620";
++		swdev->name = "mt7620";
++	}
++	swdev->cpu_port = MT7530_CPU_PORT;
++	swdev->ports = MT7530_NUM_PORTS;
++	swdev->vlans = MT7530_NUM_VLANS;
++	if (IS_ENABLED(CONFIG_SOC_MT7621) || IS_ENABLED(CONFIG_MACH_MT7623))
++		swdev->ops = &mt7621_ops;
++	else
++		swdev->ops = &mt7530_ops;
++
++	ret = register_switch(swdev, NULL);
++	if (ret) {
++		dev_err(dev, "failed to register mt7530\n");
++		return ret;
++	}
++
++	mt7530_reset_switch(swdev);
++
++	map = mt7530_find_mapping(dev->of_node);
++	if (map)
++		mt7530_apply_mapping(mt7530, map);
++	mt7530_apply_config(swdev);
++
++	/* magic vodoo */
++	if (!(IS_ENABLED(CONFIG_SOC_MT7621) || IS_ENABLED(CONFIG_MACH_MT7623)) && bus && mt7530_r32(mt7530, REG_HWTRAP) !=  0x1117edf) {
++	        dev_info(dev, "fixing up MHWTRAP register - bootloader probably played with it\n");
++		mt7530_w32(mt7530, REG_HWTRAP, 0x1117edf);
++	}
++	dev_info(dev, "loaded %s driver\n", swdev->name);
++
++	return 0;
++}
+diff --git a/drivers/net/ethernet/mediatek/mt7530.h b/drivers/net/ethernet/mediatek/mt7530.h
+new file mode 100644
+index 0000000..1fc8c62
+--- /dev/null
++++ b/drivers/net/ethernet/mediatek/mt7530.h
+@@ -0,0 +1,20 @@
++/*
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * 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 the
++ * GNU General Public License for more details.
++ *
++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
++ */
++
++#ifndef _MT7530_H__
++#define _MT7530_H__
++
++int mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus, int vlan);
++
++#endif
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+index 2097ae1..ca7e961 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -24,6 +24,9 @@
+ 
+ #include "mtk_eth_soc.h"
+ 
++/* the callback used by the driver core to bringup the switch */
++int mtk_gsw_init(struct mtk_eth *eth);
++
+ static int mtk_msg_level = -1;
+ module_param_named(msg_level, mtk_msg_level, int, 0);
+ MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
+@@ -69,7 +72,7 @@ static int mtk_mdio_busy_wait(struct mtk_eth *eth)
+ 			return 0;
+ 		if (time_after(jiffies, t_start + PHY_IAC_TIMEOUT))
+ 			break;
+-		usleep_range(10, 20);
++//		usleep_range(10, 20);
+ 	}
+ 
+ 	dev_err(eth->dev, "mdio: MDIO timeout\n");
+@@ -132,36 +135,8 @@ static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+ 
+ static void mtk_phy_link_adjust(struct net_device *dev)
+ {
+-	struct mtk_mac *mac = netdev_priv(dev);
+-	u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
+-		  MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
+-		  MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
+-		  MAC_MCR_BACKPR_EN;
+-
+-	switch (mac->phy_dev->speed) {
+-	case SPEED_1000:
+-		mcr |= MAC_MCR_SPEED_1000;
+-		break;
+-	case SPEED_100:
+-		mcr |= MAC_MCR_SPEED_100;
+-		break;
+-	};
+-
+-	if (mac->phy_dev->link)
+-		mcr |= MAC_MCR_FORCE_LINK;
+-
+-	if (mac->phy_dev->duplex)
+-		mcr |= MAC_MCR_FORCE_DPX;
+-
+-	if (mac->phy_dev->pause)
+-		mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
+-
+-	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+-
+-	if (mac->phy_dev->link)
+-		netif_carrier_on(dev);
+-	else
+-		netif_carrier_off(dev);
++	netif_carrier_on(dev);
++	return;
+ }
+ 
+ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+@@ -193,7 +168,7 @@ static int mtk_phy_connect_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ 
+ 	dev_info(eth->dev,
+ 		 "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
+-		 mac->id, phydev_name(phydev), phydev->phy_id,
++		 mac->id, dev_name(&phydev->dev), phydev->phy_id,
+ 		 phydev->drv->name);
+ 
+ 	mac->phy_dev = phydev;
+@@ -209,7 +184,7 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ 
+ 	np = of_parse_phandle(mac->of_node, "phy-handle", 0);
+ 	if (!np)
+-		return -ENODEV;
++		return 0;
+ 
+ 	switch (of_get_phy_mode(np)) {
+ 	case PHY_INTERFACE_MODE_RGMII:
+@@ -239,7 +214,8 @@ static int mtk_phy_connect(struct mtk_mac *mac)
+ 	mac->phy_dev->supported &= PHY_BASIC_FEATURES;
+ 	mac->phy_dev->advertising = mac->phy_dev->supported |
+ 				    ADVERTISED_Autoneg;
+-	phy_start_aneg(mac->phy_dev);
++	if (mac->phy_dev)
++		phy_start_aneg(mac->phy_dev);
+ 
+ 	return 0;
+ }
+@@ -626,7 +602,6 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
+ 	WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ 				(!nr_frags * TX_DMA_LS0)));
+ 
+-	netdev_sent_queue(dev, skb->len);
+ 	skb_tx_timestamp(skb);
+ 
+ 	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
+@@ -906,7 +881,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget)
+ 	for (i = 0; i < MTK_MAC_COUNT; i++) {
+ 		if (!eth->netdev[i] || !done[i])
+ 			continue;
+-		netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+ 		total += done[i];
+ 	}
+ 
+@@ -1284,9 +1258,12 @@ static int mtk_open(struct net_device *dev)
+ 	}
+ 	atomic_inc(&eth->dma_refcnt);
+ 
+-	phy_start(mac->phy_dev);
++	if (mac->phy_dev)
++		phy_start(mac->phy_dev);
+ 	netif_start_queue(dev);
+ 
++	netif_carrier_on(dev);
++
+ 	return 0;
+ }
+ 
+@@ -1319,8 +1296,10 @@ static int mtk_stop(struct net_device *dev)
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 	struct mtk_eth *eth = mac->hw;
+ 
++	netif_carrier_off(dev);
+ 	netif_tx_disable(dev);
+-	phy_stop(mac->phy_dev);
++	if (mac->phy_dev)
++		phy_stop(mac->phy_dev);
+ 
+ 	/* only shutdown DMA if this is the last user */
+ 	if (!atomic_dec_and_test(&eth->dma_refcnt))
+@@ -1346,20 +1325,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ 	reset_control_deassert(eth->rstc);
+ 	usleep_range(10, 20);
+ 
+-	/* Set GE2 driving and slew rate */
+-	regmap_write(eth->pctl, GPIO_DRV_SEL10, 0xa00);
+-
+-	/* set GE2 TDSEL */
+-	regmap_write(eth->pctl, GPIO_OD33_CTRL8, 0x5);
+-
+-	/* set GE2 TUNE */
+-	regmap_write(eth->pctl, GPIO_BIAS_CTRL, 0x0);
+-
+ 	/* GE1, Force 1000M/FD, FC ON */
+-	mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(0));
++	mtk_w32(eth, 0x0105e33b, MTK_MAC_MCR(0));
+ 
+-	/* GE2, Force 1000M/FD, FC ON */
+-	mtk_w32(eth, MAC_MCR_FIXED_LINK, MTK_MAC_MCR(1));
++	/* GE2, use autopolling */
++	mtk_w32(eth, 0x01056300, MTK_MAC_MCR(1));
+ 
+ 	/* Enable RX VLan Offloading */
+ 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
+@@ -1377,6 +1347,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ 	if (err)
+ 		return err;
+ 
++	mtk_gsw_init(eth);
++
+ 	/* disable delay and normal interrupt */
+ 	mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ 	mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+@@ -1404,6 +1376,8 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
+ 		mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
+ 	}
+ 
++	mt7623_gsw_config(eth);
++
+ 	return 0;
+ }
+ 
+@@ -1433,7 +1407,8 @@ static void mtk_uninit(struct net_device *dev)
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 	struct mtk_eth *eth = mac->hw;
+ 
+-	phy_disconnect(mac->phy_dev);
++	if (mac->phy_dev)
++		phy_disconnect(mac->phy_dev);
+ 	mtk_mdio_cleanup(eth);
+ 	mtk_irq_disable(eth, ~0);
+ 	free_irq(eth->irq[0], dev);
+@@ -1445,7 +1420,7 @@ static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 
+-	switch (cmd) {
++	if (mac->phy_dev) switch (cmd) {
+ 	case SIOCGMIIPHY:
+ 	case SIOCGMIIREG:
+ 	case SIOCSMIIREG:
+@@ -1508,9 +1483,10 @@ static int mtk_get_settings(struct net_device *dev,
+ 			    struct ethtool_cmd *cmd)
+ {
+ 	struct mtk_mac *mac = netdev_priv(dev);
+-	int err;
++	int err = -1;
+ 
+-	err = phy_read_status(mac->phy_dev);
++	if (mac->phy_dev)
++		err = phy_read_status(mac->phy_dev);
+ 	if (err)
+ 		return -ENODEV;
+ 
+@@ -1522,11 +1498,16 @@ static int mtk_set_settings(struct net_device *dev,
+ {
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 
+-	if (cmd->phy_address != mac->phy_dev->mdio.addr) {
+-		mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus,
+-					       cmd->phy_address);
+-		if (!mac->phy_dev)
++	if (!mac->phy_dev)
++		return -ENODEV;
++
++	if (cmd->phy_address != mac->phy_dev->addr) {
++		if (mac->hw->mii_bus->phy_map[cmd->phy_address]) {
++			mac->phy_dev =
++				mac->hw->mii_bus->phy_map[cmd->phy_address];
++		} else {
+ 			return -ENODEV;
++		}
+ 	}
+ 
+ 	return phy_ethtool_sset(mac->phy_dev, cmd);
+@@ -1560,6 +1541,9 @@ static int mtk_nway_reset(struct net_device *dev)
+ {
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 
++	if (!mac->phy_dev)
++		return -ENODEV;
++
+ 	return genphy_restart_aneg(mac->phy_dev);
+ }
+ 
+@@ -1568,6 +1552,9 @@ static u32 mtk_get_link(struct net_device *dev)
+ 	struct mtk_mac *mac = netdev_priv(dev);
+ 	int err;
+ 
++	if (!mac->phy_dev)
++		return -ENODEV;
++
+ 	err = genphy_update_link(mac->phy_dev);
+ 	if (err)
+ 		return ethtool_op_get_link(dev);
+@@ -1619,7 +1606,6 @@ static void mtk_get_ethtool_stats(struct net_device *dev,
+ 		data_src = (u64*)hwstats;
+ 		data_dst = data;
+ 		start = u64_stats_fetch_begin_irq(&hwstats->syncp);
+-
+ 		for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++)
+ 			*data_dst++ = *(data_src + mtk_ethtool_stats[i].offset);
+ 	} while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
+@@ -1789,6 +1775,9 @@ static int mtk_probe(struct platform_device *pdev)
+ 	clk_prepare_enable(eth->clk_gp1);
+ 	clk_prepare_enable(eth->clk_gp2);
+ 
++	eth->switch_np = of_parse_phandle(pdev->dev.of_node,
++					  "mediatek,switch", 0);
++
+ 	eth->dev = &pdev->dev;
+ 	eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+ 	INIT_WORK(&eth->pending_work, mtk_pending_work);
+diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+index 4cfb40c..bbe0346 100644
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -401,6 +401,9 @@ struct mtk_eth {
+ 	struct mii_bus			*mii_bus;
+ 	struct work_struct		pending_work;
+ 	struct tasklet_struct		tx_clean_tasklet;
++
++	struct device_node		*switch_np;
++	void				*sw_priv;
+ };
+ 
+ /* struct mtk_mac -	the structure that holds the info about the MACs of the
+@@ -428,4 +431,6 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+ 
++int mt7623_gsw_config(struct mtk_eth *eth);
++
+ #endif /* MTK_ETH_H */
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0075-clk-mediatek-enable-critical-clocks.patch b/target/linux/mediatek/patches-4.4/0075-clk-mediatek-enable-critical-clocks.patch
new file mode 100644
index 0000000000..fe0c366c85
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0075-clk-mediatek-enable-critical-clocks.patch
@@ -0,0 +1,74 @@
+From b7a101f53b7cb0e05658e04accf9fd12512b5735 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 06:46:51 +0200
+Subject: [PATCH 75/78] clk: mediatek: enable critical clocks
+
+Signed-off-by: John Crispin <blogic@openwrt.org>
+---
+ drivers/clk/mediatek/clk-mt2701.c |   22 ++++++++++++++++++++--
+ 1 file changed, 20 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 812b347..1634288 100644
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -573,6 +573,20 @@ static const struct mtk_gate top_clks[] __initconst = {
+ 	GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div", 28),
+ };
+ 
++static struct clk_onecell_data *mt7623_top_clk_data __initdata;
++static struct clk_onecell_data *mt7623_pll_clk_data __initdata;
++
++static void __init mtk_clk_enable_critical(void)
++{
++	if (!mt7623_top_clk_data || !mt7623_pll_clk_data)
++		return;
++
++	clk_prepare_enable(mt7623_pll_clk_data->clks[CLK_APMIXED_ARMPLL]);
++	clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_MEM_SEL]);
++	clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_DDRPHYCFG_SEL]);
++	clk_prepare_enable(mt7623_top_clk_data->clks[CLK_TOP_RTC_SEL]);
++}
++
+ static void __init mtk_topckgen_init(struct device_node *node)
+ {
+ 	struct clk_onecell_data *clk_data;
+@@ -585,7 +599,7 @@ static void __init mtk_topckgen_init(struct device_node *node)
+ 		return;
+ 	}
+ 
+-	clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
++	mt7623_top_clk_data = clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
+ 
+ 	mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
+ 								clk_data);
+@@ -606,6 +620,8 @@ static void __init mtk_topckgen_init(struct device_node *node)
+ 	if (r)
+ 		pr_err("%s(): could not register clock provider: %d\n",
+ 			__func__, r);
++
++	mtk_clk_enable_critical();
+ }
+ CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt2701-topckgen", mtk_topckgen_init);
+ 
+@@ -1202,7 +1218,7 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
+ 	struct clk_onecell_data *clk_data;
+ 	int r;
+ 
+-	clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
++	mt7623_pll_clk_data = clk_data = mtk_alloc_clk_data(ARRAY_SIZE(apmixed_plls));
+ 	if (!clk_data)
+ 		return;
+ 
+@@ -1213,6 +1229,8 @@ static void __init mtk_apmixedsys_init(struct device_node *node)
+ 	if (r)
+ 		pr_err("%s(): could not register clock provider: %d\n",
+ 			__func__, r);
++
++	mtk_clk_enable_critical();
+ }
+ CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt2701-apmixedsys",
+ 							mtk_apmixedsys_init);
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0076-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch b/target/linux/mediatek/patches-4.4/0076-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
new file mode 100644
index 0000000000..ee3ba76e33
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0076-clk-mediatek-Export-CPU-mux-clocks-for-CPU-frequency.patch
@@ -0,0 +1,306 @@
+From cc94bef897241da9b978c9799defbdbabe9ff6ec Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 02:26:37 +0200
+Subject: [PATCH 76/78] clk: mediatek: Export CPU mux clocks for CPU frequency
+ control
+
+This patch adds CPU mux clocks which are used by Mediatek cpufreq driver
+for intermediate clock source switching.
+
+Signed-off-by: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
+---
+ drivers/clk/mediatek/Makefile          |    2 +-
+ drivers/clk/mediatek/clk-cpumux.c      |  127 ++++++++++++++++++++++++++++++++
+ drivers/clk/mediatek/clk-cpumux.h      |   22 ++++++
+ drivers/clk/mediatek/clk-mt2701.c      |    8 ++
+ drivers/clk/mediatek/clk-mt8173.c      |   23 ++++++
+ include/dt-bindings/clock/mt2701-clk.h |    3 +-
+ include/dt-bindings/clock/mt8173-clk.h |    4 +-
+ 7 files changed, 186 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/clk/mediatek/clk-cpumux.c
+ create mode 100644 drivers/clk/mediatek/clk-cpumux.h
+
+diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
+index 5b2b91b..76bfab6 100644
+--- a/drivers/clk/mediatek/Makefile
++++ b/drivers/clk/mediatek/Makefile
+@@ -1,4 +1,4 @@
+-obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
++obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o
+ obj-$(CONFIG_RESET_CONTROLLER) += reset.o
+ obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
+ obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c
+new file mode 100644
+index 0000000..91b5238
+--- /dev/null
++++ b/drivers/clk/mediatek/clk-cpumux.c
+@@ -0,0 +1,127 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/mfd/syscon.h>
++#include <linux/slab.h>
++
++#include "clk-mtk.h"
++#include "clk-cpumux.h"
++
++struct mtk_clk_cpumux {
++	struct clk_hw	hw;
++	struct regmap	*regmap;
++	u32		reg;
++	u32		mask;
++	u8		shift;
++};
++
++static inline struct mtk_clk_cpumux *to_clk_mux(struct clk_hw *_hw)
++{
++	return container_of(_hw, struct mtk_clk_cpumux, hw);
++}
++
++static u8 clk_cpumux_get_parent(struct clk_hw *hw)
++{
++	struct mtk_clk_cpumux *mux = to_clk_mux(hw);
++	int num_parents = clk_hw_get_num_parents(hw);
++	unsigned int val;
++
++	regmap_read(mux->regmap, mux->reg, &val);
++
++	val >>= mux->shift;
++	val &= mux->mask;
++
++	if (val >= num_parents)
++		return -EINVAL;
++
++	return val;
++}
++
++static int clk_cpumux_set_parent(struct clk_hw *hw, u8 index)
++{
++	struct mtk_clk_cpumux *mux = to_clk_mux(hw);
++	u32 mask, val;
++
++	val = index << mux->shift;
++	mask = mux->mask << mux->shift;
++
++	return regmap_update_bits(mux->regmap, mux->reg, mask, val);
++}
++
++static const struct clk_ops clk_cpumux_ops = {
++	.get_parent = clk_cpumux_get_parent,
++	.set_parent = clk_cpumux_set_parent,
++};
++
++static struct clk __init *mtk_clk_register_cpumux(const struct mtk_composite *mux,
++					   struct regmap *regmap)
++{
++	struct mtk_clk_cpumux *cpumux;
++	struct clk *clk;
++	struct clk_init_data init;
++
++	cpumux = kzalloc(sizeof(*cpumux), GFP_KERNEL);
++	if (!cpumux)
++		return ERR_PTR(-ENOMEM);
++
++	init.name = mux->name;
++	init.ops = &clk_cpumux_ops;
++	init.parent_names = mux->parent_names;
++	init.num_parents = mux->num_parents;
++	init.flags = mux->flags;
++
++	cpumux->reg = mux->mux_reg;
++	cpumux->shift = mux->mux_shift;
++	cpumux->mask = BIT(mux->mux_width) - 1;
++	cpumux->regmap = regmap;
++	cpumux->hw.init = &init;
++
++	clk = clk_register(NULL, &cpumux->hw);
++	if (IS_ERR(clk))
++		kfree(cpumux);
++
++	return clk;
++}
++
++int __init mtk_clk_register_cpumuxes(struct device_node *node,
++			      const struct mtk_composite *clks, int num,
++			      struct clk_onecell_data *clk_data)
++{
++	int i;
++	struct clk *clk;
++	struct regmap *regmap;
++
++	regmap = syscon_node_to_regmap(node);
++	if (IS_ERR(regmap)) {
++		pr_err("Cannot find regmap for %s: %ld\n", node->full_name,
++		       PTR_ERR(regmap));
++		return PTR_ERR(regmap);
++	}
++
++	for (i = 0; i < num; i++) {
++		const struct mtk_composite *mux = &clks[i];
++
++		clk = mtk_clk_register_cpumux(mux, regmap);
++		if (IS_ERR(clk)) {
++			pr_err("Failed to register clk %s: %ld\n",
++			       mux->name, PTR_ERR(clk));
++			continue;
++		}
++
++		clk_data->clks[mux->id] = clk;
++	}
++
++	return 0;
++}
+diff --git a/drivers/clk/mediatek/clk-cpumux.h b/drivers/clk/mediatek/clk-cpumux.h
+new file mode 100644
+index 0000000..52c769f
+--- /dev/null
++++ b/drivers/clk/mediatek/clk-cpumux.h
+@@ -0,0 +1,22 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __DRV_CLK_CPUMUX_H
++#define __DRV_CLK_CPUMUX_H
++
++int mtk_clk_register_cpumuxes(struct device_node *node,
++			      const struct mtk_composite *clks, int num,
++			      struct clk_onecell_data *clk_data);
++
++#endif /* __DRV_CLK_CPUMUX_H */
+diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
+index 1634288..5c37fcb 100644
+--- a/drivers/clk/mediatek/clk-mt2701.c
++++ b/drivers/clk/mediatek/clk-mt2701.c
+@@ -18,6 +18,7 @@
+ 
+ #include "clk-mtk.h"
+ #include "clk-gate.h"
++#include "clk-cpumux.h"
+ 
+ #include <dt-bindings/clock/mt2701-clk.h>
+ 
+@@ -465,6 +466,10 @@ static const char * const cpu_parents[] __initconst = {
+ 	"mmpll"
+ };
+ 
++static const struct mtk_composite cpu_muxes[] __initconst = {
++	MUX(CLK_INFRA_CPUSEL, "infra_cpu_sel", cpu_parents, 0x0000, 2, 2),
++};
++
+ static const struct mtk_composite top_muxes[] __initconst = {
+ 	MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+ 		0x0040, 0, 3, INVALID_MUX_GATE_BIT),
+@@ -677,6 +682,9 @@ static void __init mtk_infrasys_init(struct device_node *node)
+ 	mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
+ 						clk_data);
+ 
++	mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
++						clk_data);
++
+ 	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ 	if (r)
+ 		pr_err("%s(): could not register clock provider: %d\n",
+diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c
+index 227e356..dfb109f 100644
+--- a/drivers/clk/mediatek/clk-mt8173.c
++++ b/drivers/clk/mediatek/clk-mt8173.c
+@@ -18,6 +18,7 @@
+ 
+ #include "clk-mtk.h"
+ #include "clk-gate.h"
++#include "clk-cpumux.h"
+ 
+ #include <dt-bindings/clock/mt8173-clk.h>
+ 
+@@ -526,6 +527,25 @@ static const char * const i2s3_b_ck_parents[] __initconst = {
+ 	"apll2_div5"
+ };
+ 
++static const char * const ca53_parents[] __initconst = {
++	"clk26m",
++	"armca7pll",
++	"mainpll",
++	"univpll"
++};
++
++static const char * const ca57_parents[] __initconst = {
++	"clk26m",
++	"armca15pll",
++	"mainpll",
++	"univpll"
++};
++
++static const struct mtk_composite cpu_muxes[] __initdata = {
++	MUX(CLK_INFRA_CA53SEL, "infra_ca53_sel", ca53_parents, 0x0000, 0, 2),
++	MUX(CLK_INFRA_CA57SEL, "infra_ca57_sel", ca57_parents, 0x0000, 2, 2),
++};
++
+ static const struct mtk_composite top_muxes[] __initconst = {
+ 	/* CLK_CFG_0 */
+ 	MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3),
+@@ -945,6 +965,9 @@ static void __init mtk_infrasys_init(struct device_node *node)
+ 						clk_data);
+ 	mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data);
+ 
++	mtk_clk_register_cpumuxes(node, cpu_muxes, ARRAY_SIZE(cpu_muxes),
++						clk_data);
++
+ 	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+ 	if (r)
+ 		pr_err("%s(): could not register clock provider: %d\n",
+diff --git a/include/dt-bindings/clock/mt2701-clk.h b/include/dt-bindings/clock/mt2701-clk.h
+index 50972d1..a6c63b8 100644
+--- a/include/dt-bindings/clock/mt2701-clk.h
++++ b/include/dt-bindings/clock/mt2701-clk.h
+@@ -217,7 +217,8 @@
+ #define CLK_INFRA_PMICWRAP			17
+ #define CLK_INFRA_DDCCI				18
+ #define CLK_INFRA_CLK_13M                       19
+-#define CLK_INFRA_NR				20
++#define CLK_INFRA_CPUSEL			20
++#define CLK_INFRA_NR				21
+ 
+ /* PERICFG */
+ 
+diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h
+index 7956ba1..c82ed7c 100644
+--- a/include/dt-bindings/clock/mt8173-clk.h
++++ b/include/dt-bindings/clock/mt8173-clk.h
+@@ -192,7 +192,9 @@
+ #define CLK_INFRA_PMICSPI		10
+ #define CLK_INFRA_PMICWRAP		11
+ #define CLK_INFRA_CLK_13M		12
+-#define CLK_INFRA_NR_CLK		13
++#define CLK_INFRA_CA53SEL		13
++#define CLK_INFRA_CA57SEL		14
++#define CLK_INFRA_NR_CLK		15
+ 
+ /* PERI_SYS */
+ 
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0077-cpufreq-mediatek-add-driver.patch b/target/linux/mediatek/patches-4.4/0077-cpufreq-mediatek-add-driver.patch
new file mode 100644
index 0000000000..4d8ed58334
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0077-cpufreq-mediatek-add-driver.patch
@@ -0,0 +1,727 @@
+From d1421147c328a7d06d9a6b8330c73e45139b1e48 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Wed, 30 Mar 2016 23:48:53 +0200
+Subject: [PATCH 77/78] cpufreq: mediatek: add driver
+
+Signed-off-by: John Crispin <john@phrozen.org>
+---
+ arch/arm/boot/dts/mt7623-evb.dts |  160 ++++++++++++----
+ arch/arm/boot/dts/mt7623.dtsi    |   50 ++++-
+ drivers/cpufreq/Kconfig.arm      |    9 +
+ drivers/cpufreq/Makefile         |    1 +
+ drivers/cpufreq/mt7623-cpufreq.c |  389 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 570 insertions(+), 39 deletions(-)
+ create mode 100644 drivers/cpufreq/mt7623-cpufreq.c
+
+diff --git a/arch/arm/boot/dts/mt7623-evb.dts b/arch/arm/boot/dts/mt7623-evb.dts
+index bc2b3f1..4a433f0 100644
+--- a/arch/arm/boot/dts/mt7623-evb.dts
++++ b/arch/arm/boot/dts/mt7623-evb.dts
+@@ -39,6 +39,22 @@
+ 	};
+ };
+ 
++&cpu0 {
++	proc-supply = <&mt6323_vproc_reg>;
++};
++
++&cpu1 {
++	proc-supply = <&mt6323_vproc_reg>;
++};
++
++&cpu2 {
++	proc-supply = <&mt6323_vproc_reg>;
++};
++
++&cpu3 {
++	proc-supply = <&mt6323_vproc_reg>;
++};
++
+ &pwrap {
+ 	pmic: mt6323 {
+ 		compatible = "mediatek,mt6323";
+@@ -267,38 +283,36 @@
+ 	};
+ };
+ 
+-&uart2 {
+-	status = "okay";
+-};
++&pio {
++	nand_pins_default: nanddefault {
++		pins_dat {
++			pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_NLD7>,
++				 <MT7623_PIN_112_MSDC0_DAT6_FUNC_NLD6>,
++				 <MT7623_PIN_114_MSDC0_DAT4_FUNC_NLD4>,
++				 <MT7623_PIN_118_MSDC0_DAT3_FUNC_NLD3>,
++				 <MT7623_PIN_121_MSDC0_DAT0_FUNC_NLD0>,
++				 <MT7623_PIN_120_MSDC0_DAT1_FUNC_NLD1>,
++				 <MT7623_PIN_113_MSDC0_DAT5_FUNC_NLD5>,
++				 <MT7623_PIN_115_MSDC0_RSTB_FUNC_NLD8>,
++				 <MT7623_PIN_119_MSDC0_DAT2_FUNC_NLD2>;
++			input-enable;
++			drive-strength = <MTK_DRIVE_8mA>;
++			bias-pull-up;
++		};
+ 
+-&mmc0 {
+-	status = "okay";
+-	pinctrl-names = "default", "state_uhs";
+-	pinctrl-0 = <&mmc0_pins_default>;
+-	pinctrl-1 = <&mmc0_pins_uhs>;
+-	bus-width = <8>;
+-	max-frequency = <50000000>;
+-	cap-mmc-highspeed;
+-	vmmc-supply = <&mt6323_vemc3v3_reg>;
+-	vqmmc-supply = <&mt6323_vio18_reg>;
+-	non-removable;
+-};
++		pins_we {
++			pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_NWEB>;
++			drive-strength = <MTK_DRIVE_8mA>;
++			bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
++		};
+ 
+-&mmc1 {
+-	status = "okay";
+-	pinctrl-names = "default", "state_uhs";
+-	pinctrl-0 = <&mmc1_pins_default>;
+-	pinctrl-1 = <&mmc1_pins_uhs>;
+-	bus-width = <4>;
+-	max-frequency = <50000000>;
+-	cap-sd-highspeed;
+-	sd-uhs-sdr25;
+-//	cd-gpios = <&pio 132 0>;
+-	vmmc-supply = <&mt6323_vmch_reg>;
+-	vqmmc-supply = <&mt6323_vmc_reg>;
+-};
++		pins_ale {
++			pinmux = <MT7623_PIN_116_MSDC0_CMD_FUNC_NALE>;
++			drive-strength = <MTK_DRIVE_8mA>;
++			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
++		};
++	};
+ 
+-&pio {
+ 	mmc0_pins_default: mmc0default {
+ 		pins_cmd_dat {
+ 			pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_MSDC0_DAT7>,
+@@ -370,11 +384,6 @@
+ 			bias-pull-down;
+ 			drive-strength = <MTK_DRIVE_4mA>;
+ 		};
+-
+-//		pins_insert {
+-//			pinmux = <MT8173_PIN_132_I2S0_DATA1_FUNC_GPIO132>;
+-//			bias-pull-up;
+-//		};
+ 	};
+ 
+ 	mmc1_pins_uhs: mmc1 {
+@@ -422,6 +431,36 @@
+ 	};
+ };
+ 
++&uart2 {
++	status = "okay";
++};
++
++&mmc0 {
++	status = "okay";
++	pinctrl-names = "default", "state_uhs";
++	pinctrl-0 = <&mmc0_pins_default>;
++	pinctrl-1 = <&mmc0_pins_uhs>;
++	bus-width = <8>;
++	max-frequency = <50000000>;
++	cap-mmc-highspeed;
++	vmmc-supply = <&mt6323_vemc3v3_reg>;
++	vqmmc-supply = <&mt6323_vio18_reg>;
++	non-removable;
++};
++
++&mmc1 {
++	status = "okay";
++	pinctrl-names = "default", "state_uhs";
++	pinctrl-0 = <&mmc1_pins_default>;
++	pinctrl-1 = <&mmc1_pins_uhs>;
++	bus-width = <4>;
++	max-frequency = <50000000>;
++	cap-sd-highspeed;
++	sd-uhs-sdr25;
++	vmmc-supply = <&mt6323_vmch_reg>;
++	vqmmc-supply = <&mt6323_vmc_reg>;
++};
++
+ &usb1 {
+ 	vusb33-supply = <&mt6323_vusb_reg>;
+ 	vbus-supply = <&usb_p1_vbus>;
+@@ -456,3 +495,56 @@
+ 	mediatek,reset-pin = <&pio 15 0>;
+ 	status = "okay";
+ };
++
++&nand {
++	status = "okay";
++	#address-cells = <1>;
++	#size-cells = <1>;
++
++	pinctrl-names = "default";
++	pinctrl-0 = <&nand_pins_default>;
++
++	partition@0 {
++		label = "preloader";
++		reg = <0x0 0x40000>;
++		read-only;
++	};
++
++	partition@1 {
++		label = "u-boot";
++		reg = <0x40000 0x80000>;
++		read-only;
++	};
++
++	partition@2 {
++		label = "u-boot-env";
++		reg = <0xc0000 0x40000>;
++		read-only;
++	};
++
++	partition@3 {
++		label = "factory";
++		reg = <0x100000 0x40000>;
++		read-only;
++	};
++
++	partition@4 {
++		label = "kernel";
++		reg = <0x140000 0x2000000>;
++	};
++
++	partition@4 {
++		label = "kernel2";
++		reg = <0x2140000 0x2000000>;
++	};
++
++	partition@5 {
++		label = "rootfs";
++		reg = <0x4140000 0x1000000>;
++	};
++
++	partition@6 {
++		label = "usrdata";
++		reg = <0x5140000 0x9f80000>;
++	};
++};
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+index f405ec7..76d603a 100644
+--- a/arch/arm/boot/dts/mt7623.dtsi
++++ b/arch/arm/boot/dts/mt7623.dtsi
+@@ -31,25 +31,65 @@
+ 		#size-cells = <0>;
+ 		enable-method = "mediatek,mt6589-smp";
+ 
+-		cpu@0 {
++		cpu0: cpu@0 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+ 			reg = <0x0>;
++			clocks = <&infracfg CLK_INFRA_CPUSEL>,
++				 <&apmixedsys CLK_APMIXED_MAINPLL>;
++			clock-names = "cpu", "intermediate";
++			operating-points = <
++				598000 1150000
++				747500 1150000
++				1040000 1150000
++				1196000 1200000
++				1300000 1300000
++			>;
+ 		};
+-		cpu@1 {
++		cpu1: cpu@1 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+ 			reg = <0x1>;
++			clocks = <&infracfg CLK_INFRA_CPUSEL>,
++				 <&apmixedsys CLK_APMIXED_MAINPLL>;
++			clock-names = "cpu", "intermediate";
++			operating-points = <
++				598000 1150000
++				747500 1150000
++				1040000 1150000
++				1196000 1200000
++				1300000 1300000
++			>;
+ 		};
+-		cpu@2 {
++		cpu2: cpu@2 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+ 			reg = <0x2>;
++			clocks = <&infracfg CLK_INFRA_CPUSEL>,
++				 <&apmixedsys CLK_APMIXED_MAINPLL>;
++			clock-names = "cpu", "intermediate";
++			operating-points = <
++				598000 1150000
++				747500 1150000
++				1040000 1150000
++				1196000 1200000
++				1300000 1300000
++			>;
+ 		};
+-		cpu@3 {
++		cpu3: cpu@3 {
+ 			device_type = "cpu";
+ 			compatible = "arm,cortex-a7";
+ 			reg = <0x3>;
++			clocks = <&infracfg CLK_INFRA_CPUSEL>,
++				 <&apmixedsys CLK_APMIXED_MAINPLL>;
++			clock-names = "cpu", "intermediate";
++			operating-points = <
++				598000 1150000
++				747500 1150000
++				1040000 1150000
++				1196000 1200000
++				1300000 1300000
++			>;
+ 		};
+ 	};
+ 
+@@ -300,7 +340,7 @@
+ 		clocks = <&pericfg CLK_PERI_NFI>, <&pericfg CLK_PERI_NFI_ECC>,
+ 			 <&pericfg CLK_PERI_NFI_PAD>;
+ 		clock-names = "nfi_clk", "nfiecc_clk", "pad_clk";
+-		nand-on-flash-bbt;
++	//	nand-on-flash-bbt;
+ 		status = "disabled";
+ 	};
+ 
+diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
+index b1f8a73..baf945e 100644
+--- a/drivers/cpufreq/Kconfig.arm
++++ b/drivers/cpufreq/Kconfig.arm
+@@ -81,6 +81,15 @@ config ARM_KIRKWOOD_CPUFREQ
+ 	  This adds the CPUFreq driver for Marvell Kirkwood
+ 	  SoCs.
+ 
++config ARM_MT7623_CPUFREQ
++	bool "Mediatek MT7623 CPUFreq support"
++	depends on ARCH_MEDIATEK && REGULATOR
++	depends on ARM || (ARM_CPU_TOPOLOGY && COMPILE_TEST)
++	depends on !CPU_THERMAL || THERMAL=y
++	select PM_OPP
++	help
++	  This adds the CPUFreq driver support for Mediatek MT7623 SoC.
++
+ config ARM_MT8173_CPUFREQ
+ 	bool "Mediatek MT8173 CPUFreq support"
+ 	depends on ARCH_MEDIATEK && REGULATOR
+diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
+index c0af1a1..e198752 100644
+--- a/drivers/cpufreq/Makefile
++++ b/drivers/cpufreq/Makefile
+@@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ)	+= hisi-acpu-cpufreq.o
+ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ)		+= imx6q-cpufreq.o
+ obj-$(CONFIG_ARM_INTEGRATOR)		+= integrator-cpufreq.o
+ obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
++obj-$(CONFIG_ARM_MT7623_CPUFREQ)	+= mt7623-cpufreq.o
+ obj-$(CONFIG_ARM_MT8173_CPUFREQ)	+= mt8173-cpufreq.o
+ obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
+ obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)	+= pxa2xx-cpufreq.o
+diff --git a/drivers/cpufreq/mt7623-cpufreq.c b/drivers/cpufreq/mt7623-cpufreq.c
+new file mode 100644
+index 0000000..8d154ce
+--- /dev/null
++++ b/drivers/cpufreq/mt7623-cpufreq.c
+@@ -0,0 +1,389 @@
++/*
++ * Copyright (c) 2015 Linaro Ltd.
++ * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
++ *
++ * 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 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 the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/cpu.h>
++#include <linux/cpu_cooling.h>
++#include <linux/cpufreq.h>
++#include <linux/cpumask.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_opp.h>
++#include <linux/regulator/consumer.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++#define VOLT_TOL		(10000)
++
++/*
++ * When scaling the clock frequency of a CPU clock domain, the clock source
++ * needs to be switched to another stable PLL clock temporarily until
++ * the original PLL becomes stable at target frequency.
++ */
++struct mtk_cpu_dvfs_info {
++	struct device *cpu_dev;
++	struct regulator *proc_reg;
++	struct clk *cpu_clk;
++	struct clk *inter_clk;
++	struct thermal_cooling_device *cdev;
++	int intermediate_voltage;
++};
++
++static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
++{
++	return regulator_set_voltage(info->proc_reg, vproc,
++				     vproc + VOLT_TOL);
++}
++
++static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
++				  unsigned int index)
++{
++	struct cpufreq_frequency_table *freq_table = policy->freq_table;
++	struct clk *cpu_clk = policy->clk;
++	struct clk *armpll = clk_get_parent(cpu_clk);
++	struct mtk_cpu_dvfs_info *info = policy->driver_data;
++	struct device *cpu_dev = info->cpu_dev;
++	struct dev_pm_opp *opp;
++	long freq_hz, old_freq_hz;
++	int vproc, old_vproc, inter_vproc, target_vproc, ret;
++
++	inter_vproc = info->intermediate_voltage;
++
++	old_freq_hz = clk_get_rate(cpu_clk);
++	old_vproc = regulator_get_voltage(info->proc_reg);
++
++	freq_hz = freq_table[index].frequency * 1000;
++
++	rcu_read_lock();
++	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
++	if (IS_ERR(opp)) {
++		rcu_read_unlock();
++		pr_err("cpu%d: failed to find OPP for %ld\n",
++		       policy->cpu, freq_hz);
++		return PTR_ERR(opp);
++	}
++	vproc = dev_pm_opp_get_voltage(opp);
++	rcu_read_unlock();
++
++	/*
++	 * If the new voltage or the intermediate voltage is higher than the
++	 * current voltage, scale up voltage first.
++	 */
++	target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
++	if (old_vproc < target_vproc) {
++		ret = mtk_cpufreq_set_voltage(info, target_vproc);
++		if (ret) {
++			pr_err("cpu%d: failed to scale up voltage!\n",
++			       policy->cpu);
++			mtk_cpufreq_set_voltage(info, old_vproc);
++			return ret;
++		}
++	}
++
++	/* Reparent the CPU clock to intermediate clock. */
++	ret = clk_set_parent(cpu_clk, info->inter_clk);
++	if (ret) {
++		pr_err("cpu%d: failed to re-parent cpu clock!\n",
++		       policy->cpu);
++		mtk_cpufreq_set_voltage(info, old_vproc);
++		WARN_ON(1);
++		return ret;
++	}
++
++	/* Set the original PLL to target rate. */
++	ret = clk_set_rate(armpll, freq_hz);
++	if (ret) {
++		pr_err("cpu%d: failed to scale cpu clock rate!\n",
++		       policy->cpu);
++		clk_set_parent(cpu_clk, armpll);
++		mtk_cpufreq_set_voltage(info, old_vproc);
++		return ret;
++	}
++
++	/* Set parent of CPU clock back to the original PLL. */
++	ret = clk_set_parent(cpu_clk, armpll);
++	if (ret) {
++		pr_err("cpu%d: failed to re-parent cpu clock!\n",
++		       policy->cpu);
++		mtk_cpufreq_set_voltage(info, inter_vproc);
++		WARN_ON(1);
++		return ret;
++	}
++
++	/*
++	 * If the new voltage is lower than the intermediate voltage or the
++	 * original voltage, scale down to the new voltage.
++	 */
++	if (vproc < inter_vproc || vproc < old_vproc) {
++		ret = mtk_cpufreq_set_voltage(info, vproc);
++		if (ret) {
++			pr_err("cpu%d: failed to scale down voltage!\n",
++			       policy->cpu);
++			clk_set_parent(cpu_clk, info->inter_clk);
++			clk_set_rate(armpll, old_freq_hz);
++			clk_set_parent(cpu_clk, armpll);
++			return ret;
++		}
++	}
++
++	return 0;
++}
++
++static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
++{
++	struct mtk_cpu_dvfs_info *info = policy->driver_data;
++	struct device_node *np = of_node_get(info->cpu_dev->of_node);
++
++	if (WARN_ON(!np))
++		return;
++
++	if (of_find_property(np, "#cooling-cells", NULL)) {
++		info->cdev = of_cpufreq_cooling_register(np,
++							 policy->related_cpus);
++
++		if (IS_ERR(info->cdev)) {
++			dev_err(info->cpu_dev,
++				"running cpufreq without cooling device: %ld\n",
++				PTR_ERR(info->cdev));
++
++			info->cdev = NULL;
++		}
++	}
++
++	of_node_put(np);
++}
++
++static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
++{
++	struct device *cpu_dev;
++	struct regulator *proc_reg = ERR_PTR(-ENODEV);
++	struct clk *cpu_clk = ERR_PTR(-ENODEV);
++	struct clk *inter_clk = ERR_PTR(-ENODEV);
++	struct dev_pm_opp *opp;
++	unsigned long rate;
++	int ret;
++
++	cpu_dev = get_cpu_device(cpu);
++	if (!cpu_dev) {
++		pr_err("failed to get cpu%d device\n", cpu);
++		return -ENODEV;
++	}
++
++	cpu_clk = clk_get(cpu_dev, "cpu");
++	if (IS_ERR(cpu_clk)) {
++		if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
++			pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
++		else
++			pr_err("failed to get cpu clk for cpu%d\n", cpu);
++
++		ret = PTR_ERR(cpu_clk);
++		return ret;
++	}
++
++	inter_clk = clk_get(cpu_dev, "intermediate");
++	if (IS_ERR(inter_clk)) {
++		if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
++			pr_warn("intermediate clk for cpu%d not ready, retry.\n",
++				cpu);
++		else
++			pr_err("failed to get intermediate clk for cpu%d\n",
++			       cpu);
++
++		ret = PTR_ERR(inter_clk);
++		goto out_free_resources;
++	}
++
++	proc_reg = regulator_get_exclusive(cpu_dev, "proc");
++	if (IS_ERR(proc_reg)) {
++		if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
++			pr_warn("proc regulator for cpu%d not ready, retry.\n",
++				cpu);
++		else
++			pr_err("failed to get proc regulator for cpu%d\n",
++			       cpu);
++
++		ret = PTR_ERR(proc_reg);
++		goto out_free_resources;
++	}
++
++	ret = dev_pm_opp_of_add_table(cpu_dev);
++	if (ret) {
++		pr_warn("no OPP table for cpu%d\n", cpu);
++		goto out_free_resources;
++	}
++
++	/* Search a safe voltage for intermediate frequency. */
++	rate = clk_get_rate(inter_clk);
++	rcu_read_lock();
++	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
++	if (IS_ERR(opp)) {
++		rcu_read_unlock();
++		pr_err("failed to get intermediate opp for cpu%d\n", cpu);
++		ret = PTR_ERR(opp);
++		goto out_free_opp_table;
++	}
++	info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
++	rcu_read_unlock();
++
++	info->cpu_dev = cpu_dev;
++	info->proc_reg = proc_reg;
++	info->cpu_clk = cpu_clk;
++	info->inter_clk = inter_clk;
++
++	return 0;
++
++out_free_opp_table:
++	dev_pm_opp_of_remove_table(cpu_dev);
++
++out_free_resources:
++	if (!IS_ERR(proc_reg))
++		regulator_put(proc_reg);
++	if (!IS_ERR(cpu_clk))
++		clk_put(cpu_clk);
++	if (!IS_ERR(inter_clk))
++		clk_put(inter_clk);
++
++	return ret;
++}
++
++static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
++{
++	if (!IS_ERR(info->proc_reg))
++		regulator_put(info->proc_reg);
++	if (!IS_ERR(info->cpu_clk))
++		clk_put(info->cpu_clk);
++	if (!IS_ERR(info->inter_clk))
++		clk_put(info->inter_clk);
++
++	dev_pm_opp_of_remove_table(info->cpu_dev);
++}
++
++static int mtk_cpufreq_init(struct cpufreq_policy *policy)
++{
++	struct mtk_cpu_dvfs_info *info;
++	struct cpufreq_frequency_table *freq_table;
++	int ret;
++
++	info = kzalloc(sizeof(*info), GFP_KERNEL);
++	if (!info)
++		return -ENOMEM;
++
++	ret = mtk_cpu_dvfs_info_init(info, policy->cpu);
++	if (ret) {
++		pr_err("%s failed to initialize dvfs info for cpu%d\n",
++		       __func__, policy->cpu);
++		goto out_free_dvfs_info;
++	}
++
++	ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
++	if (ret) {
++		pr_err("failed to init cpufreq table for cpu%d: %d\n",
++		       policy->cpu, ret);
++		goto out_release_dvfs_info;
++	}
++
++	ret = cpufreq_table_validate_and_show(policy, freq_table);
++	if (ret) {
++		pr_err("%s: invalid frequency table: %d\n", __func__, ret);
++		goto out_free_cpufreq_table;
++	}
++
++	/* CPUs in the same cluster share a clock and power domain. */
++	cpumask_setall(policy->cpus);
++	policy->driver_data = info;
++	policy->clk = info->cpu_clk;
++
++	return 0;
++
++out_free_cpufreq_table:
++	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
++
++out_release_dvfs_info:
++	mtk_cpu_dvfs_info_release(info);
++
++out_free_dvfs_info:
++	kfree(info);
++
++	return ret;
++}
++
++static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
++{
++	struct mtk_cpu_dvfs_info *info = policy->driver_data;
++
++	cpufreq_cooling_unregister(info->cdev);
++	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
++	mtk_cpu_dvfs_info_release(info);
++	kfree(info);
++
++	return 0;
++}
++
++static struct cpufreq_driver mt7623_cpufreq_driver = {
++	.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
++	.verify = cpufreq_generic_frequency_table_verify,
++	.target_index = mtk_cpufreq_set_target,
++	.get = cpufreq_generic_get,
++	.init = mtk_cpufreq_init,
++	.exit = mtk_cpufreq_exit,
++	.ready = mtk_cpufreq_ready,
++	.name = "mtk-cpufreq",
++	.attr = cpufreq_generic_attr,
++};
++
++static int mt7623_cpufreq_probe(struct platform_device *pdev)
++{
++	int ret;
++
++	ret = cpufreq_register_driver(&mt7623_cpufreq_driver);
++	if (ret)
++		pr_err("failed to register mtk cpufreq driver\n");
++
++	return ret;
++}
++
++static struct platform_driver mt7623_cpufreq_platdrv = {
++	.driver = {
++		.name	= "mt7623-cpufreq",
++	},
++	.probe		= mt7623_cpufreq_probe,
++};
++
++static int mt7623_cpufreq_driver_init(void)
++{
++	struct platform_device *pdev;
++	int err;
++
++	if (!of_machine_is_compatible("mediatek,mt7623"))
++		return -ENODEV;
++
++	err = platform_driver_register(&mt7623_cpufreq_platdrv);
++	if (err)
++		return err;
++
++	/*
++	 * Since there's no place to hold device registration code and no
++	 * device tree based way to match cpufreq driver yet, both the driver
++	 * and the device registration codes are put here to handle defer
++	 * probing.
++	 */
++	pdev = platform_device_register_simple("mt7623-cpufreq", -1, NULL, 0);
++	if (IS_ERR(pdev)) {
++		pr_err("failed to register mtk-cpufreq platform device\n");
++		return PTR_ERR(pdev);
++	}
++
++	return 0;
++}
++device_initcall(mt7623_cpufreq_driver_init);
+-- 
+1.7.10.4
+
diff --git a/target/linux/mediatek/patches-4.4/0078-arm-mediatek-make-a7-timer-work.patch b/target/linux/mediatek/patches-4.4/0078-arm-mediatek-make-a7-timer-work.patch
new file mode 100644
index 0000000000..69117dc76b
--- /dev/null
+++ b/target/linux/mediatek/patches-4.4/0078-arm-mediatek-make-a7-timer-work.patch
@@ -0,0 +1,52 @@
+From e722886f122fd3dd6240160f21937d2f21e9d910 Mon Sep 17 00:00:00 2001
+From: John Crispin <blogic@openwrt.org>
+Date: Thu, 31 Mar 2016 06:07:01 +0200
+Subject: [PATCH 78/78] arm: mediatek: make a7 timer work Signed-off-by: John
+ Crispin <blogic@openwrt.org>
+
+---
+ arch/arm/boot/dts/mt7623.dtsi     |    2 ++
+ arch/arm/mach-mediatek/Kconfig    |    1 +
+ arch/arm/mach-mediatek/mediatek.c |    1 +
+ 3 files changed, 4 insertions(+)
+
+diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
+index 76d603a..cd08b6e 100644
+--- a/arch/arm/boot/dts/mt7623.dtsi
++++ b/arch/arm/boot/dts/mt7623.dtsi
+@@ -120,6 +120,8 @@
+ 			     <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ 			     <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ 			     <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
++		clock-frequency = <13000000>;
++		arm,cpu-registers-not-fw-configured;
+ 	};
+ 
+ 	topckgen: power-controller@10000000 {
+diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
+index a7fef77..2c05bc31 100644
+--- a/arch/arm/mach-mediatek/Kconfig
++++ b/arch/arm/mach-mediatek/Kconfig
+@@ -24,6 +24,7 @@ config MACH_MT6592
+ config MACH_MT7623
+ 	bool "MediaTek MT7623 SoCs support"
+ 	default ARCH_MEDIATEK
++	select HAVE_ARM_ARCH_TIMER
+ 	select MIGHT_HAVE_PCI
+ 
+ config MACH_MT8127
+diff --git a/arch/arm/mach-mediatek/mediatek.c b/arch/arm/mach-mediatek/mediatek.c
+index bcfca37..7553a8c 100644
+--- a/arch/arm/mach-mediatek/mediatek.c
++++ b/arch/arm/mach-mediatek/mediatek.c
+@@ -29,6 +29,7 @@ static void __init mediatek_timer_init(void)
+ 	void __iomem *gpt_base;
+ 
+ 	if (of_machine_is_compatible("mediatek,mt6589") ||
++	    of_machine_is_compatible("mediatek,mt7623") ||
+ 	    of_machine_is_compatible("mediatek,mt8135") ||
+ 	    of_machine_is_compatible("mediatek,mt8127")) {
+ 		/* turn on GPT6 which ungates arch timer clocks */
+-- 
+1.7.10.4
+