[][Add initial mtk feed for OpenWRT v21.02]

[Description]
Add initial mtk feed for OpenWRT v21.02

[Release-log]
N/A

Change-Id: I8051c6ba87f1ccf26c02fdd88a17d66f63c0b101
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/4495320
diff --git a/target/linux/mediatek/patches-5.4/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch b/target/linux/mediatek/patches-5.4/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch
new file mode 100644
index 0000000..3a9e061
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0020-dts-mt7622-enable-new-mtk-snand-for-ubi.patch
@@ -0,0 +1,23 @@
+--- a/arch/arm64/boot/dts/mediatek/mt7622.dtsi
++++ b/arch/arm64/boot/dts/mediatek/mt7622.dtsi
+@@ -567,6 +567,20 @@
+ 		status = "disabled";
+ 	};
+ 
++	snand: snfi@1100d000 {
++		compatible = "mediatek,mt7622-snand";
++		reg = <0 0x1100d000 0 0x1000>, <0 0x1100e000 0 0x1000>;
++		reg-names = "nfi", "ecc";
++		interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>;
++		clocks = <&pericfg CLK_PERI_NFI_PD>,
++			 <&pericfg CLK_PERI_SNFI_PD>,
++			 <&pericfg CLK_PERI_NFIECC_PD>;
++		clock-names = "nfi_clk", "pad_clk", "ecc_clk";
++		#address-cells = <1>;
++		#size-cells = <0>;
++		status = "disabled";
++	};
++
+ 	nor_flash: spi@11014000 {
+ 		compatible = "mediatek,mt7622-nor",
+ 			     "mediatek,mt8173-nor";
diff --git a/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch b/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch
new file mode 100755
index 0000000..e0ac7ab
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0100-hwnat_Kconfig_Makefile.patch
@@ -0,0 +1,33 @@
+--- a/net/Kconfig	2020-04-29 17:25:49.750444000 +0800
++++ b/net/Kconfig	2020-04-29 17:42:40.950424000 +0800
+@@ -451,6 +451,18 @@
+ 	  migration of VMs with direct attached VFs by failing over to the
+ 	  paravirtual datapath when the VF is unplugged.
+ 
++config HW_NAT
++	bool "HW NAT support"
++	default n
++	---help---
++	 This feature provides a fast path to support network lan/wan nat.
++	 If you need hw_nat engine to reduce cpu loading, please say Y.
++
++	  Note that the answer to this question doesn't directly affect the
++	  kernel: saying N will just cause the configurator to skip all
++	  the questions about Mediatek Ethernet devices. If you say Y,
++	  you will be asked for your specific card in the following questions.
++
+ endif   # if NET
+ 
+ # Used by archs to tell that they support BPF JIT compiler plus which flavour.
+--- a/net/Makefile	2020-04-23 16:36:46.000000000 +0800
++++ b/net/Makefile	2020-04-29 17:42:58.106487000 +0800
+@@ -62,6 +62,9 @@
+ obj-$(CONFIG_6LOWPAN)		+= 6lowpan/
+ obj-$(CONFIG_IEEE802154)	+= ieee802154/
+ obj-$(CONFIG_MAC802154)		+= mac802154/
++ifeq ($(CONFIG_HW_NAT),y)
++obj-y                           += nat/foe_hook/
++endif
+ 
+ ifeq ($(CONFIG_NET),y)
+ obj-$(CONFIG_SYSCTL)		+= sysctl_net.o
diff --git a/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch b/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch
new file mode 100644
index 0000000..98e5ab6
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0200-show_model_name_in_cpuinfo_on_arm64.patch
@@ -0,0 +1,16 @@
+Index: linux-5.4.70/arch/arm64/kernel/cpuinfo.c
+===================================================================
+--- linux-5.4.70.orig/arch/arm64/kernel/cpuinfo.c
++++ linux-5.4.70/arch/arm64/kernel/cpuinfo.c
+@@ -139,9 +139,8 @@ static int c_show(struct seq_file *m, vo
+ 		 * "processor".  Give glibc what it expects.
+ 		 */
+ 		seq_printf(m, "processor\t: %d\n", i);
+-		if (compat)
+-			seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
+-				   MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
++		seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
++			   MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
+ 
+ 		seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
+ 			   loops_per_jiffy / (500000UL/HZ),
diff --git a/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch b/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch
new file mode 100644
index 0000000..d52db50
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0504-macsec-revert-async-support.patch
@@ -0,0 +1,12 @@
+--- a/drivers/net/macsec.c
++++ b/drivers/net/macsec.c
+@@ -1309,8 +1309,7 @@
+ 	struct crypto_aead *tfm;
+ 	int ret;
+ 
+-	/* Pick a sync gcm(aes) cipher to ensure order is preserved. */
+-	tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
++	tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
+ 
+ 	if (IS_ERR(tfm))
+ 		return tfm;
diff --git a/target/linux/mediatek/patches-5.4/0666-spi-mediatek-support-IPM-Design.patch b/target/linux/mediatek/patches-5.4/0666-spi-mediatek-support-IPM-Design.patch
new file mode 100644
index 0000000..b8b2dc7
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0666-spi-mediatek-support-IPM-Design.patch
@@ -0,0 +1,636 @@
+From 675b477b2a50b2fb97f35944756f89644bf70092 Mon Sep 17 00:00:00 2001
+From: Qii Wang <qii.wang@mediatek.com>
+Date: Tue, 5 Jan 2021 16:48:39 +0800
+Subject: [PATCH] spi: mediatek: support IPM Design
+
+[Description]
+1. support sigle mode;
+2. support dual/quad mode with spi-mem framework.
+
+Signed-off-by: Leilk Liu <leilk.liu@mediatek.com>
+Reviewed-by: Qii Wang <qii.wang@mediatek.com>
+---
+ drivers/spi/spi-mt65xx.c                 | 395 +++++++++++++++++++++--
+ include/linux/platform_data/spi-mt65xx.h |   2 +-
+ 2 files changed, 370 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
+index 8acf24f7c..9183c64e4 100644
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -17,6 +17,7 @@
+ #include <linux/platform_data/spi-mt65xx.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/spi/spi.h>
++#include <linux/spi/spi-mem.h>
+ #include <linux/dma-mapping.h>
+ 
+ #define SPI_CFG0_REG                      0x0000
+@@ -31,6 +32,7 @@
+ #define SPI_CFG2_REG                      0x0028
+ #define SPI_TX_SRC_REG_64                 0x002c
+ #define SPI_RX_DST_REG_64                 0x0030
++#define SPI_CFG3_IPM_REG                  0x0040
+ 
+ #define SPI_CFG0_SCK_HIGH_OFFSET          0
+ #define SPI_CFG0_SCK_LOW_OFFSET           8
+@@ -42,13 +44,15 @@
+ #define SPI_CFG1_CS_IDLE_OFFSET           0
+ #define SPI_CFG1_PACKET_LOOP_OFFSET       8
+ #define SPI_CFG1_PACKET_LENGTH_OFFSET     16
+-#define SPI_CFG1_GET_TICK_DLY_OFFSET      30
++#define SPI_CFG1_GET_TICKDLY_OFFSET       29
+ 
++#define SPI_CFG1_GET_TICKDLY_MASK	  GENMASK(31, 29)
+ #define SPI_CFG1_CS_IDLE_MASK             0xff
+ #define SPI_CFG1_PACKET_LOOP_MASK         0xff00
+ #define SPI_CFG1_PACKET_LENGTH_MASK       0x3ff0000
++#define SPI_CFG1_IPM_PACKET_LENGTH_MASK   GENMASK(31, 16)
+ #define SPI_CFG2_SCK_HIGH_OFFSET          0
+-#define SPI_CFG2_SCK_LOW_OFFSET           16
++#define SPI_CFG2_SCK_LOW_OFFSET		  16
+ 
+ #define SPI_CMD_ACT                  BIT(0)
+ #define SPI_CMD_RESUME               BIT(1)
+@@ -67,6 +71,25 @@
+ #define SPI_CMD_TX_ENDIAN            BIT(15)
+ #define SPI_CMD_FINISH_IE            BIT(16)
+ #define SPI_CMD_PAUSE_IE             BIT(17)
++#define SPI_CMD_IPM_NONIDLE_MODE     BIT(19)
++#define SPI_CMD_IPM_SPIM_LOOP        BIT(21)
++#define SPI_CMD_IPM_GET_TICKDLY_OFFSET    22
++
++#define SPI_CMD_IPM_GET_TICKDLY_MASK	GENMASK(24, 22)
++
++#define PIN_MODE_CFG(x)	((x) / 2)
++
++#define SPI_CFG3_IPM_PIN_MODE_OFFSET		0
++#define SPI_CFG3_IPM_HALF_DUPLEX_DIR		BIT(2)
++#define SPI_CFG3_IPM_HALF_DUPLEX_EN		BIT(3)
++#define SPI_CFG3_IPM_XMODE_EN			BIT(4)
++#define SPI_CFG3_IPM_NODATA_FLAG		BIT(5)
++#define SPI_CFG3_IPM_CMD_BYTELEN_OFFSET		8
++#define SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET	12
++
++#define SPI_CFG3_IPM_CMD_PIN_MODE_MASK		GENMASK(1, 0)
++#define SPI_CFG3_IPM_CMD_BYTELEN_MASK		GENMASK(11, 8)
++#define SPI_CFG3_IPM_ADDR_BYTELEN_MASK		GENMASK(15, 12)
+ 
+ #define MT8173_SPI_MAX_PAD_SEL 3
+ 
+@@ -77,6 +100,9 @@
+ 
+ #define MTK_SPI_MAX_FIFO_SIZE 32U
+ #define MTK_SPI_PACKET_SIZE 1024
++#define MTK_SPI_IPM_PACKET_SIZE SZ_64K
++#define MTK_SPI_IPM_PACKET_LOOP SZ_256
++
+ #define MTK_SPI_32BITS_MASK  (0xffffffff)
+ 
+ #define DMA_ADDR_EXT_BITS (36)
+@@ -90,6 +116,9 @@ struct mtk_spi_compatible {
+ 	bool enhance_timing;
+ 	/* some IC support DMA addr extension */
+ 	bool dma_ext;
++	/* the IPM IP design improve some feature, and support dual/quad mode */
++	bool ipm_design;
++	bool support_quad;
+ };
+ 
+ struct mtk_spi {
+@@ -104,6 +133,12 @@ struct mtk_spi {
+ 	struct scatterlist *tx_sgl, *rx_sgl;
+ 	u32 tx_sgl_len, rx_sgl_len;
+ 	const struct mtk_spi_compatible *dev_comp;
++
++	struct completion spimem_done;
++	bool use_spimem;
++	struct device *dev;
++	dma_addr_t tx_dma;
++	dma_addr_t rx_dma;
+ };
+ 
+ static const struct mtk_spi_compatible mtk_common_compat;
+@@ -112,6 +147,14 @@ static const struct mtk_spi_compatible mt2712_compat = {
+ 	.must_tx = true,
+ };
+ 
++static const struct mtk_spi_compatible ipm_compat = {
++	.must_tx = true,
++	.enhance_timing = true,
++	.dma_ext = true,
++	.ipm_design = true,
++	.support_quad = true,
++};
++
+ static const struct mtk_spi_compatible mt6765_compat = {
+ 	.need_pad_sel = true,
+ 	.must_tx = true,
+@@ -140,11 +183,14 @@ static const struct mtk_spi_compatible mt8183_compat = {
+  * supplies it.
+  */
+ static const struct mtk_chip_config mtk_default_chip_info = {
+-	.cs_pol = 0,
+ 	.sample_sel = 0,
++	.get_tick_dly = 0,
+ };
+ 
+ static const struct of_device_id mtk_spi_of_match[] = {
++	{ .compatible = "mediatek,ipm-spi",
++		.data = (void *)&ipm_compat,
++	},
+ 	{ .compatible = "mediatek,mt2701-spi",
+ 		.data = (void *)&mtk_common_compat,
+ 	},
+@@ -190,19 +236,48 @@ static void mtk_spi_reset(struct mtk_spi *mdata)
+ 	writel(reg_val, mdata->base + SPI_CMD_REG);
+ }
+ 
+-static int mtk_spi_prepare_message(struct spi_master *master,
+-				   struct spi_message *msg)
++static int mtk_spi_hw_init(struct spi_master *master,
++			   struct spi_device *spi)
+ {
+ 	u16 cpha, cpol;
+ 	u32 reg_val;
+-	struct spi_device *spi = msg->spi;
+ 	struct mtk_chip_config *chip_config = spi->controller_data;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+ 	cpha = spi->mode & SPI_CPHA ? 1 : 0;
+ 	cpol = spi->mode & SPI_CPOL ? 1 : 0;
+ 
++	if (mdata->dev_comp->enhance_timing) {
++		if (mdata->dev_comp->ipm_design) {
++			/* CFG3 reg only used for spi-mem,
++			 * here write to default value
++			 */
++			writel(0x0, mdata->base + SPI_CFG3_IPM_REG);
++
++			reg_val = readl(mdata->base + SPI_CMD_REG);
++			reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK;
++			reg_val |= chip_config->get_tick_dly
++				   << SPI_CMD_IPM_GET_TICKDLY_OFFSET;
++			writel(reg_val, mdata->base + SPI_CMD_REG);
++		} else {
++			reg_val = readl(mdata->base + SPI_CFG1_REG);
++			reg_val &= ~SPI_CFG1_GET_TICKDLY_MASK;
++			reg_val |= chip_config->get_tick_dly
++				   << SPI_CFG1_GET_TICKDLY_OFFSET;
++			writel(reg_val, mdata->base + SPI_CFG1_REG);
++		}
++	}
++
+ 	reg_val = readl(mdata->base + SPI_CMD_REG);
++	if (mdata->dev_comp->ipm_design) {
++		/* SPI transfer without idle time until packet length done */
++		reg_val |= SPI_CMD_IPM_NONIDLE_MODE;
++		if (spi->mode & SPI_LOOP)
++			reg_val |= SPI_CMD_IPM_SPIM_LOOP;
++		else
++			reg_val &= ~SPI_CMD_IPM_SPIM_LOOP;
++	}
++
+ 	if (cpha)
+ 		reg_val |= SPI_CMD_CPHA;
+ 	else
+@@ -231,10 +306,12 @@ static int mtk_spi_prepare_message(struct spi_master *master,
+ #endif
+ 
+ 	if (mdata->dev_comp->enhance_timing) {
+-		if (chip_config->cs_pol)
++		/* set CS polarity */
++		if (spi->mode & SPI_CS_HIGH)
+ 			reg_val |= SPI_CMD_CS_POL;
+ 		else
+ 			reg_val &= ~SPI_CMD_CS_POL;
++
+ 		if (chip_config->sample_sel)
+ 			reg_val |= SPI_CMD_SAMPLE_SEL;
+ 		else
+@@ -260,11 +337,20 @@ static int mtk_spi_prepare_message(struct spi_master *master,
+ 	return 0;
+ }
+ 
++static int mtk_spi_prepare_message(struct spi_master *master,
++				   struct spi_message *msg)
++{
++	return mtk_spi_hw_init(master, msg->spi);
++}
++
+ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
+ {
+ 	u32 reg_val;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(spi->master);
+ 
++	if (spi->mode & SPI_CS_HIGH)
++		enable = !enable;
++
+ 	reg_val = readl(mdata->base + SPI_CMD_REG);
+ 	if (!enable) {
+ 		reg_val |= SPI_CMD_PAUSE_EN;
+@@ -278,14 +364,14 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable)
+ }
+ 
+ static void mtk_spi_prepare_transfer(struct spi_master *master,
+-				     struct spi_transfer *xfer)
++				     u32 speed_hz)
+ {
+ 	u32 spi_clk_hz, div, sck_time, cs_time, reg_val;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+ 	spi_clk_hz = clk_get_rate(mdata->spi_clk);
+-	if (xfer->speed_hz < spi_clk_hz / 2)
+-		div = DIV_ROUND_UP(spi_clk_hz, xfer->speed_hz);
++	if (speed_hz < spi_clk_hz / 2)
++		div = DIV_ROUND_UP(spi_clk_hz, speed_hz);
+ 	else
+ 		div = 1;
+ 
+@@ -323,12 +409,24 @@ static void mtk_spi_setup_packet(struct spi_master *master)
+ 	u32 packet_size, packet_loop, reg_val;
+ 	struct mtk_spi *mdata = spi_master_get_devdata(master);
+ 
+-	packet_size = min_t(u32, mdata->xfer_len, MTK_SPI_PACKET_SIZE);
++	if (mdata->dev_comp->ipm_design)
++		packet_size = min_t(u32,
++				    mdata->xfer_len,
++				    MTK_SPI_IPM_PACKET_SIZE);
++	else
++		packet_size = min_t(u32,
++				    mdata->xfer_len,
++				    MTK_SPI_PACKET_SIZE);
++
+ 	packet_loop = mdata->xfer_len / packet_size;
+ 
+ 	reg_val = readl(mdata->base + SPI_CFG1_REG);
+-	reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK);
++	if (mdata->dev_comp->ipm_design)
++		reg_val &= ~SPI_CFG1_IPM_PACKET_LENGTH_MASK;
++	else
++		reg_val &= ~SPI_CFG1_PACKET_LENGTH_MASK;
+ 	reg_val |= (packet_size - 1) << SPI_CFG1_PACKET_LENGTH_OFFSET;
++	reg_val &= ~SPI_CFG1_PACKET_LOOP_MASK;
+ 	reg_val |= (packet_loop - 1) << SPI_CFG1_PACKET_LOOP_OFFSET;
+ 	writel(reg_val, mdata->base + SPI_CFG1_REG);
+ }
+@@ -423,7 +521,7 @@ static int mtk_spi_fifo_transfer(struct spi_master *master,
+ 	mdata->cur_transfer = xfer;
+ 	mdata->xfer_len = min(MTK_SPI_MAX_FIFO_SIZE, xfer->len);
+ 	mdata->num_xfered = 0;
+-	mtk_spi_prepare_transfer(master, xfer);
++	mtk_spi_prepare_transfer(master, xfer->speed_hz);
+ 	mtk_spi_setup_packet(master);
+ 
+ 	cnt = xfer->len / 4;
+@@ -455,7 +553,7 @@ static int mtk_spi_dma_transfer(struct spi_master *master,
+ 	mdata->cur_transfer = xfer;
+ 	mdata->num_xfered = 0;
+ 
+-	mtk_spi_prepare_transfer(master, xfer);
++	mtk_spi_prepare_transfer(master, xfer->speed_hz);
+ 
+ 	cmd = readl(mdata->base + SPI_CMD_REG);
+ 	if (xfer->tx_buf)
+@@ -532,6 +630,13 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
+ 	else
+ 		mdata->state = MTK_SPI_IDLE;
+ 
++	/* SPI-MEM ops */
++	if (mdata->use_spimem) {
++		complete(&mdata->spimem_done);
++
++		return IRQ_HANDLED;
++	}
++
+ 	if (!master->can_dma(master, master->cur_msg->spi, trans)) {
+ 		if (trans->rx_buf) {
+ 			cnt = mdata->xfer_len / 4;
+@@ -615,12 +720,241 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
+ 	return IRQ_HANDLED;
+ }
+ 
++static bool mtk_spi_mem_supports_op(struct spi_mem *mem,
++				     const struct spi_mem_op *op)
++{
++	if (op->data.buswidth > 4 || op->addr.buswidth > 4 ||
++	    op->dummy.buswidth > 4 || op->cmd.buswidth > 4)
++		return false;
++
++	if (op->addr.nbytes && op->dummy.nbytes &&
++	    op->addr.buswidth != op->dummy.buswidth)
++		return false;
++
++	if (op->addr.nbytes + op->dummy.nbytes > 16)
++		return false;
++
++	if (op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE) {
++		if (op->data.nbytes / MTK_SPI_IPM_PACKET_SIZE >
++		    MTK_SPI_IPM_PACKET_LOOP ||
++		    op->data.nbytes % MTK_SPI_IPM_PACKET_SIZE != 0)
++			return false;
++	}
++
++	if (op->data.dir == SPI_MEM_DATA_IN &&
++	    !IS_ALIGNED((size_t)op->data.buf.in, 4))
++		return false;
++
++	return true;
++}
++
++static void mtk_spi_mem_setup_dma_xfer(struct spi_master *master,
++				   const struct spi_mem_op *op)
++{
++	struct mtk_spi *mdata = spi_master_get_devdata(master);
++
++	writel((u32)(mdata->tx_dma & MTK_SPI_32BITS_MASK),
++	       mdata->base + SPI_TX_SRC_REG);
++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
++	if (mdata->dev_comp->dma_ext)
++		writel((u32)(mdata->tx_dma >> 32),
++		       mdata->base + SPI_TX_SRC_REG_64);
++#endif
++
++	if (op->data.dir == SPI_MEM_DATA_IN) {
++		writel((u32)(mdata->rx_dma & MTK_SPI_32BITS_MASK),
++			   mdata->base + SPI_RX_DST_REG);
++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
++		if (mdata->dev_comp->dma_ext)
++			writel((u32)(mdata->rx_dma >> 32),
++				   mdata->base + SPI_RX_DST_REG_64);
++#endif
++	}
++}
++
++static int mtk_spi_transfer_wait(struct spi_mem *mem,
++				 const struct spi_mem_op *op)
++{
++	struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
++	unsigned long long ms = 1;
++
++	if (op->data.dir == SPI_MEM_NO_DATA)
++		ms = 8LL * 1000LL * 32;
++	else
++		ms = 8LL * 1000LL * op->data.nbytes;
++	do_div(ms, mem->spi->max_speed_hz);
++	ms += ms + 1000; /* 1s tolerance */
++
++	if (ms > UINT_MAX)
++		ms = UINT_MAX;
++
++	if (!wait_for_completion_timeout(&mdata->spimem_done,
++					 msecs_to_jiffies(ms))) {
++		dev_err(mdata->dev, "spi-mem transfer timeout\n");
++		return -ETIMEDOUT;
++	}
++
++	return 0;
++}
++
++static int mtk_spi_mem_exec_op(struct spi_mem *mem,
++				const struct spi_mem_op *op)
++{
++	struct mtk_spi *mdata = spi_master_get_devdata(mem->spi->master);
++	u32 reg_val, nio = 1, tx_size;
++	char *tx_tmp_buf;
++	int ret = 0;
++
++	mdata->use_spimem = true;
++	reinit_completion(&mdata->spimem_done);
++
++	mtk_spi_reset(mdata);
++	mtk_spi_hw_init(mem->spi->master, mem->spi);
++	mtk_spi_prepare_transfer(mem->spi->master, mem->spi->max_speed_hz);
++
++	reg_val = readl(mdata->base + SPI_CFG3_IPM_REG);
++	/* opcode byte len */
++	reg_val &= ~SPI_CFG3_IPM_CMD_BYTELEN_MASK;
++	reg_val |= 1 << SPI_CFG3_IPM_CMD_BYTELEN_OFFSET;
++
++	/* addr & dummy byte len */
++	reg_val &= ~SPI_CFG3_IPM_ADDR_BYTELEN_MASK;
++	if (op->addr.nbytes || op->dummy.nbytes)
++		reg_val |= (op->addr.nbytes + op->dummy.nbytes) <<
++			    SPI_CFG3_IPM_ADDR_BYTELEN_OFFSET;
++
++	/* data byte len */
++	if (op->data.dir == SPI_MEM_NO_DATA) {
++		reg_val |= SPI_CFG3_IPM_NODATA_FLAG;
++		writel(0, mdata->base + SPI_CFG1_REG);
++	} else {
++		reg_val &= ~SPI_CFG3_IPM_NODATA_FLAG;
++		mdata->xfer_len = op->data.nbytes;
++		mtk_spi_setup_packet(mem->spi->master);
++	}
++
++	if (op->addr.nbytes || op->dummy.nbytes) {
++		if (op->addr.buswidth == 1 || op->dummy.buswidth == 1)
++			reg_val |= SPI_CFG3_IPM_XMODE_EN;
++		else
++			reg_val &= ~SPI_CFG3_IPM_XMODE_EN;
++	}
++
++	if (op->addr.buswidth == 2 ||
++	    op->dummy.buswidth == 2 ||
++	    op->data.buswidth == 2)
++		nio = 2;
++	else if (op->addr.buswidth == 4 ||
++		 op->dummy.buswidth == 4 ||
++		 op->data.buswidth == 4)
++		nio = 4;
++
++	reg_val &= ~SPI_CFG3_IPM_CMD_PIN_MODE_MASK;
++	reg_val |= PIN_MODE_CFG(nio) << SPI_CFG3_IPM_PIN_MODE_OFFSET;
++
++	reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_EN;
++	if (op->data.dir == SPI_MEM_DATA_IN)
++		reg_val |= SPI_CFG3_IPM_HALF_DUPLEX_DIR;
++	else
++		reg_val &= ~SPI_CFG3_IPM_HALF_DUPLEX_DIR;
++	writel(reg_val, mdata->base + SPI_CFG3_IPM_REG);
++
++	tx_size = 1 + op->addr.nbytes + op->dummy.nbytes;
++	if (op->data.dir == SPI_MEM_DATA_OUT)
++		tx_size += op->data.nbytes;
++
++	tx_size = max(tx_size, (u32)32);
++
++	tx_tmp_buf = kzalloc(tx_size, GFP_KERNEL | GFP_DMA);
++	if (!tx_tmp_buf)
++		return -ENOMEM;
++
++	tx_tmp_buf[0] = op->cmd.opcode;
++
++	if (op->addr.nbytes) {
++		int i;
++
++		for (i = 0; i < op->addr.nbytes; i++)
++			tx_tmp_buf[i + 1] = op->addr.val >>
++					(8 * (op->addr.nbytes - i - 1));
++	}
++
++	if (op->dummy.nbytes)
++		memset(tx_tmp_buf + op->addr.nbytes + 1,
++		       0xff,
++		       op->dummy.nbytes);
++
++	if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT)
++		memcpy(tx_tmp_buf + op->dummy.nbytes + op->addr.nbytes + 1,
++		       op->data.buf.out,
++		       op->data.nbytes);
++
++	mdata->tx_dma = dma_map_single(mdata->dev, tx_tmp_buf,
++				       tx_size, DMA_TO_DEVICE);
++	if (dma_mapping_error(mdata->dev, mdata->tx_dma)) {
++		ret = -ENOMEM;
++		goto err_exit;
++	}
++
++	if (op->data.dir == SPI_MEM_DATA_IN) {
++		mdata->rx_dma = dma_map_single(mdata->dev,
++					       op->data.buf.in,
++					       op->data.nbytes,
++					       DMA_FROM_DEVICE);
++		if (dma_mapping_error(mdata->dev, mdata->rx_dma)) {
++			ret = -ENOMEM;
++			goto unmap_tx_dma;
++		}
++	}
++
++	reg_val = readl(mdata->base + SPI_CMD_REG);
++	reg_val |= SPI_CMD_TX_DMA;
++	if (op->data.dir == SPI_MEM_DATA_IN)
++		reg_val |= SPI_CMD_RX_DMA;
++	writel(reg_val, mdata->base + SPI_CMD_REG);
++
++	mtk_spi_mem_setup_dma_xfer(mem->spi->master, op);
++
++	mtk_spi_enable_transfer(mem->spi->master);
++
++	/* Wait for the interrupt. */
++	ret = mtk_spi_transfer_wait(mem, op);
++	if (ret)
++		goto unmap_rx_dma;
++
++	/* spi disable dma */
++	reg_val = readl(mdata->base + SPI_CMD_REG);
++	reg_val &= ~SPI_CMD_TX_DMA;
++	if (op->data.dir == SPI_MEM_DATA_IN)
++		reg_val &= ~SPI_CMD_RX_DMA;
++	writel(reg_val, mdata->base + SPI_CMD_REG);
++
++	if (op->data.dir == SPI_MEM_DATA_IN)
++		dma_unmap_single(mdata->dev, mdata->rx_dma,
++				 op->data.nbytes, DMA_FROM_DEVICE);
++unmap_rx_dma:
++	dma_unmap_single(mdata->dev, mdata->rx_dma,
++			 op->data.nbytes, DMA_FROM_DEVICE);
++unmap_tx_dma:
++	dma_unmap_single(mdata->dev, mdata->tx_dma,
++			 tx_size, DMA_TO_DEVICE);
++err_exit:
++	kfree(tx_tmp_buf);
++	mdata->use_spimem = false;
++
++	return ret;
++}
++
++static const struct spi_controller_mem_ops mtk_spi_mem_ops = {
++	.supports_op = mtk_spi_mem_supports_op,
++	.exec_op = mtk_spi_mem_exec_op,
++};
++
+ static int mtk_spi_probe(struct platform_device *pdev)
+ {
+ 	struct spi_master *master;
+ 	struct mtk_spi *mdata;
+ 	const struct of_device_id *of_id;
+-	struct resource *res;
+ 	int i, irq, ret, addr_bits;
+ 
+ 	master = spi_alloc_master(&pdev->dev, sizeof(*mdata));
+@@ -629,7 +963,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 		return -ENOMEM;
+ 	}
+ 
+-	master->auto_runtime_pm = true;
++//	master->auto_runtime_pm = true;
+ 	master->dev.of_node = pdev->dev.of_node;
+ 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+ 
+@@ -648,9 +982,25 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 
+ 	mdata = spi_master_get_devdata(master);
+ 	mdata->dev_comp = of_id->data;
++
++	if (mdata->dev_comp->enhance_timing)
++		master->mode_bits |= SPI_CS_HIGH;
++
+ 	if (mdata->dev_comp->must_tx)
+ 		master->flags = SPI_MASTER_MUST_TX;
+ 
++	if (mdata->dev_comp->ipm_design)
++		master->mode_bits |= SPI_LOOP;
++
++	if (mdata->dev_comp->support_quad) {
++		master->mem_ops = &mtk_spi_mem_ops;
++		master->mode_bits |= SPI_RX_DUAL | SPI_TX_DUAL |
++				     SPI_RX_QUAD | SPI_TX_QUAD;
++
++		mdata->dev = &pdev->dev;
++		init_completion(&mdata->spimem_done);
++	}
++
+ 	if (mdata->dev_comp->need_pad_sel) {
+ 		mdata->pad_num = of_property_count_u32_elems(
+ 			pdev->dev.of_node,
+@@ -683,15 +1033,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 	}
+ 
+ 	platform_set_drvdata(pdev, master);
+-
+-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+-	if (!res) {
+-		ret = -ENODEV;
+-		dev_err(&pdev->dev, "failed to determine base address\n");
+-		goto err_put_master;
+-	}
+-
+-	mdata->base = devm_ioremap_resource(&pdev->dev, res);
++	mdata->base = devm_platform_ioremap_resource(pdev, 0);
+ 	if (IS_ERR(mdata->base)) {
+ 		ret = PTR_ERR(mdata->base);
+ 		goto err_put_master;
+@@ -713,6 +1055,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 		goto err_put_master;
+ 	}
+ 
++/*
+ 	mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk");
+ 	if (IS_ERR(mdata->parent_clk)) {
+ 		ret = PTR_ERR(mdata->parent_clk);
+@@ -750,7 +1093,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
+ 	clk_disable_unprepare(mdata->spi_clk);
+ 
+ 	pm_runtime_enable(&pdev->dev);
+-
++*/
+ 	ret = devm_spi_register_master(&pdev->dev, master);
+ 	if (ret) {
+ 		dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
+diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h
+index f0e6d6483..fae9bc15c 100644
+--- a/include/linux/platform_data/spi-mt65xx.h
++++ b/include/linux/platform_data/spi-mt65xx.h
+@@ -11,7 +11,7 @@
+ 
+ /* Board specific platform_data */
+ struct mtk_chip_config {
+-	u32 cs_pol;
+ 	u32 sample_sel;
++	u32 get_tick_dly;
+ };
+ #endif
+-- 
+2.17.1
+
diff --git a/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch b/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch
new file mode 100644
index 0000000..86b2089
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0666-spi-mtk-nor-fix-timeout-calculation-overflow.patch
@@ -0,0 +1,179 @@
+From patchwork Tue Sep 22 11:49:02 2020
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+X-Patchwork-Submitter: Chuanhong Guo <gch981213@gmail.com>
+X-Patchwork-Id: 11792387
+Return-Path: 
+ <SRS0=i66O=C7=lists.infradead.org=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@kernel.org>
+Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org
+ [172.30.200.123])
+	by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 21EB0618
+	for <patchwork-linux-arm@patchwork.kernel.org>;
+ Tue, 22 Sep 2020 11:51:33 +0000 (UTC)
+Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134])
+	(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
+	(No client certificate requested)
+	by mail.kernel.org (Postfix) with ESMTPS id E15FF221EB
+	for <patchwork-linux-arm@patchwork.kernel.org>;
+ Tue, 22 Sep 2020 11:51:32 +0000 (UTC)
+Authentication-Results: mail.kernel.org;
+	dkim=pass (2048-bit key) header.d=lists.infradead.org
+ header.i=@lists.infradead.org header.b="KBg/skkC";
+	dkim=fail reason="signature verification failed" (2048-bit key)
+ header.d=gmail.com header.i=@gmail.com header.b="Gtqp4rrT"
+DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org E15FF221EB
+Authentication-Results: mail.kernel.org;
+ dmarc=fail (p=none dis=none) header.from=gmail.com
+Authentication-Results: mail.kernel.org;
+ spf=none
+ smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org
+DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
+	d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding:
+	Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive:
+	List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:To:From:
+	Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender
+	:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner;
+	bh=Xg61WV47qNPjINdHDPnF6T3q8GN8f9evwhTMdYR0Zqs=; b=KBg/skkCvnF7/8AlleTay0p/H2
+	hC4Lzo+slWhX5/eepUEXzhTr5ORf4Dx9gD65UEuordKQKFpg6Y9ApoGaYtmBJ0vABdAZt+oVG4sFf
+	K3z3CYV6EZ5qvwsZt53Xm3YsHojgu+Lnc/MGgGWBRjCtTP7gshm480pZ0w6ADgHvrym5hNajUF6+5
+	zMm5Wwq34jxUApGU7k5FAPsvO5ctYCuhECq/mLB6tplCVh3/+XLdSiHMUlY17fh+xs732kgaDotuQ
+	QYgXtDmMB1pVKCq5cf3Bcuz7Ww47vLSx4rBxtdB/vpp2w9SdrU6K8Q7DuJ3+XrGfbMhKtBU5ektA8
+	GxEUUaKw==;
+Received: from localhost ([::1] helo=merlin.infradead.org)
+	by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux))
+	id 1kKgo2-0000Ze-Fb; Tue, 22 Sep 2020 11:50:00 +0000
+Received: from mail-pg1-x543.google.com ([2607:f8b0:4864:20::543])
+ by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux))
+ id 1kKgnr-0000Vv-6z; Tue, 22 Sep 2020 11:49:49 +0000
+Received: by mail-pg1-x543.google.com with SMTP id o25so6798387pgm.0;
+ Tue, 22 Sep 2020 04:49:46 -0700 (PDT)
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;
+ h=from:to:cc:subject:date:message-id:mime-version
+ :content-transfer-encoding;
+ bh=EJwpKrbgqo/Jc/SWHvyAGB9CrpkZ5L1Hzq9tInFHTYk=;
+ b=Gtqp4rrTgM1+bYxfUQXe+lfPcgHRW6GccdN42Iszl6ozMbezvftl1BUcKE22S6eFW3
+ Vs+lcKZN9Eh9C53YAAd0cuZYhJ2GqlfGNLA/9SyB7s/gIwHqO9Cuu17YpB9dAFfEUxoS
+ 825uUcTeRe6BTagZAh2/MBluiMY3TszRi94MbOftxUg+wSqp0wMAPe9RN0gAEc/l2xgK
+ 8PhXbZv3uItI4QqoKYiz93vrF/zYhj+oGTI44g2li2fpAgCNL7lXCpSE2C9NsEe+YqTw
+ aO5A3W8t4jvp8oCJEvr/MWY1ZZLd1fVJ17W3aGXoDi/7EUcAvX9G5Ee7U68UXGMtty/d
+ z5Nw==
+X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
+ d=1e100.net; s=20161025;
+ h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version
+ :content-transfer-encoding;
+ bh=EJwpKrbgqo/Jc/SWHvyAGB9CrpkZ5L1Hzq9tInFHTYk=;
+ b=XhcpP16zYyJr/qCT9JbO3fn8RyfI44xJL3hvgNrlcr4ljkEZ4TF6OfyhjdEZYeeA3C
+ kLlWuAqrSn6mweuhS2LZ0BV5QL/YYaVO4wP4B/y3j+tNbnW3JNM0NtEY19pOtaM4vYK/
+ tPuNxld5RvJWxQ9BLs8hH6y7j/ob6oDug170P5YkwK6Wa/FLCi2bw92/vldhdnFP/Nny
+ 1bbiWRVls1Ra/Q3z90tGViMkBdlcff6MI9DR5M6a1HTQN7kN9rLDCMGs3r9XVComY07N
+ ECbrZbL+iJwuRuT43RAUxE72X/Pn0WYD20unzITf8bta92usNDRgEuxc1bLyL+uHxgUk
+ YQKA==
+X-Gm-Message-State: AOAM531Xr1Bg4uwupCAPpH4eBWVrXGALjIWa+5AVNZ8w6ltS4BGgWv6b
+ e4g6ycKnUp/KalpJhOMi90o=
+X-Google-Smtp-Source: 
+ ABdhPJx36OliaaLkiX3ZeZNNWgd/qSKiRor2X0eeHScDrjMSi5bTiEzAfX5j7hkQgqz8ZUT0qqLRNA==
+X-Received: by 2002:a63:1863:: with SMTP id 35mr3131307pgy.413.1600775385014;
+ Tue, 22 Sep 2020 04:49:45 -0700 (PDT)
+Received: from guoguo-omen.lan ([156.96.148.94])
+ by smtp.gmail.com with ESMTPSA id r4sm2223750pjf.4.2020.09.22.04.49.42
+ (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
+ Tue, 22 Sep 2020 04:49:44 -0700 (PDT)
+From: Chuanhong Guo <gch981213@gmail.com>
+To: linux-spi@vger.kernel.org
+Subject: [PATCH v2] spi: spi-mtk-nor: fix timeout calculation overflow
+Date: Tue, 22 Sep 2020 19:49:02 +0800
+Message-Id: <20200922114905.2942859-1-gch981213@gmail.com>
+X-Mailer: git-send-email 2.26.2
+MIME-Version: 1.0
+X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 
+X-CRM114-CacheID: sfid-20200922_074948_345420_69207EBE 
+X-CRM114-Status: GOOD (  12.60  )
+X-Spam-Score: 2.6 (++)
+X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary:
+ Content analysis details:   (2.6 points)
+ pts rule name              description
+ ---- ----------------------
+ --------------------------------------------------
+ 2.6 RCVD_IN_SBL            RBL: Received via a relay in Spamhaus SBL
+ [156.96.148.94 listed in zen.spamhaus.org]
+ -0.0 RCVD_IN_DNSWL_NONE     RBL: Sender listed at https://www.dnswl.org/,
+ no trust [2607:f8b0:4864:20:0:0:0:543 listed in]
+ [list.dnswl.org]
+ 0.0 FREEMAIL_FROM          Sender email is commonly abused enduser mail
+ provider [gch981213[at]gmail.com]
+ 0.2 FREEMAIL_ENVFROM_END_DIGIT Envelope-from freemail username ends
+ in digit [gch981213[at]gmail.com]
+ -0.0 SPF_PASS               SPF: sender matches SPF record
+ 0.0 SPF_HELO_NONE          SPF: HELO does not publish an SPF Record
+ -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature
+ -0.1 DKIM_VALID_AU          Message has a valid DKIM or DK signature from
+ author's domain
+ -0.1 DKIM_VALID_EF          Message has a valid DKIM or DK signature from
+ envelope-from domain
+ 0.1 DKIM_SIGNED            Message has a DKIM or DK signature,
+ not necessarily
+ valid
+X-BeenThere: linux-arm-kernel@lists.infradead.org
+X-Mailman-Version: 2.1.29
+Precedence: list
+List-Id: <linux-arm-kernel.lists.infradead.org>
+List-Unsubscribe: 
+ <http://lists.infradead.org/mailman/options/linux-arm-kernel>,
+ <mailto:linux-arm-kernel-request@lists.infradead.org?subject=unsubscribe>
+List-Archive: <http://lists.infradead.org/pipermail/linux-arm-kernel/>
+List-Post: <mailto:linux-arm-kernel@lists.infradead.org>
+List-Help: <mailto:linux-arm-kernel-request@lists.infradead.org?subject=help>
+List-Subscribe: 
+ <http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>,
+ <mailto:linux-arm-kernel-request@lists.infradead.org?subject=subscribe>
+Cc: linux-kernel@vger.kernel.org, stable@vger.kernel.org,
+ Mark Brown <broonie@kernel.org>, linux-mediatek@lists.infradead.org,
+ bayi.cheng@mediatek.com, Matthias Brugger <matthias.bgg@gmail.com>,
+ Chuanhong Guo <gch981213@gmail.com>, linux-arm-kernel@lists.infradead.org
+Sender: "linux-arm-kernel" <linux-arm-kernel-bounces@lists.infradead.org>
+Errors-To: 
+ linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org
+
+CLK_TO_US macro is used to calculate potential transfer time for various
+timeout handling. However it overflows on transfer bigger than 512 bytes
+because it first did (len * 8 * 1000000).
+This controller typically operates at 45MHz. This patch did 2 things:
+1. calculate clock / 1000000 first
+2. add a 4M transfer size cap so that the final timeout in DMA reading
+   doesn't overflow
+
+Fixes: 881d1ee9fe81f ("spi: add support for mediatek spi-nor controller")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
+---
+
+Change since v1: fix transfer size cap to 4M
+
+ drivers/spi/spi-mtk-nor.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
+index 6e6ca2b8e6c82..62f5ff2779884 100644
+--- a/drivers/spi/spi-mtk-nor.c
++++ b/drivers/spi/spi-mtk-nor.c
+@@ -89,7 +89,7 @@
+ // Buffered page program can do one 128-byte transfer
+ #define MTK_NOR_PP_SIZE			128
+ 
+-#define CLK_TO_US(sp, clkcnt)		((clkcnt) * 1000000 / sp->spi_freq)
++#define CLK_TO_US(sp, clkcnt)		DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000)
+ 
+ struct mtk_nor {
+ 	struct spi_controller *ctlr;
+@@ -177,6 +177,10 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+ 	if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) {
+ 		if ((op->data.dir == SPI_MEM_DATA_IN) &&
+ 		    mtk_nor_match_read(op)) {
++			// limit size to prevent timeout calculation overflow
++			if (op->data.nbytes > 0x400000)
++				op->data.nbytes = 0x400000;
++
+ 			if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) ||
+ 			    (op->data.nbytes < MTK_NOR_DMA_ALIGN))
+ 				op->data.nbytes = 1;
diff --git a/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch b/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch
new file mode 100644
index 0000000..a04f5d6
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0667-spi-mediatek-fix-timeout-for-large-data.patch
@@ -0,0 +1,34 @@
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -720,6 +720,23 @@ static irqreturn_t mtk_spi_interrupt(int
+ 	return IRQ_HANDLED;
+ }
+ 
++static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem,
++                                      struct spi_mem_op *op)
++{
++	int opcode_len;
++
++	if(!op->data.nbytes)
++		return 0;
++
++	if (op->data.dir != SPI_MEM_NO_DATA) {
++		opcode_len = 1 + op->addr.nbytes + op->dummy.nbytes;
++		if (opcode_len + op->data.nbytes > MTK_SPI_IPM_PACKET_SIZE)
++			op->data.nbytes = MTK_SPI_IPM_PACKET_SIZE -opcode_len;
++	}
++
++	return 0;
++}
++
+ static bool mtk_spi_mem_supports_op(struct spi_mem *mem,
+ 				     const struct spi_mem_op *op)
+ {
+@@ -946,6 +963,7 @@ err_exit:
+ }
+ 
+ static const struct spi_controller_mem_ops mtk_spi_mem_ops = {
++	.adjust_op_size = mtk_spi_mem_adjust_op_size,
+ 	.supports_op = mtk_spi_mem_supports_op,
+ 	.exec_op = mtk_spi_mem_exec_op,
+ };
diff --git a/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch b/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch
new file mode 100644
index 0000000..31562bf
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0668-spi-mediatek-fix-dma-unmap-twice.patch
@@ -0,0 +1,16 @@
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -946,12 +946,10 @@ static int mtk_spi_mem_exec_op(struct sp
+ 		reg_val &= ~SPI_CMD_RX_DMA;
+ 	writel(reg_val, mdata->base + SPI_CMD_REG);
+ 
++unmap_rx_dma:
+ 	if (op->data.dir == SPI_MEM_DATA_IN)
+ 		dma_unmap_single(mdata->dev, mdata->rx_dma,
+ 				 op->data.nbytes, DMA_FROM_DEVICE);
+-unmap_rx_dma:
+-	dma_unmap_single(mdata->dev, mdata->rx_dma,
+-			 op->data.nbytes, DMA_FROM_DEVICE);
+ unmap_tx_dma:
+ 	dma_unmap_single(mdata->dev, mdata->tx_dma,
+ 			 tx_size, DMA_TO_DEVICE);
diff --git a/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch b/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch
new file mode 100644
index 0000000..582771b
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0669-fix-SPIM-NAND-and-NOR-probing.patch
@@ -0,0 +1,33 @@
+--- a/drivers/spi/spi-mt65xx.c
++++ b/drivers/spi/spi-mt65xx.c
+@@ -1073,7 +1073,7 @@ static int mtk_spi_probe(struct platform
+ 		goto err_put_master;
+ 	}
+ 
+-/*
++
+ 	mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk");
+ 	if (IS_ERR(mdata->parent_clk)) {
+ 		ret = PTR_ERR(mdata->parent_clk);
+@@ -1101,17 +1101,17 @@ static int mtk_spi_probe(struct platform
+ 		goto err_put_master;
+ 	}
+ 
+-	ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
++	/*ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk);
+ 	if (ret < 0) {
+ 		dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret);
+ 		clk_disable_unprepare(mdata->spi_clk);
+ 		goto err_put_master;
+ 	}
+ 
+-	clk_disable_unprepare(mdata->spi_clk);
++	clk_disable_unprepare(mdata->sel_clk);*/
++
++	//pm_runtime_enable(&pdev->dev);
+ 
+-	pm_runtime_enable(&pdev->dev);
+-*/
+ 	ret = devm_spi_register_master(&pdev->dev, master);
+ 	if (ret) {
+ 		dev_err(&pdev->dev, "failed to register master (%d)\n", ret);
diff --git a/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch b/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch
new file mode 100644
index 0000000..3023076
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/0701-fix-mtk-nfi-driver-dependency.patch
@@ -0,0 +1,10 @@
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -429,6 +429,7 @@ config SPI_MT65XX
+ 
+ config SPI_MTK_SNFI
+ 	tristate "MediaTek SPI NAND interface"
++	depends on MTD
+ 	select MTD_SPI_NAND
+ 	help
+ 	  This selects the SPI NAND FLASH interface(SNFI),
diff --git a/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch b/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch
new file mode 100644
index 0000000..150087a
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/1002-mtkhnat-add-support-for-virtual-interface-acceleration.patch
@@ -0,0 +1,127 @@
+diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
+index 3d73c0c..960ade1 100644
+--- a/include/net/netfilter/nf_flow_table.h
++++ b/include/net/netfilter/nf_flow_table.h
+@@ -92,9 +92,12 @@ struct flow_offload {
+ #define FLOW_OFFLOAD_PATH_VLAN		BIT(1)
+ #define FLOW_OFFLOAD_PATH_PPPOE		BIT(2)
+ #define FLOW_OFFLOAD_PATH_DSA		BIT(3)
++#define FLOW_OFFLOAD_PATH_DSLITE	BIT(4)
++#define FLOW_OFFLOAD_PATH_6RD		BIT(5)
+ 
+ struct flow_offload_hw_path {
+ 	struct net_device *dev;
++	struct net_device *virt_dev;
+ 	u32 flags;
+ 
+ 	u8 eth_src[ETH_ALEN];
+diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
+index be6801524..c51af70f6 100644
+--- a/net/8021q/vlan_dev.c
++++ b/net/8021q/vlan_dev.c
+@@ -761,6 +761,7 @@ static int vlan_dev_flow_offload_check(struct flow_offload_hw_path *path)
+ 	path->flags |= FLOW_OFFLOAD_PATH_VLAN;
+ 	path->vlan_proto = vlan->vlan_proto;
+ 	path->vlan_id = vlan->vlan_id;
++	path->virt_dev = dev;
+ 	path->dev = vlan->real_dev;
+ 
+ 	if (vlan->real_dev->netdev_ops->ndo_flow_offload_check)
+diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
+index 1b7e3141c..da4e34f74 100644
+--- a/net/ipv6/ip6_tunnel.c
++++ b/net/ipv6/ip6_tunnel.c
+@@ -57,6 +57,11 @@
+ #include <net/netns/generic.h>
+ #include <net/dst_metadata.h>
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
++
+ MODULE_AUTHOR("Ville Nuorvala");
+ MODULE_DESCRIPTION("IPv6 tunneling device");
+ MODULE_LICENSE("GPL");
+@@ -1880,6 +1885,22 @@ int ip6_tnl_get_iflink(const struct net_device *dev)
+ }
+ EXPORT_SYMBOL(ip6_tnl_get_iflink);
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct net_device *dev = path->dev;
++	struct ip6_tnl *tnl = netdev_priv(dev);
++
++	if (path->flags & FLOW_OFFLOAD_PATH_DSLITE)
++		return -EEXIST;
++
++	path->flags |= FLOW_OFFLOAD_PATH_DSLITE;
++	path->dev = tnl->dev;
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ int ip6_tnl_encap_add_ops(const struct ip6_tnl_encap_ops *ops,
+ 			  unsigned int num)
+ {
+@@ -1941,6 +1962,9 @@ static const struct net_device_ops ip6_tnl_netdev_ops = {
+ 	.ndo_change_mtu = ip6_tnl_change_mtu,
+ 	.ndo_get_stats	= ip6_get_stats,
+ 	.ndo_get_iflink = ip6_tnl_get_iflink,
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++	.ndo_flow_offload_check = ipip6_dev_flow_offload_check,
++#endif
+ };
+ 
+ #define IPXIPX_FEATURES (NETIF_F_SG |		\
+diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
+index 98954830c..42b6e8c4c 100644
+--- a/net/ipv6/sit.c
++++ b/net/ipv6/sit.c
+@@ -52,6 +52,11 @@
+ #include <net/net_namespace.h>
+ #include <net/netns/generic.h>
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++#include <linux/netfilter.h>
++#include <net/netfilter/nf_flow_table.h>
++#endif
++
+ /*
+    This version of net/ipv6/sit.c is cloned of net/ipv4/ip_gre.c
+ 
+@@ -1345,6 +1350,22 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ 	return err;
+ }
+ 
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++static int ipip6_dev_flow_offload_check(struct flow_offload_hw_path *path)
++{
++	struct net_device *dev = path->dev;
++	struct ip_tunnel *tnl = netdev_priv(dev);
++
++	if (path->flags & FLOW_OFFLOAD_PATH_6RD)
++		return -EEXIST;
++
++	path->flags |= FLOW_OFFLOAD_PATH_6RD;
++	path->dev = tnl->dev;
++
++	return 0;
++}
++#endif /* CONFIG_NF_FLOW_TABLE */
++
+ static const struct net_device_ops ipip6_netdev_ops = {
+ 	.ndo_init	= ipip6_tunnel_init,
+ 	.ndo_uninit	= ipip6_tunnel_uninit,
+@@ -1352,6 +1373,9 @@ static const struct net_device_ops ipip6_netdev_ops = {
+ 	.ndo_do_ioctl	= ipip6_tunnel_ioctl,
+ 	.ndo_get_stats64 = ip_tunnel_get_stats64,
+ 	.ndo_get_iflink = ip_tunnel_get_iflink,
++#if IS_ENABLED(CONFIG_NF_FLOW_TABLE)
++	.ndo_flow_offload_check = ipip6_dev_flow_offload_check,
++#endif
+ };
+ 
+ static void ipip6_dev_free(struct net_device *dev)
diff --git a/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch b/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch
new file mode 100644
index 0000000..4b99d9d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/1015-pcie-add-pcie-gen3-upstream-driver.patch
@@ -0,0 +1,36 @@
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index 70e0782..67988f8 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -241,6 +241,19 @@ config PCIE_MEDIATEK
+ 	  Say Y here if you want to enable PCIe controller support on
+ 	  MediaTek SoCs.
+ 
++config PCIE_MEDIATEK_GEN3
++	tristate "MediaTek Gen3 PCIe controller"
++	depends on ARCH_MEDIATEK || COMPILE_TEST
++	depends on PCI_MSI_IRQ_DOMAIN
++	help
++	  Adds support for PCIe Gen3 MAC controller for MediaTek SoCs.
++	  This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed,
++	  and support up to 256 MSI interrupt numbers for
++	  multi-function devices.
++
++	  Say Y here if you want to enable Gen3 PCIe controller support on
++	  MediaTek SoCs.
++
+ config PCIE_MOBIVEIL
+ 	bool "Mobiveil AXI PCIe controller"
+ 	depends on ARCH_ZYNQMP || COMPILE_TEST
+diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
+index a2a22c9..54a496a 100644
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -27,6 +27,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
+ obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
+ obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
+ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
++obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie-mediatek-gen3.o
+ obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
+ obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
+ obj-$(CONFIG_VMD) += vmd.o
diff --git a/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch b/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch
new file mode 100644
index 0000000..e0ee954
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/1023-kgdb-add-interrupt-control.patch
@@ -0,0 +1,42 @@
+diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c
+index 1a157ca..258fe4b 100644
+--- a/arch/arm64/kernel/kgdb.c
++++ b/arch/arm64/kernel/kgdb.c
+@@ -18,6 +18,10 @@
+ #include <asm/debug-monitors.h>
+ #include <asm/insn.h>
+ #include <asm/traps.h>
++#include <asm/ptrace.h>
++
++
++static DEFINE_PER_CPU(unsigned int, kgdb_pstate);
+ 
+ struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = {
+ 	{ "x0", 8, offsetof(struct pt_regs, regs[0])},
+@@ -206,6 +210,8 @@ int kgdb_arch_handle_exception(int exception_vector, int signo,
+ 		err = 0;
+ 		break;
+ 	case 's':
++		__this_cpu_write(kgdb_pstate, linux_regs->pstate);
++		linux_regs->pstate |= PSR_I_BIT;
+ 		/*
+ 		 * Update step address value with address passed
+ 		 * with step packet.
+@@ -249,9 +255,17 @@ NOKPROBE_SYMBOL(kgdb_compiled_brk_fn);
+ 
+ static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr)
+ {
++	unsigned int pstate;
++
+ 	if (!kgdb_single_step)
+ 		return DBG_HOOK_ERROR;
++	kernel_disable_single_step();
+ 
++	pstate = __this_cpu_read(kgdb_pstate);
++	if (pstate & PSR_I_BIT)
++		regs->pstate |= PSR_I_BIT;
++	else
++		regs->pstate &= ~PSR_I_BIT;
+ 	kgdb_handle_exception(0, SIGTRAP, 0, regs);
+ 	return DBG_HOOK_HANDLED;
+ }
diff --git a/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch b/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch
new file mode 100644
index 0000000..5cf486c
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/1024-pcie-add-multi-MSI-support.patch
@@ -0,0 +1,64 @@
+diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
+index 2a54fa7a3..132b3204c 100644
+--- a/drivers/pci/controller/pcie-mediatek.c
++++ b/drivers/pci/controller/pcie-mediatek.c
+@@ -446,24 +446,24 @@ static int mtk_pcie_irq_domain_alloc(struct irq_domain *domain, unsigned int vir
+ 				     unsigned int nr_irqs, void *args)
+ {
+ 	struct mtk_pcie_port *port = domain->host_data;
+-	unsigned long bit;
++	int bit, i;
+ 
+-	WARN_ON(nr_irqs != 1);
+ 	mutex_lock(&port->lock);
+ 
+-	bit = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM);
+-	if (bit >= MTK_MSI_IRQS_NUM) {
++	bit = bitmap_find_free_region(port->msi_irq_in_use, MTK_MSI_IRQS_NUM,
++							order_base_2(nr_irqs));
++	if (bit < 0) {
+ 		mutex_unlock(&port->lock);
+ 		return -ENOSPC;
+ 	}
+ 
+-	__set_bit(bit, port->msi_irq_in_use);
+-
+ 	mutex_unlock(&port->lock);
+ 
+-	irq_domain_set_info(domain, virq, bit, &mtk_msi_bottom_irq_chip,
+-			    domain->host_data, handle_edge_irq,
+-			    NULL, NULL);
++	for (i = 0; i < nr_irqs; i++) {
++		irq_domain_set_info(domain, virq + i, bit + i,
++				    &mtk_msi_bottom_irq_chip, domain->host_data,
++				    handle_edge_irq, NULL, NULL);
++	}
+ 
+ 	return 0;
+ }
+@@ -501,7 +501,7 @@ static struct irq_chip mtk_msi_irq_chip = {
+ 
+ static struct msi_domain_info mtk_msi_domain_info = {
+ 	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+-		   MSI_FLAG_PCI_MSIX),
++		   MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+ 	.chip	= &mtk_msi_irq_chip,
+ };
+ 
+@@ -633,14 +633,14 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc)
+ 		if (status & MSI_STATUS){
+ 			unsigned long imsi_status;
+ 
++			/* Clear MSI interrupt status */
++			writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
+ 			while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
+ 				for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
+ 					virq = irq_find_mapping(port->inner_domain, bit);
+ 					generic_handle_irq(virq);
+ 				}
+ 			}
+-			/* Clear MSI interrupt status */
+-			writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
+ 		}
+ 	}
+ 
diff --git a/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch b/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch
new file mode 100644
index 0000000..185c55d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/400-mtd-add-mtk-snand-driver.patch
@@ -0,0 +1,21 @@
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -228,6 +228,8 @@ source "drivers/mtd/ubi/Kconfig"
+ 
+ source "drivers/mtd/hyperbus/Kconfig"
+ 
++source "drivers/mtd/mtk-snand/Kconfig"
++
+ source "drivers/mtd/composite/Kconfig"
+ 
+ endif # MTD
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -33,5 +33,7 @@ obj-$(CONFIG_MTD_SPI_NOR)	+= spi-nor/
+ obj-$(CONFIG_MTD_UBI)		+= ubi/
+ obj-$(CONFIG_MTD_HYPERBUS)	+= hyperbus/
+ 
++obj-$(CONFIG_MTK_SPI_NAND)	+= mtk-snand/
++
+ # Composite drivers must be loaded last
+ obj-y				+= composite/
diff --git a/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch b/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch
new file mode 100644
index 0000000..a02873d
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/401-pinctrl-add-mt7986-driver.patch
@@ -0,0 +1,30 @@
+diff --git a/drivers/pinctrl/mediatek/Kconfig b/drivers/pinctrl/mediatek/Kconfig
+index 701f9af..9109f91 100644
+--- a/drivers/pinctrl/mediatek/Kconfig
++++ b/drivers/pinctrl/mediatek/Kconfig
+@@ -100,6 +100,13 @@ config PINCTRL_MT7622
+ 	default ARM64 && ARCH_MEDIATEK
+ 	select PINCTRL_MTK_MOORE
+ 
++config PINCTRL_MT7986
++	bool "Mediatek MT7986 pin control"
++	depends on OF
++	depends on ARM64 || COMPILE_TEST
++	default ARM64 && ARCH_MEDIATEK
++	select PINCTRL_MTK_MOORE
++
+ config PINCTRL_MT8173
+ 	bool "Mediatek MT8173 pin control"
+ 	depends on OF
+diff --git a/drivers/pinctrl/mediatek/Makefile b/drivers/pinctrl/mediatek/Makefile
+index a74325a..d408585 100644
+--- a/drivers/pinctrl/mediatek/Makefile
++++ b/drivers/pinctrl/mediatek/Makefile
+@@ -15,6 +15,7 @@ obj-$(CONFIG_PINCTRL_MT6797)	+= pinctrl-mt6797.o
+ obj-$(CONFIG_PINCTRL_MT7622)	+= pinctrl-mt7622.o
+ obj-$(CONFIG_PINCTRL_MT7623)	+= pinctrl-mt7623.o
+ obj-$(CONFIG_PINCTRL_MT7629)	+= pinctrl-mt7629.o
++obj-$(CONFIG_PINCTRL_MT7986)	+= pinctrl-mt7986.o
+ obj-$(CONFIG_PINCTRL_MT8173)	+= pinctrl-mt8173.o
+ obj-$(CONFIG_PINCTRL_MT8183)	+= pinctrl-mt8183.o
+ obj-$(CONFIG_PINCTRL_MT8516)	+= pinctrl-mt8516.o
diff --git a/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch b/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch
new file mode 100644
index 0000000..6b10584
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/730-net-ethernet-mtk_eth_soc-add-mtk-dsa-tag-rx-offload.patch
@@ -0,0 +1,44 @@
+--- linux-5.4.77.orig/net/dsa/tag_mtk.c
++++ linux-5.4.77/net/dsa/tag_mtk.c
+@@ -73,22 +73,28 @@ static struct sk_buff *mtk_tag_rcv(struc
+ 	bool is_multicast_skb = is_multicast_ether_addr(dest) &&
+ 				!is_broadcast_ether_addr(dest);
+ 
+-	if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN)))
+-		return NULL;
++	if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) {
++		hdr = ntohs(skb->vlan_proto);
++		skb->vlan_proto = 0;
++		skb->vlan_tci = 0;
++	} else {
++		if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN)))
++			return NULL;
+ 
+-	/* The MTK header is added by the switch between src addr
+-	 * and ethertype at this point, skb->data points to 2 bytes
+-	 * after src addr so header should be 2 bytes right before.
+-	 */
+-	phdr = (__be16 *)(skb->data - 2);
+-	hdr = ntohs(*phdr);
++		/* The MTK header is added by the switch between src addr
++		 * and ethertype at this point, skb->data points to 2 bytes
++		 * after src addr so header should be 2 bytes right before.
++		 */
++		phdr = (__be16 *)(skb->data - 2);
++		hdr = ntohs(*phdr);
+ 
+-	/* Remove MTK tag and recalculate checksum. */
+-	skb_pull_rcsum(skb, MTK_HDR_LEN);
++		/* Remove MTK tag and recalculate checksum. */
++		skb_pull_rcsum(skb, MTK_HDR_LEN);
+ 
+-	memmove(skb->data - ETH_HLEN,
+-		skb->data - ETH_HLEN - MTK_HDR_LEN,
+-		2 * ETH_ALEN);
++		memmove(skb->data - ETH_HLEN,
++			skb->data - ETH_HLEN - MTK_HDR_LEN,
++			2 * ETH_ALEN);
++	}
+ 
+ 	/* Get source port information */
+ 	port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK);
diff --git a/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch b/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch
new file mode 100755
index 0000000..361eca6
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/738-mt7531-gsw-internal_phy_calibration.patch
@@ -0,0 +1,1282 @@
+Index: drivers/net/phy/mtk/mt753x/Makefile
+===================================================================
+--- a/drivers/net/phy/mtk/mt753x/Makefile
++++ b/drivers/net/phy/mtk/mt753x/Makefile
+@@ -7,5 +7,5 @@ obj-$(CONFIG_MT753X_GSW)	+= mt753x.o
+ mt753x-$(CONFIG_SWCONFIG)	+= mt753x_swconfig.o
+ 
+ mt753x-y			+= mt753x_mdio.o mt7530.o mt7531.o \
+-					mt753x_common.o mt753x_vlan.o mt753x_nl.o
++					mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o
+ 
+Index: drivers/net/phy/mtk/mt753x/mt7531.c
+===================================================================
+--- a/drivers/net/phy/mtk/mt753x/mt7531.c
++++ b/drivers/net/phy/mtk/mt753x/mt7531.c
+@@ -658,6 +658,27 @@ static void mt7531_core_pll_setup(struct
+ 
+ static int mt7531_internal_phy_calibration(struct gsw_mt753x *gsw)
+ {
++	u32 i, val;
++	int ret;
++
++	dev_info(gsw->dev,">>>>>>>>>>>>>>>>>>>>>>>>>>>>> START CALIBRATION:\n");
++
++	/* gphy value from sw path */
++	val = gsw->mmd_read(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403);
++	val |= GBE_EFUSE_SETTING;
++	gsw->mmd_write(gsw, 0, PHY_DEV1F, PHY_DEV1F_REG_403, val);
++
++	for (i = 0; i < 5; i++) {
++		dev_info(gsw->dev, "-------- gephy-calbration (port:%d) --------\n",
++			 i);
++		ret = mt753x_phy_calibration(gsw, i);
++
++		/* set Auto-negotiation with giga extension. */
++		gsw->mii_write(gsw, i, 0, 0x1340);
++		if (ret)
++			return ret;
++	}
++
+ 	return 0;
+ }
+ 
+Index: drivers/net/phy/mtk/mt753x/mt753x.h
+===================================================================
+--- a/drivers/net/phy/mtk/mt753x/mt753x.h
++++ b/drivers/net/phy/mtk/mt753x/mt753x.h
+@@ -140,6 +140,8 @@ void mt753x_irq_enable(struct gsw_mt753x
+ int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr);
+ int extphy_init(struct gsw_mt753x *gsw, int addr);
+ 
++int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr);
++
+ /* MDIO Indirect Access Registers */
+ #define MII_MMD_ACC_CTL_REG		0x0d
+ #define MMD_CMD_S			14
+Index: drivers/net/phy/mtk/mt753x/mt753x_phy.c
+===================================================================
+new file mode 100644
+--- /dev/null
++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.c
+@@ -0,0 +1,1069 @@
++// SPDX-License-Identifier:	GPL-2.0+
++/*
++ * Common part for MediaTek MT753x gigabit switch
++ *
++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#include <linux/kernel.h>
++#include <linux/delay.h>
++
++#include "mt753x.h"
++#include "mt753x_regs.h"
++#include "mt753x_phy.h"
++
++u32 tc_phy_read_dev_reg(struct gsw_mt753x *gsw, u32 port_num, u32 dev_addr, u32 reg_addr)
++{
++	u32 phy_val;
++    phy_val = gsw->mmd_read(gsw, port_num, dev_addr, reg_addr);
++    
++    //printk("switch phy cl45 r %d 0x%x 0x%x = %x\n",port_num, dev_addr, reg_addr, phy_val);
++	//switch_phy_read_cl45(port_num, dev_addr, reg_addr, &phy_val);
++	return phy_val;
++}
++
++void tc_phy_write_dev_reg(struct gsw_mt753x *gsw, u32 port_num, u32 dev_addr, u32 reg_addr, u32 write_data)
++{
++	u32 phy_val;
++    gsw->mmd_write(gsw, port_num, dev_addr, reg_addr, write_data);
++    phy_val = gsw->mmd_read(gsw, port_num, dev_addr, reg_addr);
++    //printk("switch phy cl45 w %d 0x%x 0x%x 0x%x --> read back 0x%x\n",port_num, dev_addr, reg_addr, write_data, phy_val);
++	//switch_phy_write_cl45(port_num, dev_addr, reg_addr, write_data);
++}
++
++void switch_phy_write(struct gsw_mt753x *gsw, u32 port_num, u32 reg_addr, u32 write_data){
++	gsw->mii_write(gsw, port_num, reg_addr, write_data);
++}
++
++u32 switch_phy_read(struct gsw_mt753x *gsw, u32 port_num, u32 reg_addr){
++	return gsw->mii_read(gsw, port_num, reg_addr);
++}
++
++const u8 MT753x_ZCAL_TO_R50ohm_GE_TBL_100[64] = {
++	127, 127, 127, 127, 127, 127, 127, 127,
++	127, 127, 127, 127, 127, 123, 122, 117,
++	115, 112, 103, 100, 98, 87, 85, 83,
++	81, 72, 70, 68, 66, 64, 55, 53,
++	52, 50, 49, 48, 38, 36, 35, 34,
++	33, 32, 22, 21, 20, 19, 18, 17,
++	16, 7, 6, 5, 4, 3, 2, 1,
++	0, 0, 0, 0, 0, 0, 0, 0
++};
++
++const u8 MT753x_TX_OFFSET_TBL[64] = {
++	0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
++	0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
++	0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8,
++	0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0,
++	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
++	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
++	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
++	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
++};
++
++u8 ge_cal_flag;
++
++u8 all_ge_ana_cal_wait(struct gsw_mt753x *gsw, u32 delay, u32 phyaddr) // for EN7512 
++{
++	u8 all_ana_cal_status;	
++	u32 cnt, tmp_1e_17c;
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017c, 0x0001);	// da_calin_flag pull high
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001);
++	//printk("delay = %d\n", delay);
++	
++	cnt = 10000;
++	do {
++		udelay(delay);
++		cnt--;
++		all_ana_cal_status = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17b) & 0x1;
++
++	} while ((all_ana_cal_status == 0) && (cnt != 0));
++
++
++	if(all_ana_cal_status == 1) {
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0);
++		return all_ana_cal_status;
++	} else {
++		tmp_1e_17c = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c);
++		if ((tmp_1e_17c & 0x1) != 1) {
++			pr_info("FIRST MDC/MDIO write error\n");
++			pr_info("FIRST 1e_17c = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c));
++
++		}
++		printk("re-K again\n");
++        
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0);
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001);
++		cnt = 10000;
++		do {
++			udelay(delay);
++			cnt--;
++			tmp_1e_17c = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c);
++			if ((tmp_1e_17c & 0x1) != 1) {
++				pr_info("SECOND MDC/MDIO write error\n");
++				pr_info("SECOND 1e_17c = %x\n", tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17c));
++				tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001);
++				tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001);
++				tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0x0001);
++			}
++		} while ((cnt != 0) && (tmp_1e_17c == 0));
++
++		cnt = 10000;
++		do {
++			udelay(delay);
++			cnt--;
++			all_ana_cal_status = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x17b) & 0x1;
++	
++		} while ((all_ana_cal_status == 0) && (cnt != 0));
++	
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x17c, 0);
++	}
++
++    if(all_ana_cal_status == 0){
++        pr_info("!!!!!!!!!!!! dev1Eh_reg17b ERROR\n");
++    }
++	
++	return all_ana_cal_status;
++}
++
++
++
++
++int ge_cal_rext(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay)
++{
++	u8 rg_zcal_ctrl, all_ana_cal_status;
++	u16 ad_cal_comp_out_init;
++	u16 dev1e_e0_ana_cal_r5;
++	int calibration_polarity;
++	u8 cnt = 0;
++	u16 dev1e_17a_tmp, dev1e_e0_tmp;
++
++	/* *** Iext/Rext Cal start ************ */
++	all_ana_cal_status = ANACAL_INIT;
++	/* analog calibration enable, Rext calibration enable */
++	/* 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a */
++	/* 1e_dc[0]:rg_txvos_calen */
++	/* 1e_e1[4]:rg_cal_refsel(0:1.2V) */
++	//tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00db, 0x1110)
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x1110);
++	//tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00dc, 0x0000);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0);
++	//tc_phy_write_dev_reg(phyaddr, 0x1e, 0x00e1, 0x0000);
++	//tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e1, 0x10);
++	
++	rg_zcal_ctrl = 0x20;/* start with 0 dB */
++	dev1e_e0_ana_cal_r5 = tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0xe0); // get default value
++	/* 1e_e0[5:0]:rg_zcal_ctrl */
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0xe0, rg_zcal_ctrl);
++	all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr);/* delay 20 usec */
++
++	if (all_ana_cal_status == 0) {
++		all_ana_cal_status = ANACAL_ERROR;
++		printk(" GE Rext AnaCal ERROR init!   \r\n");
++		return -1;
++	}
++	/* 1e_17a[8]:ad_cal_comp_out */
++	ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x017a) >> 8) & 0x1;
++	if (ad_cal_comp_out_init == 1)
++		calibration_polarity = -1;
++	else /* ad_cal_comp_out_init == 0 */
++		calibration_polarity = 1;
++	cnt = 0;
++	while (all_ana_cal_status < ANACAL_ERROR) {
++		cnt++;
++		rg_zcal_ctrl += calibration_polarity;
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0xe0, (rg_zcal_ctrl));
++		all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); /* delay 20 usec */
++		dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a);
++		if (all_ana_cal_status == 0) {
++			all_ana_cal_status = ANACAL_ERROR;
++			printk("  GE Rext AnaCal ERROR 2!   \r\n");
++			return -1;
++		} else if (((dev1e_17a_tmp >> 8) & 0x1) != ad_cal_comp_out_init) {
++			all_ana_cal_status = ANACAL_FINISH;
++			//printk("  GE Rext AnaCal Done! (%d)(0x%x)  \r\n", cnt, rg_zcal_ctrl);
++		} else {
++			dev1e_17a_tmp = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a);
++			dev1e_e0_tmp =	tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0xe0);
++			if ((rg_zcal_ctrl == 0x3F) || (rg_zcal_ctrl == 0x00)) {
++				all_ana_cal_status = ANACAL_SATURATION;  /* need to FT(IC fail?) */
++				printk(" GE Rext AnaCal Saturation!  \r\n");
++				rg_zcal_ctrl = 0x20;  /* 0 dB */
++			} 
++		}
++	}
++
++	if (all_ana_cal_status == ANACAL_ERROR) {
++		rg_zcal_ctrl = 0x20;  /* 0 dB */
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl));
++	} else if(all_ana_cal_status == ANACAL_FINISH){
++		//tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl));
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, ((rg_zcal_ctrl << 8) | rg_zcal_ctrl));
++		printk("0x1e-e0 = %x\n", tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x00e0));
++		/* ****  1f_115[2:0] = rg_zcal_ctrl[5:3]  // Mog review */
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1f, 0x0115, ((rg_zcal_ctrl & 0x3f) >> 3));
++		printk("0x1f-115 = %x\n", tc_phy_read_dev_reg(gsw,  PHY0, 0x1f, 0x115));
++		printk("  GE Rext AnaCal Done! (%d)(0x%x)  \r\n", cnt, rg_zcal_ctrl);
++		ge_cal_flag = 1;
++	} else {
++		printk("GE Rxet cal something wrong2\n");
++	}
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000);
++
++	return 0;
++}
++
++//-----------------------------------------------------------------
++int ge_cal_r50(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay)
++{
++	u8 rg_zcal_ctrl, all_ana_cal_status, calibration_pair;
++	u16 ad_cal_comp_out_init;
++	u16 dev1e_e0_ana_cal_r5;
++	int calibration_polarity;
++	u8 cnt = 0;
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x1100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000);	// 1e_dc[0]:rg_txvos_calen
++
++	for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) {
++		rg_zcal_ctrl = 0x20;  						// start with 0 dB
++		dev1e_e0_ana_cal_r5 = (tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x00e0) & (~0x003f));
++		tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl));	// 1e_e0[5:0]:rg_zcal_ctrl
++		if(calibration_pair == ANACAL_PAIR_A)
++		{
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1101);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000);	
++			//printk("R50 pair A 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00dc));
++
++		}
++		else if(calibration_pair == ANACAL_PAIR_B)
++		{
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x1000);	// 1e_dc[12]:rg_zcalen_b
++			//printk("R50 pair B 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00db),tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00dc));
++
++		}
++		else if(calibration_pair == ANACAL_PAIR_C)
++		{
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0100);	// 1e_dc[8]:rg_zcalen_c
++			//printk("R50 pair C 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00dc));
++
++		}
++		else // if(calibration_pair == ANACAL_PAIR_D)
++		{
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x1100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0010);	// 1e_dc[4]:rg_zcalen_d
++			//printk("R50 pair D 1e_db=%x 1e_db=%x\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00db), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x00dc));
++
++		}
++
++		all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec
++		if(all_ana_cal_status == 0)
++		{
++			all_ana_cal_status = ANACAL_ERROR;	
++			printk( "GE R50 AnaCal ERROR init!   \r\n");
++			return -1;
++		}
++	
++		ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x017a)>>8) & 0x1;		// 1e_17a[8]:ad_cal_comp_out	
++		if(ad_cal_comp_out_init == 1)
++			calibration_polarity = -1;
++		else
++			calibration_polarity = 1;
++
++		cnt = 0;
++		while(all_ana_cal_status < ANACAL_ERROR)
++		{
++			cnt ++;
++			rg_zcal_ctrl += calibration_polarity;
++			tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl));
++			all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec
++
++			if(all_ana_cal_status == 0)
++			{
++				all_ana_cal_status = ANACAL_ERROR;	
++				printk( "  GE R50 AnaCal ERROR 2!   \r\n");
++				return -1;
++			}
++			else if(((tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x017a)>>8)&0x1) != ad_cal_comp_out_init) 
++			{
++				all_ana_cal_status = ANACAL_FINISH;	
++			}
++			else {
++				if((rg_zcal_ctrl == 0x3F)||(rg_zcal_ctrl == 0x00))	
++				{
++					all_ana_cal_status = ANACAL_SATURATION;  // need to FT
++					printk( " GE R50 AnaCal Saturation!  \r\n");
++				}
++			}
++		}
++		
++		if(all_ana_cal_status == ANACAL_ERROR) {	
++			rg_zcal_ctrl = 0x20;  // 0 dB
++			//tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00e0, (dev1e_e0_ana_cal_r5 | rg_zcal_ctrl));
++		}
++		else {
++			rg_zcal_ctrl = MT753x_ZCAL_TO_R50ohm_GE_TBL_100[rg_zcal_ctrl - 9];	// wait Mog zcal/r50 mapping table
++			printk( " GE R50 AnaCal Done! (%d) (0x%x)(0x%x) \r\n", cnt, rg_zcal_ctrl, (rg_zcal_ctrl|0x80));
++		}
++		
++		if(calibration_pair == ANACAL_PAIR_A) {
++			ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174) & (~0x7f00);
++			//ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174);
++			//printk( " GE-a 1e_174(0x%x)(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000)));	// 1e_174[15:8]
++			//printk( " GE-a 1e_174(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++		}
++		else if(calibration_pair == ANACAL_PAIR_B) {
++			ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174) & (~0x007f);
++			//ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174);
++			//printk( " GE-b 1e_174(0x%x)(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), ad_cal_comp_out_init, tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++			
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0174, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080)));	// 1e_174[7:0]
++			//printk( " GE-b 1e_174(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++		}
++		else if(calibration_pair == ANACAL_PAIR_C) {
++			ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175) & (~0x7f00);
++			//ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175);
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<8)&0xff00) | 0x8000)));	// 1e_175[15:8]
++			//printk( " GE-c 1e_174(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++		} else {// if(calibration_pair == ANACAL_PAIR_D) 
++			ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175) & (~0x007f);
++			//ad_cal_comp_out_init = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175);
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0175, (ad_cal_comp_out_init | (((rg_zcal_ctrl<<0)&0x00ff) | 0x0080)));	// 1e_175[7:0]
++			//printk( " GE-d 1e_174(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++		}
++		//tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00e0, ((rg_zcal_ctrl<<8)|rg_zcal_ctrl));
++	}
++	
++	printk( " GE 1e_174(0x%x), 1e_175(0x%x)  \r\n", tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0174), tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x0175));
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000);
++
++	return 0;
++}
++
++int ge_cal_tx_offset(struct gsw_mt753x *gsw,  u8 phyaddr, u32 delay)
++{
++	u8 all_ana_cal_status, calibration_pair;
++	u16 ad_cal_comp_out_init;
++	int calibration_polarity, tx_offset_temp;
++	u8 tx_offset_reg_shift, tabl_idx, i;
++	u8 cnt = 0;
++	u16 tx_offset_reg, reg_temp, cal_temp;
++	//switch_phy_write(phyaddr, R0, 0x2100);//harry tmp
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0001);	// 1e_dc[0]:rg_txvos_calen
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0096, 0x8000);	// 1e_96[15]:bypass_tx_offset_cal, Hw bypass, Fw cal
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x003e, 0xf808);	// 1e_3e
++	for(i = 0; i <= 4; i++)
++		tc_phy_write_dev_reg(gsw, i, 0x1e, 0x00dd, 0x0000);	
++	for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++)
++	{
++		tabl_idx = 31;
++		tx_offset_temp = MT753x_TX_OFFSET_TBL[tabl_idx];
++
++		if(calibration_pair == ANACAL_PAIR_A) {
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, 0x145, 0x5010);
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x1000);				// 1e_dd[12]:rg_txg_calen_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017d, (0x8000|DAC_IN_0V));	// 1e_17d:dac_in0_a
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0181, (0x8000|DAC_IN_0V));	// 1e_181:dac_in1_a
++			//printk("tx offset pairA 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181));
++			reg_temp = (tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0172) & (~0x3f00));
++			tx_offset_reg_shift = 8;									// 1e_172[13:8]
++			tx_offset_reg = 0x0172;
++
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));
++		} else if(calibration_pair == ANACAL_PAIR_B) {
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, 0x145, 0x5018);
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0100);				// 1e_dd[8]:rg_txg_calen_b
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017e, (0x8000|DAC_IN_0V));	// 1e_17e:dac_in0_b
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0182, (0x8000|DAC_IN_0V));	// 1e_182:dac_in1_b
++			//printk("tx offset pairB 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181));
++			reg_temp = (tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0172) & (~0x003f));
++			tx_offset_reg_shift = 0;									// 1e_172[5:0]
++			tx_offset_reg = 0x0172;
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));
++		} else if(calibration_pair == ANACAL_PAIR_C) {
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0010);				// 1e_dd[4]:rg_txg_calen_c
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017f, (0x8000|DAC_IN_0V));	// 1e_17f:dac_in0_c
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0183, (0x8000|DAC_IN_0V));	// 1e_183:dac_in1_c
++			reg_temp = (tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0173) & (~0x3f00));
++			//printk("tx offset pairC 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181));
++			tx_offset_reg_shift = 8;									// 1e_173[13:8]
++			tx_offset_reg = 0x0173;
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));
++		} else {// if(calibration_pair == ANACAL_PAIR_D)
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0001);				// 1e_dd[0]:rg_txg_calen_d
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0180, (0x8000|DAC_IN_0V));	// 1e_180:dac_in0_d
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0184, (0x8000|DAC_IN_0V));	// 1e_184:dac_in1_d
++			//printk("tx offset pairD 1e_dd = %x, 1e_17d=%x, 1e_181=%x\n", tc_phy_read_dev_reg(phyaddr, 0x1e, 0x00dd), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x017d), tc_phy_read_dev_reg(phyaddr, 0x1e, 0x0181));
++			reg_temp = (tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x0173) & (~0x003f));
++			tx_offset_reg_shift = 0;									// 1e_173[5:0]
++			tx_offset_reg = 0x0173;
++			//tc_phy_write_dev_reg(phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));
++		}
++		tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));	// 1e_172, 1e_173
++		all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec
++		if(all_ana_cal_status == 0) {
++			all_ana_cal_status = ANACAL_ERROR;	
++			printk( " GE Tx offset AnaCal ERROR init!   \r\n");
++			return -1;
++		}
++	
++		ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a)>>8) & 0x1;		// 1e_17a[8]:ad_cal_comp_out	
++		if(ad_cal_comp_out_init == 1)
++			calibration_polarity = 1;
++		else
++			calibration_polarity = -1;
++
++		cnt = 0;
++		//printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]);
++		while(all_ana_cal_status < ANACAL_ERROR) {
++			
++			cnt ++;
++			tabl_idx += calibration_polarity;
++			//tx_offset_temp += calibration_polarity;
++			//cal_temp = tx_offset_temp;
++			cal_temp = MT753x_TX_OFFSET_TBL[tabl_idx];
++			//printk("TX offset cnt = %d, tabl_idx= %x, offset_val = %x\n", cnt, tabl_idx, MT753x_TX_OFFSET_TBL[tabl_idx]);
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(cal_temp<<tx_offset_reg_shift)));
++
++			all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec
++			if(all_ana_cal_status == 0) {
++				all_ana_cal_status = ANACAL_ERROR;	
++				printk( " GE Tx offset AnaCal ERROR init 2!   \r\n");
++				return -1;
++			} else if(((tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x017a)>>8)&0x1) != ad_cal_comp_out_init) {
++				all_ana_cal_status = ANACAL_FINISH;	
++			} else {
++				if((tabl_idx == 0)||(tabl_idx == 0x3f)) {
++					all_ana_cal_status = ANACAL_SATURATION;  // need to FT
++					printk( " GE Tx offset AnaCal Saturation!  \r\n");
++				}
++			}
++		}
++		
++		if(all_ana_cal_status == ANACAL_ERROR) {	
++			tx_offset_temp = TX_AMP_OFFSET_0MV;
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg, (reg_temp|(tx_offset_temp<<tx_offset_reg_shift)));
++		} else {
++			printk( " GE Tx offset AnaCal Done! (pair-%d)(%d)(0x%x) 0x1e_%x=0x%x\n", calibration_pair, cnt, MT753x_TX_OFFSET_TBL[tabl_idx], tx_offset_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_offset_reg));
++		}
++	}
++
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017d, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017e, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017f, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0180, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0181, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0182, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0183, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0184, 0x0000);
++	
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000);	// disable analog calibration circuit
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000);	// disable Tx offset calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000);	// disable analog calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000);	// disable Tx offset calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x003e, 0x0000);	// disable Tx VLD force mode
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0000);	// disable Tx offset/amplitude calibration circuit	
++
++	return 0;
++}
++
++int ge_cal_tx_amp(struct gsw_mt753x *gsw, u8 phyaddr, u32 delay)
++{
++	u8	all_ana_cal_status, calibration_pair, i;
++	u16	ad_cal_comp_out_init;
++	int	calibration_polarity;
++	u32	tx_amp_reg_shift; 
++	u16	reg_temp;
++	u32	tx_amp_temp, tx_amp_reg, cnt=0, tx_amp_reg_100;
++	u32	debug_tmp, reg_backup, reg_tmp; 
++	u32	orig_1e_11, orig_1f_300;
++
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x1100);	// 1e_db[12]:rg_cal_ckinv, [8]:rg_ana_calen, [4]:rg_rext_calen, [0]:rg_zcalen_a
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0001);	// 1e_dc[0]:rg_txvos_calen
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e1, 0x0010);	// 1e_e1[4]:select 1V
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x003e, 0xf808);	// 1e_3e:enable Tx VLD
++
++	orig_1e_11 = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x11);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x11, 0xff00);
++//	tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x27a, 0x33);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0xc9, 0xffff);
++	orig_1f_300 = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x300);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x300, 0x4);
++	for(i = 0; i <= 4; i++)
++		tc_phy_write_dev_reg(gsw, i, 0x1e, 0x00dd, 0x0000);
++	for(calibration_pair = ANACAL_PAIR_A; calibration_pair <= ANACAL_PAIR_D; calibration_pair ++) {
++		tx_amp_temp = 0x20;	// start with 0 dB
++
++		if(calibration_pair == ANACAL_PAIR_A) {
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x1000);				// 1e_dd[12]:tx_a amp calibration enable
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017d, (0x8000|DAC_IN_2V));	// 1e_17d:dac_in0_a	
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0181, (0x8000|DAC_IN_2V));	// 1e_181:dac_in1_a
++			reg_temp = (tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x012) & (~0xfc00));
++			tx_amp_reg_shift = 10;										// 1e_12[15:10]
++			tx_amp_reg = 0x12;
++			tx_amp_reg_100 = 0x16;
++		} else if(calibration_pair == ANACAL_PAIR_B) {
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0100);				// 1e_dd[8]:tx_b amp calibration enable
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017e, (0x8000|DAC_IN_2V));	// 1e_17e:dac_in0_b
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0182, (0x8000|DAC_IN_2V));	// 1e_182:dac_in1_b
++			reg_temp = (tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x017) & (~0x3f00));
++			tx_amp_reg_shift = 8;										// 1e_17[13:8]
++			tx_amp_reg = 0x17;
++			tx_amp_reg_100 = 0x18;
++		} else if(calibration_pair == ANACAL_PAIR_C) {
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0010);				// 1e_dd[4]:tx_c amp calibration enable
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017f, (0x8000|DAC_IN_2V));	// 1e_17f:dac_in0_c
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0183, (0x8000|DAC_IN_2V));	// 1e_183:dac_in1_c
++			reg_temp = (tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x019) & (~0x3f00));
++			tx_amp_reg_shift = 8;										// 1e_19[13:8]
++			tx_amp_reg = 0x19;
++			tx_amp_reg_100 = 0x20;
++		} else { //if(calibration_pair == ANACAL_PAIR_D)
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0001);				// 1e_dd[0]:tx_d amp calibration enable
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0180, (0x8000|DAC_IN_2V));	// 1e_180:dac_in0_d
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0184, (0x8000|DAC_IN_2V));	// 1e_184:dac_in1_d
++			reg_temp = (tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x021) & (~0x3f00));
++			tx_amp_reg_shift = 8;										// 1e_21[13:8]
++			tx_amp_reg = 0x21;
++			tx_amp_reg_100 = 0x22;
++		}
++		tc_phy_write_dev_reg( gsw, phyaddr, 0x1e, tx_amp_reg, (tx_amp_temp|(tx_amp_temp<<tx_amp_reg_shift)));	// 1e_12, 1e_17, 1e_19, 1e_21
++		tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100, (tx_amp_temp|(tx_amp_temp<<tx_amp_reg_shift)));
++		all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); 	// delay 20 usec
++		if(all_ana_cal_status == 0) {
++			all_ana_cal_status = ANACAL_ERROR;	
++			printk( " GE Tx amp AnaCal ERROR init init!   \r\n");
++			return -1;
++		}
++	
++		ad_cal_comp_out_init = (tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x017a)>>8) & 0x1;		// 1e_17a[8]:ad_cal_comp_out
++		if(ad_cal_comp_out_init == 1)
++			calibration_polarity = -1;
++		else
++			calibration_polarity = 1;
++
++		cnt =0;
++		while(all_ana_cal_status < ANACAL_ERROR) {
++			cnt ++;
++			tx_amp_temp += calibration_polarity;
++			//printk("tx_amp : %x, 1e %x = %x\n", tx_amp_temp, tx_amp_reg, (reg_temp|(tx_amp_temp<<tx_amp_reg_shift)));
++			tc_phy_write_dev_reg( gsw, phyaddr, 0x1e, tx_amp_reg, (tx_amp_temp|(tx_amp_temp<<tx_amp_reg_shift)));
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100, (tx_amp_temp|(tx_amp_temp<<tx_amp_reg_shift)));
++			all_ana_cal_status = all_ge_ana_cal_wait(gsw, delay, phyaddr); // delay 20 usec
++			if(all_ana_cal_status == 0) {
++				all_ana_cal_status = ANACAL_ERROR;	
++				printk( " GE Tx amp AnaCal ERROR 2!   \r\n");
++				return -1;
++			} else if(((tc_phy_read_dev_reg(gsw,  PHY0, 0x1e, 0x017a)>>8)&0x1) != ad_cal_comp_out_init) {
++				//printk("TX AMP ANACAL_FINISH\n");
++				all_ana_cal_status = ANACAL_FINISH;
++				if (phyaddr == 0) {
++					if (calibration_pair == ANACAL_PAIR_A)
++						tx_amp_temp = tx_amp_temp - 2;
++					else if(calibration_pair == ANACAL_PAIR_B)
++						tx_amp_temp = tx_amp_temp - 1;
++					else if(calibration_pair == ANACAL_PAIR_C)
++						tx_amp_temp = tx_amp_temp - 2;
++					else if(calibration_pair == ANACAL_PAIR_D)
++						tx_amp_temp = tx_amp_temp - 1;
++				} else if (phyaddr == 1) {
++					if (calibration_pair == ANACAL_PAIR_A)
++						tx_amp_temp = tx_amp_temp - 1;
++					else if(calibration_pair == ANACAL_PAIR_B)
++						tx_amp_temp = tx_amp_temp ;
++					else if(calibration_pair == ANACAL_PAIR_C)
++						tx_amp_temp = tx_amp_temp - 1;
++					else if(calibration_pair == ANACAL_PAIR_D)
++						tx_amp_temp = tx_amp_temp - 1;
++				} else if (phyaddr == 2) {
++					if (calibration_pair == ANACAL_PAIR_A)
++						tx_amp_temp = tx_amp_temp;
++					else if(calibration_pair == ANACAL_PAIR_B)
++						tx_amp_temp = tx_amp_temp - 1;
++					else if(calibration_pair == ANACAL_PAIR_C)
++						tx_amp_temp = tx_amp_temp;
++					else if(calibration_pair == ANACAL_PAIR_D)
++						tx_amp_temp = tx_amp_temp - 1;
++				} else if (phyaddr == 3) {
++					tx_amp_temp = tx_amp_temp;
++				} else if (phyaddr == 4) {
++					if (calibration_pair == ANACAL_PAIR_A)
++						tx_amp_temp = tx_amp_temp;
++					else if(calibration_pair == ANACAL_PAIR_B)
++						tx_amp_temp = tx_amp_temp - 1;
++					else if(calibration_pair == ANACAL_PAIR_C)
++						tx_amp_temp = tx_amp_temp;
++					else if(calibration_pair == ANACAL_PAIR_D)
++						tx_amp_temp = tx_amp_temp;
++				}								
++				reg_temp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, tx_amp_reg)&(~0xff00);
++				tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)));
++				tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, (tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)));
++				if (phyaddr == 0) {
++					if ((tx_amp_reg == 0x12) || (tx_amp_reg == 0x17)) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 7));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg_100 == 0x16) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp+1+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++					if (tx_amp_reg_100 == 0x18) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++				} else if (phyaddr == 1) {
++					if (tx_amp_reg == 0x12) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 9));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg == 0x17){
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 7));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg_100 == 0x16) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++					if (tx_amp_reg_100 == 0x18) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-1+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++				} else if (phyaddr == 2) {
++					if ((tx_amp_reg == 0x12) || (tx_amp_reg == 0x17)) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 6));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if ((tx_amp_reg_100 == 0x16) || (tx_amp_reg_100 == 0x18)) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-1+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++				} else if (phyaddr == 3) {
++					if (tx_amp_reg == 0x12) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 4));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg == 0x17) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 7));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg_100 == 0x16) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-2+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++					if (tx_amp_reg_100 == 0x18) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-1+3)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++				} else if (phyaddr == 4) {
++					if ((tx_amp_reg == 0x12) || (tx_amp_reg == 0x17)) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, ((tx_amp_temp|((tx_amp_temp)<<tx_amp_reg_shift)) + 5));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++					}
++					if (tx_amp_reg_100 == 0x16) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-2+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++					if (tx_amp_reg_100 == 0x18) {
++						//printk("before : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++						tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100,(tx_amp_temp|((tx_amp_temp-1+4)<<tx_amp_reg_shift)));
++						//printk("after : PORT[%d] 1e_%x = %x\n", phyaddr, tx_amp_reg_100, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg_100));
++					}
++				}	
++
++				if (calibration_pair == ANACAL_PAIR_A){
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x12);
++					reg_tmp = ((reg_backup & 0xfc00) >> 10);
++					reg_tmp -= 8;
++                                       reg_backup = 0x0000;
++                                       reg_backup |= ((reg_tmp << 10) | (reg_tmp << 0));
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x12, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x12);
++					//printk("PORT[%d] 1e.012 = %x (OFFSET_1000M_PAIR_A)\n", phyaddr, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x16);
++					reg_tmp = ((reg_backup & 0x3f) >> 0);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f));
++					reg_backup |= (reg_tmp << 0);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x16, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x16);
++					//printk("PORT[%d] 1e.016 = %x (OFFSET_TESTMODE_1000M_PAIR_A)\n", phyaddr, reg_backup);
++				}
++				else if(calibration_pair == ANACAL_PAIR_B){
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x17);
++					reg_tmp = ((reg_backup & 0x3f00) >> 8);
++					reg_tmp -= 8;
++                                       reg_backup = 0x0000;
++                                       reg_backup |= ((reg_tmp << 8) | (reg_tmp << 0));
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x17, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x17);
++					//printk("PORT[%d] 1e.017 = %x (OFFSET_1000M_PAIR_B)\n", phyaddr, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x18);
++					reg_tmp = ((reg_backup & 0x3f) >> 0);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f));
++					reg_backup |= (reg_tmp << 0);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x18);
++					//printk("PORT[%d] 1e.018 = %x (OFFSET_TESTMODE_1000M_PAIR_B)\n", phyaddr, reg_backup);
++				}
++				else if(calibration_pair == ANACAL_PAIR_C){
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x19);
++					reg_tmp = ((reg_backup & 0x3f00) >> 8);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f00));
++					reg_backup |= (reg_tmp << 8);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x19, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x19);
++					//printk("PORT[%d] 1e.019 = %x (OFFSET_1000M_PAIR_C)\n", phyaddr, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x20);
++					reg_tmp = ((reg_backup & 0x3f) >> 0);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f));
++					reg_backup |= (reg_tmp << 0);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x20, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x20);
++					//printk("PORT[%d] 1e.020 = %x (OFFSET_TESTMODE_1000M_PAIR_C)\n", phyaddr, reg_backup);
++				}
++				else if(calibration_pair == ANACAL_PAIR_D){
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x21);
++					reg_tmp = ((reg_backup & 0x3f00) >> 8);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f00));
++					reg_backup |= (reg_tmp << 8);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x21, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x21);
++					//printk("PORT[%d] 1e.021 = %x (OFFSET_1000M_PAIR_D)\n", phyaddr, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x22);
++					reg_tmp = ((reg_backup & 0x3f) >> 0);
++					reg_tmp -= 8;
++					reg_backup = (reg_backup & (~0x3f));
++					reg_backup |= (reg_tmp << 0);
++					tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x22, reg_backup);
++					reg_backup = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x22);
++					//printk("PORT[%d] 1e.022 = %x (OFFSET_TESTMODE_1000M_PAIR_D)\n", phyaddr, reg_backup);
++				}
++
++				if (calibration_pair == ANACAL_PAIR_A){
++					//printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x12);
++					//printk("1e.012 = 0x%x\n", debug_tmp);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x16);
++					//printk("1e.016 = 0x%x\n", debug_tmp);
++				}
++	
++				else if(calibration_pair == ANACAL_PAIR_B){
++					//printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x17);
++					//printk("1e.017 = 0x%x\n", debug_tmp);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x18);
++					//printk("1e.018 = 0x%x\n", debug_tmp);
++				}
++				else if(calibration_pair == ANACAL_PAIR_C){
++					//printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x19);
++					//printk("1e.019 = 0x%x\n", debug_tmp);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x20);
++					//printk("1e.020 = 0x%x\n", debug_tmp);
++				}
++				else if(calibration_pair == ANACAL_PAIR_D){
++					//printk("PORT (%d) TX_AMP PAIR (A) FINAL CALIBRATION RESULT\n", phyaddr);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x21);
++					//printk("1e.021 = 0x%x\n", debug_tmp);
++					debug_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x22);
++					//printk("1e.022 = 0x%x\n", debug_tmp);
++				}
++
++
++				printk( " GE Tx amp AnaCal Done! (pair-%d)(1e_%x = 0x%x)\n", calibration_pair, tx_amp_reg, tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg));
++				
++			} else {
++				if((tx_amp_temp == 0x3f)||(tx_amp_temp == 0x00)) {
++					all_ana_cal_status = ANACAL_SATURATION;  // need to FT
++					printk( " GE Tx amp AnaCal Saturation!  \r\n");
++				}
++			}
++		}
++
++		if(all_ana_cal_status == ANACAL_ERROR) {	
++			tx_amp_temp = 0x20;
++			tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, tx_amp_reg, (reg_temp|(tx_amp_temp<<tx_amp_reg_shift)));
++		}
++	}
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017d, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017e, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x017f, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0180, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0181, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0182, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0183, 0x0000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0184, 0x0000);
++	
++	/* disable analog calibration circuit */
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00db, 0x0000);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00dc, 0x0000);	// disable Tx offset calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00db, 0x0000);	// disable analog calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dc, 0x0000);	// disable Tx offset calibration circuit
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x003e, 0x0000);	// disable Tx VLD force mode
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x00dd, 0x0000);	// disable Tx offset/amplitude calibration circuit
++	
++	
++
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x273, 0x2000);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0xc9, 0x0fff);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x145, 0x1000);
++
++	/* Restore CR to default */
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x11, orig_1e_11);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x300, orig_1f_300);
++
++	return 0;
++}
++
++//-----------------------------------------------------------------
++
++int phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++	//u32	reg_tmp,reg_tmp0, reg_tmp1, i;
++	u32 reg_tmp;
++	u32 CALDLY = 40;
++	u32 orig_1e_11, orig_1e_185, orig_1e_e1, orig_1f_100;
++	int ret;
++	/* set [12]AN disable, [8]full duplex, [13/6]1000Mbps */
++	//tc_phy_write_dev_reg(phyaddr, 0x0,  0x0140);
++	switch_phy_write(gsw, phyaddr, R0, 0x140);
++
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x145, 0x1010);/* fix mdi */
++	orig_1e_185 = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, RG_185);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, RG_185, 0);/* disable tx slew control */
++	orig_1f_100 = tc_phy_read_dev_reg(gsw, phyaddr, 0x1f, 0x100);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x100, 0xc000);/* BG voltage output */
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x403, 0x1099); //bypass efuse
++
++#if (1)
++	//	1f_27c[12:8] cr_da_tx_i2mpb_10m	Trimming TX bias setup(@10M)
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x27c, 0x1f1f);
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x27c, 0x3300);
++
++	//reg_tmp1 = tc_phy_read_dev_reg(gsw,  PHY0, 0x1f, 0x27c);
++	//dev1Fh_reg273h TXVLD DA register	- Adjust voltage mode TX amplitude.
++	//tc_phy_write_dev_reg(phyaddr, 0x1f, 0x273, 0);
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x273, 0x1000);
++	//reg_tmp1 = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x273);
++	//printk("reg_tmp1273 = %x\n", reg_tmp1);
++	/*1e_11 TX  overshoot Enable (PAIR A/B/C/D) in gbe mode*/
++
++	orig_1e_11 = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x11);
++	reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x11);
++	reg_tmp = reg_tmp | (0xf << 12);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x11, reg_tmp);
++	orig_1e_e1 = tc_phy_read_dev_reg(gsw, PHY0, 0x1e, 0x00e1);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e1, 0x10);
++	/* calibration start ============ */
++	printk("CALDLY = %d\n", CALDLY);
++	if(ge_cal_flag == 0){
++		ret = ge_cal_rext(gsw, 0, CALDLY);
++		if (ret == -1){
++			printk("ge_cal_rext error K port =%d\n", phyaddr);
++			return ret;
++		}
++		ge_cal_flag = 1;
++	}
++
++	/* *** R50 Cal start ***************************** */
++	/*phyaddress = 0*/
++	ret = ge_cal_r50(gsw, phyaddr, CALDLY);
++	if (ret == -1){
++		printk("R50 error K port =%d\n", phyaddr);
++		return ret;
++	}
++	/* *** R50 Cal end *** */
++	/* *** Tx offset Cal start *********************** */
++	ret = ge_cal_tx_offset(gsw, phyaddr, CALDLY);
++	if (ret == -1){
++		printk("ge_cal_tx_offset error K port =%d\n", phyaddr);
++		return ret;
++	}
++	/* *** Tx offset Cal end *** */
++
++	/* *** Tx Amp Cal start *** */
++	ret = ge_cal_tx_amp(gsw, phyaddr, CALDLY);
++	if (ret == -1){
++		printk("ge_cal_tx_amp error K port =%d\n", phyaddr);
++		return ret;
++	}
++	/* *** Tx Amp Cal end *** */
++	/*tmp maybe changed*/
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x27c, 0x1111);
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x27b, 0x47);
++	//tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x273, 0x2000);
++
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3a8, 0x0810);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3aa, 0x0008);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3ab, 0x0810);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3ad, 0x0008);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3ae, 0x0106);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3b0, 0x0001);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3b1, 0x0106);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3b3, 0x0001);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18c, 0x0001);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18d, 0x0001);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18e, 0x0001);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x18f, 0x0001);
++
++	/*da_tx_bias1_b_tx_standby = 5'b10 (dev1eh_reg3aah[12:8])*/
++	reg_tmp = tc_phy_read_dev_reg(gsw, phyaddr, 0x1e, 0x3aa);
++	reg_tmp = reg_tmp & ~(0x1f00);
++	reg_tmp = reg_tmp | 0x2 << 8;
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3aa, reg_tmp);
++
++	/*da_tx_bias1_a_tx_standby = 5'b10 (dev1eh_reg3a9h[4:0])*/
++	reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1e, 0x3a9);
++	reg_tmp = reg_tmp & ~(0x1f);
++	reg_tmp = reg_tmp | 0x2;
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3a9, reg_tmp);
++
++	/* Restore CR to default */
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, RG_185, orig_1e_185);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x100, orig_1f_100);
++	tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x11, orig_1e_11);
++	tc_phy_write_dev_reg(gsw, PHY0, 0x1e, 0x00e1, orig_1e_e1);
++#endif
++	return 0;
++}
++
++void rx_dc_offset(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++    pr_info("PORT %d RX_DC_OFFSET\n", phyaddr);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x96, 0x8000);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x37, 0x3);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x107, 0x4000);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x171, 0x1e5);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x39, 0x200f);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x39, 0x000f);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x171, 0x65);
++}
++
++void check_rx_dc_offset_pair_a(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++    u32 reg_tmp;
++
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x114f);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;
++    pr_info("before pairA output = %x\n", reg_tmp);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1142);
++    udelay(40);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;   
++    pr_info("after pairA output = %x\n", reg_tmp);
++    if ((reg_tmp & 0x80) != 0)
++        reg_tmp = (~reg_tmp) + 1;
++    if ((reg_tmp & 0xff) >4)
++        pr_info("pairA RX_DC_OFFSET error");
++}
++
++void check_rx_dc_offset_pair_b(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++    u32 reg_tmp;
++
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1151);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;
++    pr_info("before pairB output = %x\n", reg_tmp);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1143);
++    udelay(40);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;   
++    pr_info("after pairB output = %x\n", reg_tmp);
++    if ((reg_tmp & 0x80) != 0)
++        reg_tmp = (~reg_tmp) + 1;
++    if ((reg_tmp & 0xff) >4)
++        pr_info("pairB RX_DC_OFFSET error");
++}
++
++void check_rx_dc_offset_pair_c(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++    u32 reg_tmp;
++
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1153);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;
++    pr_info("before pairC output = %x\n", reg_tmp);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1144);
++    udelay(40);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;   
++    pr_info("after pairC output = %x\n", reg_tmp);
++    if ((reg_tmp & 0x80) != 0)
++        reg_tmp = (~reg_tmp) + 1;
++    if ((reg_tmp & 0xff) >4)
++        pr_info("pairC RX_DC_OFFSET error");
++}
++
++void check_rx_dc_offset_pair_d(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++    u32 reg_tmp;
++
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1155);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;
++    pr_info("before pairD output = %x\n", reg_tmp);
++    udelay(40);
++    tc_phy_write_dev_reg(gsw, phyaddr, 0x1f, 0x15, (phyaddr << 13) | 0x1145);
++    udelay(40);
++    reg_tmp = tc_phy_read_dev_reg(gsw,  phyaddr, 0x1f, 0x1a);
++    reg_tmp = reg_tmp & 0xff;   
++    pr_info("after pairD output = %x\n", reg_tmp);
++    if ((reg_tmp & 0x80) != 0)
++        reg_tmp = (~reg_tmp) + 1;
++    if ((reg_tmp & 0xff) >4)
++        pr_info("pairD RX_DC_OFFSET error");
++}
++
++
++int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr){
++
++	int ret;
++
++	ret = phy_calibration(gsw, phyaddr);
++
++	rx_dc_offset(gsw, phyaddr);
++	check_rx_dc_offset_pair_a(gsw, phyaddr);
++	check_rx_dc_offset_pair_b(gsw, phyaddr);
++	check_rx_dc_offset_pair_c(gsw, phyaddr);
++	check_rx_dc_offset_pair_d(gsw, phyaddr);
++
++	return ret;
++}
+Index: drivers/net/phy/mtk/mt753x/mt753x_phy.h
+===================================================================
+new file mode 100644
+--- /dev/null
++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.h
+@@ -0,0 +1,145 @@
++/* SPDX-License-Identifier:	GPL-2.0+ */
++/*
++ * Register definitions for MediaTek MT753x Gigabit switches
++ *
++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Weijie Gao <weijie.gao@mediatek.com>
++ */
++
++#ifndef _MT753X_PHY_H_
++#define _MT753X_PHY_H_
++
++#include <linux/bitops.h>
++
++/*phy calibration use*/
++#define DEV_1E				0x1E
++/*global device 0x1f, always set P0*/
++#define DEV_1F				0x1F
++
++
++/************IEXT/REXT CAL***************/
++/* bits range: for example BITS(16,23) = 0xFF0000*/
++#define BITS(m, n)   (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
++#define ANACAL_INIT			0x01
++#define ANACAL_ERROR			0xFD
++#define ANACAL_SATURATION		0xFE
++#define	ANACAL_FINISH			0xFF
++#define ANACAL_PAIR_A			0
++#define ANACAL_PAIR_B			1
++#define ANACAL_PAIR_C			2
++#define ANACAL_PAIR_D			3
++#define DAC_IN_0V			0x00
++#define DAC_IN_2V			0xf0
++#define TX_AMP_OFFSET_0MV		0x20
++#define TX_AMP_OFFSET_VALID_BITS	6
++
++#define R0				0
++#define PHY0				0
++#define PHY1				1
++#define PHY2				2
++#define PHY3				3
++#define PHY4				4
++#define ANA_TEST_MODE			BITS(8, 15)
++#define TST_TCLK_SEL			BITs(6, 7)
++#define ANA_TEST_VGA_RG			0x100
++
++#define FORCE_MDI_CROSS_OVER		BITS(3, 4)
++#define T10_TEST_CTL_RG			0x145
++#define RG_185				0x185
++#define RG_TX_SLEW			BIT(0)
++#define ANA_CAL_0			0xdb
++#define RG_CAL_CKINV			BIT(12)
++#define RG_ANA_CALEN			BIT(8)
++#define RG_REXT_CALEN			BIT(4)
++#define RG_ZCALEN_A			BIT(0)
++#define ANA_CAL_1			0xdc
++#define RG_ZCALEN_B			BIT(12)
++#define RG_ZCALEN_C			BIT(8)
++#define RG_ZCALEN_D			BIT(4)
++#define RG_TXVOS_CALEN			BIT(0)
++#define ANA_CAL_6			0xe1
++#define RG_CAL_REFSEL			BIT(4)
++#define RG_CAL_COMP_PWD			BIT(0)
++#define ANA_CAL_5			0xe0
++#define RG_REXT_TRIM			BITs(8, 13)
++#define RG_ZCAL_CTRL			BITs(0, 5)
++#define RG_17A				0x17a
++#define AD_CAL_COMP_OUT			BIT(8)
++#define RG_17B				0x17b
++#define AD_CAL_CLK			bit(0)
++#define RG_17C				0x17c
++#define DA_CALIN_FLAG			bit(0)
++/************R50 CAL****************************/
++#define RG_174				0x174
++#define RG_R50OHM_RSEL_TX_A_EN		BIT[15]
++#define CR_R50OHM_RSEL_TX_A		BITS[8:14]
++#define RG_R50OHM_RSEL_TX_B_EN		BIT[7]
++#define CR_R50OHM_RSEL_TX_B		BITS[6:0]
++#define RG_175				0x175
++#define RG_R50OHM_RSEL_TX_C_EN		BITS[15]
++#define CR_R50OHM_RSEL_TX_C		BITS[8:14]
++#define RG_R50OHM_RSEL_TX_D_EN		BIT[7]
++#define CR_R50OHM_RSEL_TX_D		BITS[0:6]
++/**********TX offset Calibration***************************/
++#define RG_95				0x96
++#define BYPASS_TX_OFFSET_CAL		BIT(15)
++#define RG_3E				0x3e
++#define BYPASS_PD_TXVLD_A		BIT(15)
++#define BYPASS_PD_TXVLD_B		BIT(14)
++#define BYPASS_PD_TXVLD_C		BIT(13)
++#define BYPASS_PD_TXVLD_D		BIT(12)
++#define BYPASS_PD_TX_10M		BIT(11)
++#define POWER_DOWN_TXVLD_A		BIT(7)
++#define POWER_DOWN_TXVLD_B		BIT(6)
++#define POWER_DOWN_TXVLD_C		BIT(5)
++#define POWER_DOWN_TXVLD_D		BIT(4)
++#define POWER_DOWN_TX_10M		BIT(3)
++#define RG_DD				0xdd
++#define RG_TXG_CALEN_A			BIT(12)
++#define RG_TXG_CALEN_B			BIT(8)
++#define RG_TXG_CALEN_C			BIT(4)
++#define RG_TXG_CALEN_D			BIT(0)
++#define RG_17D				0x17D
++#define FORCE_DASN_DAC_IN0_A		BIT(15)
++#define DASN_DAC_IN0_A			BITS(0, 9)
++#define RG_17E				0x17E
++#define FORCE_DASN_DAC_IN0_B		BIT(15)
++#define DASN_DAC_IN0_B			BITS(0, 9)
++#define RG_17F				0x17F
++
++#define FORCE_DASN_DAC_IN0_C		BIT(15)
++#define DASN_DAC_IN0_C			BITS(0, 9)
++#define RG_180				0x180
++#define FORCE_DASN_DAC_IN0_D		BIT(15)
++#define DASN_DAC_IN0_D			BITS(0, 9)
++
++#define RG_181				0x181
++#define FORCE_DASN_DAC_IN1_A		BIT(15)
++#define DASN_DAC_IN1_A			BITS(0, 9)
++#define RG_182				0x182
++#define FORCE_DASN_DAC_IN1_B		BIT(15)
++#define DASN_DAC_IN1_B			BITS(0, 9)
++#define RG_183				0x183
++#define FORCE_DASN_DAC_IN1_C		BIT15]
++#define DASN_DAC_IN1_C			BITS(0, 9)
++#define RG_184				0x184
++#define FORCE_DASN_DAC_IN1_D		BIT(15)
++#define DASN_DAC_IN1_D			BITS(0, 9)
++#define RG_172				0x172
++#define CR_TX_AMP_OFFSET_A		BITS(8, 13)
++#define CR_TX_AMP_OFFSET_B		BITS(0, 5)
++#define RG_173				0x173
++#define CR_TX_AMP_OFFSET_C		BITS(8, 13)
++#define CR_TX_AMP_OFFSET_D		BITS(0, 5)
++/**********TX Amp Calibration ***************************/
++#define RG_12				0x12
++#define DA_TX_I2MPB_A_GBE		BITS(10, 15)
++#define RG_17				0x17
++#define DA_TX_I2MPB_B_GBE		BITS(8, 13)
++#define RG_19				0x19
++#define DA_TX_I2MPB_C_GBE		BITS(8, 13)
++#define RG_21				0x21
++#define DA_TX_I2MPB_D_GBE		BITS(8, 13)
++
++#endif /* _MT753X_REGS_H_ */
diff --git a/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch b/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch
new file mode 100755
index 0000000..0d88c60
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/739-mt7531-gsw-port5_external_phy_init.patch
@@ -0,0 +1,156 @@
+From 9206472ba03032aea120604e8637b52408ca4b3a Mon Sep 17 00:00:00 2001
+From: Landen Chao <landen.chao@mediatek.com>
+Date: Fri, 29 May 2020 15:12:35 +0800
+Subject: [PATCH 2/2] 740_patch
+
+Change-Id: I7e0164751702f573d5185c4290ff78688f42f603
+---
+ drivers/net/phy/mtk/mt753x/Makefile        |  3 +-
+ drivers/net/phy/mtk/mt753x/mt7531.c        |  3 +
+ drivers/net/phy/mtk/mt753x/mt753x.h        |  1 +
+ drivers/net/phy/mtk/mt753x/mt753x_extphy.c | 69 ++++++++++++++++++++++
+ drivers/net/phy/mtk/mt753x/mt753x_extphy.h | 18 ++++++
+ 5 files changed, 93 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/phy/mtk/mt753x/mt753x_extphy.c
+ create mode 100644 drivers/net/phy/mtk/mt753x/mt753x_extphy.h
+
+diff --git a/drivers/net/phy/mtk/mt753x/Makefile b/drivers/net/phy/mtk/mt753x/Makefile
+index 384b0ff7..694ffa83 100644
+--- a/drivers/net/phy/mtk/mt753x/Makefile
++++ b/drivers/net/phy/mtk/mt753x/Makefile
+@@ -7,5 +7,6 @@ obj-$(CONFIG_MT753X_GSW)	+= mt753x.o
+ mt753x-$(CONFIG_SWCONFIG)	+= mt753x_swconfig.o
+ 
+ mt753x-y			+= mt753x_mdio.o mt7530.o mt7531.o \
+-					mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o
++					mt753x_common.o mt753x_vlan.o mt753x_nl.o mt753x_phy.o \
++					mt753x_extphy.o
+ 
+diff --git a/drivers/net/phy/mtk/mt753x/mt7531.c b/drivers/net/phy/mtk/mt753x/mt7531.c
+index 04729835..4a2943b1 100644
+--- a/drivers/net/phy/mtk/mt753x/mt7531.c
++++ b/drivers/net/phy/mtk/mt753x/mt7531.c
+@@ -265,6 +265,9 @@ static int mt7531_set_port_sgmii_force_mode(struct gsw_mt753x *gsw, u32 port,
+ 		return -EINVAL;
+ 	}
+ 
++	if (port == 5)
++		extphy_init(gsw, port);
++
+ 	port_base = port - 5;
+ 
+ 	switch (port_cfg->speed) {
+diff --git a/drivers/net/phy/mtk/mt753x/mt753x.h b/drivers/net/phy/mtk/mt753x/mt753x.h
+index 5053a7d7..a3f343cd 100644
+--- a/drivers/net/phy/mtk/mt753x/mt753x.h
++++ b/drivers/net/phy/mtk/mt753x/mt753x.h
+@@ -154,6 +154,7 @@ void mt753x_irq_worker(struct work_struct *work);
+ void mt753x_irq_enable(struct gsw_mt753x *gsw);
+ 
+ int mt753x_phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr);
++int extphy_init(struct gsw_mt753x *gsw, int addr);
+ 
+ /* MDIO Indirect Access Registers */
+ #define MII_MMD_ACC_CTL_REG		0x0d
+diff --git a/drivers/net/phy/mtk/mt753x/mt753x_extphy.c b/drivers/net/phy/mtk/mt753x/mt753x_extphy.c
+new file mode 100644
+index 00000000..f58e8a62
+--- /dev/null
++++ b/drivers/net/phy/mtk/mt753x/mt753x_extphy.c
+@@ -0,0 +1,69 @@
++/*
++ * Driver for MediaTek MT7531 gigabit switch
++ *
++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Landen Chao <landen.chao@mediatek.com>
++ *
++ * SPDX-License-Identifier:	GPL-2.0+
++ */
++
++#include <linux/kernel.h>
++#include <linux/mii.h>
++
++#include "mt753x.h"
++#include "mt753x_regs.h"
++#include "mt753x_extphy.h"
++
++int gpy211_init(struct gsw_mt753x *gsw, int addr)
++{
++	/* Enable rate adaption */
++	gsw->mmd_write(gsw, addr, 0x1e, 0x8, 0x24e2);
++
++	return 0;
++}
++
++static struct mt753x_extphy_id extphy_tbl[] = {
++        {0x67c9de00, 0x0fffffff0, gpy211_init},
++};
++
++static u32 get_cl22_phy_id(struct gsw_mt753x *gsw, int addr)
++{
++	int phy_reg;
++	u32 phy_id = 0;
++
++	phy_reg = gsw->mii_read(gsw, addr, MII_PHYSID1);
++	if (phy_reg < 0)
++		return 0;
++	phy_id = (phy_reg & 0xffff) << 16;
++
++	/* Grab the bits from PHYIR2, and put them in the lower half */
++	phy_reg = gsw->mii_read(gsw, addr, MII_PHYSID2);
++	if (phy_reg < 0)
++		return 0;
++
++	phy_id |= (phy_reg & 0xffff);
++
++	return phy_id;
++}
++
++static inline bool phy_id_is_match(u32 id, struct mt753x_extphy_id *phy)
++{
++	return ((id & phy->phy_id_mask) == (phy->phy_id & phy->phy_id_mask));
++}
++
++int extphy_init(struct gsw_mt753x *gsw, int addr)
++{
++	int i;
++	u32 phy_id;
++	struct mt753x_extphy_id *extphy;
++
++	phy_id = get_cl22_phy_id(gsw, addr);
++	for (i = 0; i < ARRAY_SIZE(extphy_tbl); i++) {
++		extphy = &extphy_tbl[i];
++		if(phy_id_is_match(phy_id, extphy))
++			extphy->init(gsw, addr);
++	}
++
++	return 0;
++}
+diff --git a/drivers/net/phy/mtk/mt753x/mt753x_extphy.h b/drivers/net/phy/mtk/mt753x/mt753x_extphy.h
+new file mode 100644
+index 00000000..2b72c8a9
+--- /dev/null
++++ b/drivers/net/phy/mtk/mt753x/mt753x_extphy.h
+@@ -0,0 +1,18 @@
++/*
++ * Driver for MediaTek MT753x gigabit switch
++ *
++ * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
++ *
++ * Author: Landen Chao <landen.chao@mediatek.com>
++ *
++ * SPDX-License-Identifier:	GPL-2.0+
++ */
++
++#ifndef _MT753X_EXTPHY_H_
++#define _MT753X_EXTPHY_H_
++struct mt753x_extphy_id {
++        u32 phy_id;
++        u32 phy_id_mask;
++	int (*init)(struct gsw_mt753x *gsw, int addr);
++};
++#endif
+-- 
+2.17.1
+