aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mediatek/patches-4.19
diff options
context:
space:
mode:
authorJohn Crispin <john@phrozen.org>2019-08-02 10:33:28 +0200
committerJohn Crispin <john@phrozen.org>2019-08-02 10:36:11 +0200
commit66458c49aa14ebc9ba2e4f9b6a323b8ff122807b (patch)
tree43cd8477d8341136c691d49b139038b14793f635 /target/linux/mediatek/patches-4.19
parentcb49e46a8a4526d86270ced3ba3aa90225ca82d7 (diff)
downloadupstream-66458c49aa14ebc9ba2e4f9b6a323b8ff122807b.tar.gz
upstream-66458c49aa14ebc9ba2e4f9b6a323b8ff122807b.tar.bz2
upstream-66458c49aa14ebc9ba2e4f9b6a323b8ff122807b.zip
mediatek: add v4.19 support
Bump the target to v4.19. Add a patch with additional eth driver fixes/features that MTK provided aswell as the driver for the new mt7530 switch. Signed-off-by: John Crispin <john@phrozen.org>
Diffstat (limited to 'target/linux/mediatek/patches-4.19')
-rwxr-xr-xtarget/linux/mediatek/patches-4.19/0001-arm-dts-mediatek-add-basic-support-for-MT7629-SoC.patch94
-rw-r--r--target/linux/mediatek/patches-4.19/0001-eth-sync-from-mtk-lede.patch1658
-rw-r--r--target/linux/mediatek/patches-4.19/0003-mt7531-gsw-internal_phy_calibration.patch1259
-rw-r--r--target/linux/mediatek/patches-4.19/0003-switch-add-mt7531.patch23
-rw-r--r--target/linux/mediatek/patches-4.19/0227-arm-dts-Add-Unielec-U7623-DTS.patch419
-rwxr-xr-xtarget/linux/mediatek/patches-4.19/0301-mtd-mtk-ecc-move-mtk-ecc-header-file-to-include-mtd.patch48
-rwxr-xr-xtarget/linux/mediatek/patches-4.19/0303-mtd-spinand-disable-on-die-ECC.patch36
-rwxr-xr-xtarget/linux/mediatek/patches-4.19/0304-dt-bindings-ARM-MediaTek-Document-devicetree-binding.patch66
-rw-r--r--target/linux/mediatek/patches-4.19/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch1235
-rwxr-xr-xtarget/linux/mediatek/patches-4.19/0900-bt-mtk-serial-fix.patch37
10 files changed, 4875 insertions, 0 deletions
diff --git a/target/linux/mediatek/patches-4.19/0001-arm-dts-mediatek-add-basic-support-for-MT7629-SoC.patch b/target/linux/mediatek/patches-4.19/0001-arm-dts-mediatek-add-basic-support-for-MT7629-SoC.patch
new file mode 100755
index 0000000000..904f44402a
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0001-arm-dts-mediatek-add-basic-support-for-MT7629-SoC.patch
@@ -0,0 +1,94 @@
+From acb69c6600c3df52f0b3610801f3fd44c4392333 Mon Sep 17 00:00:00 2001
+Message-Id: <acb69c6600c3df52f0b3610801f3fd44c4392333.1559210220.git.ryder.lee@mediatek.com>
+From: Ryder Lee <ryder.lee@mediatek.com>
+Date: Wed, 13 Mar 2019 16:42:15 +0800
+Subject: [PATCH] arm: dts: mediatek: add basic support for MT7629 SoC
+
+This adds basic support for MT7629 reference board.
+
+Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
+---
+ include/dt-bindings/reset/mt7629-resets.h | 71 ++++
+ 4 files changed, 704 insertions(+)
+ create mode 100644 include/dt-bindings/reset/mt7629-resets.h
+
+diff --git a/include/dt-bindings/reset/mt7629-resets.h b/include/dt-bindings/reset/mt7629-resets.h
+new file mode 100644
+index 000000000000..6bb85734f68d
+--- /dev/null
++++ b/include/dt-bindings/reset/mt7629-resets.h
+@@ -0,0 +1,71 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2019 MediaTek Inc.
++ */
++
++#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT7629
++#define _DT_BINDINGS_RESET_CONTROLLER_MT7629
++
++/* INFRACFG resets */
++#define MT7629_INFRA_EMI_MPU_RST 0
++#define MT7629_INFRA_UART5_RST 2
++#define MT7629_INFRA_CIRQ_EINT_RST 3
++#define MT7629_INFRA_APXGPT_RST 4
++#define MT7629_INFRA_SCPSYS_RST 5
++#define MT7629_INFRA_KP_RST 6
++#define MT7629_INFRA_SPI1_RST 7
++#define MT7629_INFRA_SPI4_RST 8
++#define MT7629_INFRA_SYSTIMER_RST 9
++#define MT7629_INFRA_IRRX_RST 10
++#define MT7629_INFRA_AO_BUS_RST 16
++#define MT7629_INFRA_EMI_RST 32
++#define MT7629_INFRA_APMIXED_RST 35
++#define MT7629_INFRA_MIPI_RST 36
++#define MT7629_INFRA_TRNG_RST 37
++#define MT7629_INFRA_SYSCIRQ_RST 38
++#define MT7629_INFRA_MIPI_CSI_RST 39
++#define MT7629_INFRA_GCE_FAXI_RST 40
++#define MT7629_INFRA_I2C_SRAM_RST 41
++#define MT7629_INFRA_IOMMU_RST 47
++
++/* PERICFG resets */
++#define MT7629_PERI_UART0_SW_RST 0
++#define MT7629_PERI_UART1_SW_RST 1
++#define MT7629_PERI_UART2_SW_RST 2
++#define MT7629_PERI_BTIF_SW_RST 6
++#define MT7629_PERI_PWN_SW_RST 8
++#define MT7629_PERI_DMA_SW_RST 11
++#define MT7629_PERI_NFI_SW_RST 14
++#define MT7629_PERI_I2C0_SW_RST 22
++#define MT7629_PERI_SPI0_SW_RST 33
++#define MT7629_PERI_SPI1_SW_RST 34
++#define MT7629_PERI_FLASHIF_SW_RST 36
++
++/* PCIe Subsystem resets */
++#define MT7629_PCIE1_CORE_RST 19
++#define MT7629_PCIE1_MMIO_RST 20
++#define MT7629_PCIE1_HRST 21
++#define MT7629_PCIE1_USER_RST 22
++#define MT7629_PCIE1_PIPE_RST 23
++#define MT7629_PCIE0_CORE_RST 27
++#define MT7629_PCIE0_MMIO_RST 28
++#define MT7629_PCIE0_HRST 29
++#define MT7629_PCIE0_USER_RST 30
++#define MT7629_PCIE0_PIPE_RST 31
++
++/* SSUSB Subsystem resets */
++#define MT7629_SSUSB_PHY_PWR_RST 3
++#define MT7629_SSUSB_MAC_PWR_RST 4
++
++/* ETH Subsystem resets */
++#define MT7629_ETHSYS_SYS_RST 0
++#define MT7629_ETHSYS_MCM_RST 2
++#define MT7629_ETHSYS_HSDMA_RST 5
++#define MT7629_ETHSYS_FE_RST 6
++#define MT7629_ETHSYS_ESW_RST 16
++#define MT7629_ETHSYS_GMAC_RST 23
++#define MT7629_ETHSYS_EPHY_RST 24
++#define MT7629_ETHSYS_CRYPTO_RST 29
++#define MT7629_ETHSYS_PPE_RST 31
++
++#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT7629 */
+--
+2.18.0
+
diff --git a/target/linux/mediatek/patches-4.19/0001-eth-sync-from-mtk-lede.patch b/target/linux/mediatek/patches-4.19/0001-eth-sync-from-mtk-lede.patch
new file mode 100644
index 0000000000..c5521c416b
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0001-eth-sync-from-mtk-lede.patch
@@ -0,0 +1,1658 @@
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/Kconfig
+===================================================================
+--- linux-4.19.57.orig/drivers/net/ethernet/mediatek/Kconfig
++++ linux-4.19.57/drivers/net/ethernet/mediatek/Kconfig
+@@ -1,6 +1,6 @@
+ config NET_VENDOR_MEDIATEK
+ bool "MediaTek ethernet driver"
+- depends on ARCH_MEDIATEK
++ depends on ARCH_MEDIATEK || RALINK
+ ---help---
+ If you have a Mediatek SoC with ethernet, say Y.
+
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/Makefile
+===================================================================
+--- linux-4.19.57.orig/drivers/net/ethernet/mediatek/Makefile
++++ linux-4.19.57/drivers/net/ethernet/mediatek/Makefile
+@@ -2,4 +2,5 @@
+ # Makefile for the Mediatek SoCs built-in ethernet macs
+ #
+
+-obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o
++obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth_soc.o mtk_sgmii.o \
++ mtk_eth_path.o
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_path.c
+===================================================================
+--- /dev/null
++++ linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_path.c
+@@ -0,0 +1,333 @@
++/*
++ * Copyright (C) 2018 MediaTek Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * Copyright (C) 2018 Sean Wang <sean.wang@mediatek.com>
++ */
++
++#include <linux/phy.h>
++#include <linux/regmap.h>
++
++#include "mtk_eth_soc.h"
++
++struct mtk_eth_muxc {
++ int (*set_path)(struct mtk_eth *eth, int path);
++};
++
++static const char * const mtk_eth_mux_name[] = {
++ "mux_gdm1_to_gmac1_esw", "mux_gmac2_gmac0_to_gephy",
++ "mux_u3_gmac2_to_qphy", "mux_gmac1_gmac2_to_sgmii_rgmii",
++ "mux_gmac12_to_gephy_sgmii",
++};
++
++static const char * const mtk_eth_path_name[] = {
++ "gmac1_rgmii", "gmac1_trgmii", "gmac1_sgmii", "gmac2_rgmii",
++ "gmac2_sgmii", "gmac2_gephy", "gdm1_esw",
++};
++
++static int set_mux_gdm1_to_gmac1_esw(struct mtk_eth *eth, int path)
++{
++ u32 val, mask, set;
++ bool updated = true;
++
++ switch (path) {
++ case MTK_ETH_PATH_GMAC1_SGMII:
++ mask = ~(u32)MTK_MUX_TO_ESW;
++ set = 0;
++ break;
++ case MTK_ETH_PATH_GDM1_ESW:
++ mask = ~(u32)MTK_MUX_TO_ESW;
++ set = MTK_MUX_TO_ESW;
++ break;
++ default:
++ updated = false;
++ break;
++ };
++
++ if (updated) {
++ val = mtk_r32(eth, MTK_MAC_MISC);
++ val = (val & mask) | set;
++ mtk_w32(eth, val, MTK_MAC_MISC);
++ }
++
++ dev_info(eth->dev, "path %s in %s updated = %d\n",
++ mtk_eth_path_name[path], __func__, updated);
++
++ return 0;
++}
++
++static int set_mux_gmac2_gmac0_to_gephy(struct mtk_eth *eth, int path)
++{
++ unsigned int val = 0;
++ bool updated = true;
++
++ switch (path) {
++ case MTK_ETH_PATH_GMAC2_GEPHY:
++ val = ~(u32)GEPHY_MAC_SEL;
++ break;
++ default:
++ updated = false;
++ break;
++ }
++
++ if (updated)
++ regmap_update_bits(eth->infra, INFRA_MISC2, GEPHY_MAC_SEL, val);
++
++ dev_info(eth->dev, "path %s in %s updated = %d\n",
++ mtk_eth_path_name[path], __func__, updated);
++
++ return 0;
++}
++
++static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path)
++{
++ unsigned int val = 0;
++ bool updated = true;
++
++ switch (path) {
++ case MTK_ETH_PATH_GMAC2_SGMII:
++ val = CO_QPHY_SEL;
++ break;
++ default:
++ updated = false;
++ break;
++ }
++
++ if (updated)
++ regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val);
++
++ dev_info(eth->dev, "path %s in %s updated = %d\n",
++ mtk_eth_path_name[path], __func__, updated);
++
++ return 0;
++}
++
++static int set_mux_gmac1_gmac2_to_sgmii_rgmii(struct mtk_eth *eth, int path)
++{
++ unsigned int val = 0;
++ bool updated = true;
++
++ switch (path) {
++ case MTK_ETH_PATH_GMAC1_SGMII:
++ val = SYSCFG0_SGMII_GMAC1;
++ break;
++ case MTK_ETH_PATH_GMAC2_SGMII:
++ val = SYSCFG0_SGMII_GMAC2;
++ break;
++ case MTK_ETH_PATH_GMAC1_RGMII:
++ case MTK_ETH_PATH_GMAC2_RGMII:
++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
++ val &= SYSCFG0_SGMII_MASK;
++
++ if ((path == MTK_GMAC1_RGMII && val == SYSCFG0_SGMII_GMAC1) ||
++ (path == MTK_GMAC2_RGMII && val == SYSCFG0_SGMII_GMAC2))
++ val = 0;
++ else
++ updated = false;
++ break;
++ default:
++ updated = false;
++ break;
++ };
++
++ if (updated)
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, val);
++
++ dev_info(eth->dev, "path %s in %s updated = %d\n",
++ mtk_eth_path_name[path], __func__, updated);
++
++ return 0;
++}
++
++static int set_mux_gmac12_to_gephy_sgmii(struct mtk_eth *eth, int path)
++{
++ unsigned int val = 0;
++ bool updated = true;
++
++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
++
++ switch (path) {
++ case MTK_ETH_PATH_GMAC1_SGMII:
++ val |= SYSCFG0_SGMII_GMAC1_V2;
++ break;
++ case MTK_ETH_PATH_GMAC2_GEPHY:
++ val &= ~(u32)SYSCFG0_SGMII_GMAC2_V2;
++ break;
++ case MTK_ETH_PATH_GMAC2_SGMII:
++ val |= SYSCFG0_SGMII_GMAC2_V2;
++ break;
++ default:
++ updated = false;
++ };
++
++ if (updated)
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, val);
++
++ if (!updated)
++ dev_info(eth->dev, "path %s no needs updatiion in %s\n",
++ mtk_eth_path_name[path], __func__);
++
++ dev_info(eth->dev, "path %s in %s updated = %d\n",
++ mtk_eth_path_name[path], __func__, updated);
++
++ return 0;
++}
++
++static const struct mtk_eth_muxc mtk_eth_muxc[] = {
++ { .set_path = set_mux_gdm1_to_gmac1_esw, },
++ { .set_path = set_mux_gmac2_gmac0_to_gephy, },
++ { .set_path = set_mux_u3_gmac2_to_qphy, },
++ { .set_path = set_mux_gmac1_gmac2_to_sgmii_rgmii, },
++ { .set_path = set_mux_gmac12_to_gephy_sgmii, }
++};
++
++static int mtk_eth_mux_setup(struct mtk_eth *eth, int path)
++{
++ int i, err = 0;
++
++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_PATH_BIT(path))) {
++ dev_info(eth->dev, "path %s isn't support on the SoC\n",
++ mtk_eth_path_name[path]);
++ return -EINVAL;
++ }
++
++ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_MUX))
++ return 0;
++
++ /* Setup MUX in path fabric */
++ for (i = 0; i < MTK_ETH_MUX_MAX; i++) {
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_MUX_BIT(i))) {
++ err = mtk_eth_muxc[i].set_path(eth, path);
++ if (err)
++ goto out;
++ } else {
++ dev_info(eth->dev, "mux %s isn't present on the SoC\n",
++ mtk_eth_mux_name[i]);
++ }
++ }
++
++out:
++ return err;
++}
++
++static int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id)
++{
++ unsigned int val = 0;
++ int sid, err, path;
++
++ path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_SGMII :
++ MTK_ETH_PATH_GMAC2_SGMII;
++
++ /* Setup proper MUXes along the path */
++ err = mtk_eth_mux_setup(eth, path);
++ if (err)
++ return err;
++
++ /* The path GMAC to SGMII will be enabled once the SGMIISYS is being
++ * setup done.
++ */
++ regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
++
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, ~(u32)SYSCFG0_SGMII_MASK);
++
++ /* Decide how GMAC and SGMIISYS be mapped */
++ sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? 0 : mac_id;
++
++ /* Setup SGMIISYS with the determined property */
++ if (MTK_HAS_FLAGS(eth->sgmii->flags[sid], MTK_SGMII_PHYSPEED_AN))
++ err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
++ else
++ err = mtk_sgmii_setup_mode_force(eth->sgmii, sid);
++
++ if (err)
++ return err;
++
++ regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
++ SYSCFG0_SGMII_MASK, val);
++
++ return 0;
++}
++
++static int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id)
++{
++ int err, path = 0;
++
++ if (mac_id == 1)
++ path = MTK_ETH_PATH_GMAC2_GEPHY;
++
++ if (!path)
++ return -EINVAL;
++
++ /* Setup proper MUXes along the path */
++ err = mtk_eth_mux_setup(eth, path);
++ if (err)
++ return err;
++
++ return 0;
++}
++
++static int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id)
++{
++ int err, path;
++
++ path = (mac_id == 0) ? MTK_ETH_PATH_GMAC1_RGMII :
++ MTK_ETH_PATH_GMAC2_RGMII;
++
++ /* Setup proper MUXes along the path */
++ err = mtk_eth_mux_setup(eth, path);
++ if (err)
++ return err;
++
++ return 0;
++}
++
++int mtk_setup_hw_path(struct mtk_eth *eth, int mac_id, int phymode)
++{
++ int err;
++
++ switch (phymode) {
++ case PHY_INTERFACE_MODE_TRGMII:
++ case PHY_INTERFACE_MODE_RGMII_TXID:
++ case PHY_INTERFACE_MODE_RGMII_RXID:
++ case PHY_INTERFACE_MODE_RGMII_ID:
++ case PHY_INTERFACE_MODE_RGMII:
++ case PHY_INTERFACE_MODE_MII:
++ case PHY_INTERFACE_MODE_REVMII:
++ case PHY_INTERFACE_MODE_RMII:
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_RGMII)) {
++ err = mtk_gmac_rgmii_path_setup(eth, mac_id);
++ if (err)
++ return err;
++ }
++ break;
++ case PHY_INTERFACE_MODE_SGMII:
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
++ err = mtk_gmac_sgmii_path_setup(eth, mac_id);
++ if (err)
++ return err;
++ }
++ break;
++ case PHY_INTERFACE_MODE_GMII:
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_GEPHY)) {
++ err = mtk_gmac_gephy_path_setup(eth, mac_id);
++ if (err)
++ return err;
++ }
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+===================================================================
+--- linux-4.19.57.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -23,6 +23,7 @@
+ #include <linux/reset.h>
+ #include <linux/tcp.h>
+ #include <linux/interrupt.h>
++#include <linux/mdio.h>
+ #include <linux/pinctrl/devinfo.h>
+
+ #include "mtk_eth_soc.h"
+@@ -54,8 +55,10 @@ static const struct mtk_ethtool_stats {
+ };
+
+ static const char * const mtk_clks_source_name[] = {
+- "ethif", "esw", "gp0", "gp1", "gp2", "trgpll", "sgmii_tx250m",
+- "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii_ck", "eth2pll"
++ "ethif", "sgmiitop", "esw", "gp0", "gp1", "gp2", "fe", "trgpll",
++ "sgmii_tx250m", "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb",
++ "sgmii2_tx250m", "sgmii2_rx250m", "sgmii2_cdr_ref", "sgmii2_cdr_fb",
++ "sgmii_ck", "eth2pll",
+ };
+
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+@@ -84,8 +87,8 @@ static int mtk_mdio_busy_wait(struct mtk
+ return -1;
+ }
+
+-static u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
+- u32 phy_register, u32 write_data)
++u32 _mtk_mdio_write(struct mtk_eth *eth, u32 phy_addr,
++ u32 phy_register, u32 write_data)
+ {
+ if (mtk_mdio_busy_wait(eth))
+ return -1;
+@@ -103,7 +106,7 @@ static u32 _mtk_mdio_write(struct mtk_et
+ return 0;
+ }
+
+-static u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
++u32 _mtk_mdio_read(struct mtk_eth *eth, int phy_addr, int phy_reg)
+ {
+ u32 d;
+
+@@ -123,6 +126,34 @@ static u32 _mtk_mdio_read(struct mtk_eth
+ return d;
+ }
+
++u32 mtk_cl45_ind_read(struct mtk_eth *eth, u32 port, u32 devad, u32 reg, u32 *data)
++{
++ mutex_lock(&eth->mii_bus->mdio_lock);
++
++ _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, devad);
++ _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, reg);
++ _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
++ *data = _mtk_mdio_read(eth, port, MII_MMD_ADDR_DATA_REG);
++
++ mutex_unlock(&eth->mii_bus->mdio_lock);
++
++ return 0;
++}
++
++u32 mtk_cl45_ind_write(struct mtk_eth *eth, u32 port, u32 devad, u32 reg, u32 data)
++{
++ mutex_lock(&eth->mii_bus->mdio_lock);
++
++ _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, devad);
++ _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, reg);
++ _mtk_mdio_write(eth, port, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
++ _mtk_mdio_write(eth, port, MII_MMD_ADDR_DATA_REG, data);
++
++ mutex_unlock(&eth->mii_bus->mdio_lock);
++
++ return 0;
++}
++
+ static int mtk_mdio_write(struct mii_bus *bus, int phy_addr,
+ int phy_reg, u16 val)
+ {
+@@ -165,51 +196,12 @@ static void mtk_gmac0_rgmii_adjust(struc
+ mtk_w32(eth, val, TRGMII_TCK_CTRL);
+ }
+
+-static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id)
+-{
+- u32 val;
+-
+- /* Setup the link timer and QPHY power up inside SGMIISYS */
+- regmap_write(eth->sgmiisys, SGMSYS_PCS_LINK_TIMER,
+- SGMII_LINK_TIMER_DEFAULT);
+-
+- regmap_read(eth->sgmiisys, SGMSYS_SGMII_MODE, &val);
+- val |= SGMII_REMOTE_FAULT_DIS;
+- regmap_write(eth->sgmiisys, SGMSYS_SGMII_MODE, val);
+-
+- regmap_read(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, &val);
+- val |= SGMII_AN_RESTART;
+- regmap_write(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, val);
+-
+- regmap_read(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+- val &= ~SGMII_PHYA_PWD;
+- regmap_write(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, val);
+-
+- /* Determine MUX for which GMAC uses the SGMII interface */
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_DUAL_GMAC_SHARED_SGMII)) {
+- regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val);
+- val &= ~SYSCFG0_SGMII_MASK;
+- val |= !mac_id ? SYSCFG0_SGMII_GMAC1 : SYSCFG0_SGMII_GMAC2;
+- regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val);
+-
+- dev_info(eth->dev, "setup shared sgmii for gmac=%d\n",
+- mac_id);
+- }
+-
+- /* Setup the GMAC1 going through SGMII path when SoC also support
+- * ESW on GMAC1
+- */
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_ESW | MTK_GMAC1_SGMII) &&
+- !mac_id) {
+- mtk_w32(eth, 0, MTK_MAC_MISC);
+- dev_info(eth->dev, "setup gmac1 going through sgmii");
+- }
+-}
+-
+ static void mtk_phy_link_adjust(struct net_device *dev)
+ {
+ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_eth *eth = mac->hw;
+ u16 lcl_adv = 0, rmt_adv = 0;
++ u32 lcl_eee = 0, rmt_eee = 0;
+ u8 flowctrl;
+ u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
+@@ -229,7 +221,7 @@ static void mtk_phy_link_adjust(struct n
+ };
+
+ if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII) &&
+- !mac->id && !mac->trgmii)
++ !mac->id && !mac->trgmii)
+ mtk_gmac0_rgmii_adjust(mac->hw, dev->phydev->speed);
+
+ if (dev->phydev->link)
+@@ -259,7 +251,16 @@ static void mtk_phy_link_adjust(struct n
+ flowctrl & FLOW_CTRL_RX ? "enabled" : "disabled",
+ flowctrl & FLOW_CTRL_TX ? "enabled" : "disabled");
+ }
++ /*EEE capability*/
++ mtk_cl45_ind_read(eth, 0, MDIO_MMD_AN, MDIO_AN_EEE_ADV, &lcl_eee);
++ mtk_cl45_ind_read(eth, 0, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE, &rmt_eee);
++
++ if ((lcl_eee & rmt_eee & MDIO_EEE_1000T) == MDIO_EEE_1000T)
++ mcr |= MAC_MCR_MDIO_EEE_1000T;
++ if ((lcl_eee & rmt_eee & MDIO_EEE_100TX) == MDIO_EEE_100TX)
++ mcr |= MAC_MCR_MDIO_EEE_100TX;
+
++ /*Setup MCR*/
+ mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
+
+ if (dev->phydev->link)
+@@ -290,10 +291,10 @@ static int mtk_phy_connect_node(struct m
+ return -ENODEV;
+ }
+
+- dev_info(eth->dev,
+- "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
+- mac->id, phydev_name(phydev), phydev->phy_id,
+- phydev->drv->name);
++ dev_info(eth->dev,
++ "connected mac %d to PHY at %s [uid=%08x, driver=%s]\n",
++ mac->id, phydev_name(phydev), phydev->phy_id,
++ phydev->drv->name);
+
+ return 0;
+ }
+@@ -304,6 +305,7 @@ static int mtk_phy_connect(struct net_de
+ struct mtk_eth *eth;
+ struct device_node *np;
+ u32 val;
++ int err;
+
+ eth = mac->hw;
+ np = of_parse_phandle(mac->of_node, "phy-handle", 0);
+@@ -313,6 +315,10 @@ static int mtk_phy_connect(struct net_de
+ if (!np)
+ return -ENODEV;
+
++ err = mtk_setup_hw_path(eth, mac->id, of_get_phy_mode(np));
++ if (err)
++ goto err_phy;
++
+ mac->ge_mode = 0;
+ switch (of_get_phy_mode(np)) {
+ case PHY_INTERFACE_MODE_TRGMII:
+@@ -323,10 +329,9 @@ static int mtk_phy_connect(struct net_de
+ case PHY_INTERFACE_MODE_RGMII:
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII))
+- mtk_gmac_sgmii_hw_setup(eth, mac->id);
+ break;
+ case PHY_INTERFACE_MODE_MII:
++ case PHY_INTERFACE_MODE_GMII:
+ mac->ge_mode = 1;
+ break;
+ case PHY_INTERFACE_MODE_REVMII:
+@@ -355,7 +360,7 @@ static int mtk_phy_connect(struct net_de
+ dev->phydev->speed = 0;
+ dev->phydev->duplex = 0;
+
+- if (of_phy_is_fixed_link(mac->of_node))
++ if (!strncmp(dev->phydev->drv->name, "Generic", 7))
+ dev->phydev->supported |=
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+@@ -535,37 +540,37 @@ static void mtk_stats_update(struct mtk_
+ }
+
+ static void mtk_get_stats64(struct net_device *dev,
+- struct rtnl_link_stats64 *storage)
++ struct rtnl_link_stats64 *storage)
+ {
+- struct mtk_mac *mac = netdev_priv(dev);
+- struct mtk_hw_stats *hw_stats = mac->hw_stats;
+- unsigned int start;
+-
+- if (netif_running(dev) && netif_device_present(dev)) {
+- if (spin_trylock_bh(&hw_stats->stats_lock)) {
+- mtk_stats_update_mac(mac);
+- spin_unlock_bh(&hw_stats->stats_lock);
+- }
+- }
+-
+- do {
+- start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
+- storage->rx_packets = hw_stats->rx_packets;
+- storage->tx_packets = hw_stats->tx_packets;
+- storage->rx_bytes = hw_stats->rx_bytes;
+- storage->tx_bytes = hw_stats->tx_bytes;
+- storage->collisions = hw_stats->tx_collisions;
+- storage->rx_length_errors = hw_stats->rx_short_errors +
+- hw_stats->rx_long_errors;
+- storage->rx_over_errors = hw_stats->rx_overflow;
+- storage->rx_crc_errors = hw_stats->rx_fcs_errors;
+- storage->rx_errors = hw_stats->rx_checksum_errors;
+- storage->tx_aborted_errors = hw_stats->tx_skip;
+- } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
+-
+- storage->tx_errors = dev->stats.tx_errors;
+- storage->rx_dropped = dev->stats.rx_dropped;
+- storage->tx_dropped = dev->stats.tx_dropped;
++ struct mtk_mac *mac = netdev_priv(dev);
++ struct mtk_hw_stats *hw_stats = mac->hw_stats;
++ unsigned int start;
++
++ if (netif_running(dev) && netif_device_present(dev)) {
++ if (spin_trylock_bh(&hw_stats->stats_lock)) {
++ mtk_stats_update_mac(mac);
++ spin_unlock_bh(&hw_stats->stats_lock);
++ }
++ }
++
++ do {
++ start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
++ storage->rx_packets = hw_stats->rx_packets;
++ storage->tx_packets = hw_stats->tx_packets;
++ storage->rx_bytes = hw_stats->rx_bytes;
++ storage->tx_bytes = hw_stats->tx_bytes;
++ storage->collisions = hw_stats->tx_collisions;
++ storage->rx_length_errors = hw_stats->rx_short_errors +
++ hw_stats->rx_long_errors;
++ storage->rx_over_errors = hw_stats->rx_overflow;
++ storage->rx_crc_errors = hw_stats->rx_fcs_errors;
++ storage->rx_errors = hw_stats->rx_checksum_errors;
++ storage->tx_aborted_errors = hw_stats->tx_skip;
++ } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
++
++ storage->tx_errors = dev->stats.tx_errors;
++ storage->rx_dropped = dev->stats.rx_dropped;
++ storage->tx_dropped = dev->stats.tx_dropped;
+ }
+
+ static inline int mtk_max_frag_size(int mtu)
+@@ -605,10 +610,10 @@ static int mtk_init_fq_dma(struct mtk_et
+ dma_addr_t dma_addr;
+ int i;
+
+- eth->scratch_ring = dma_zalloc_coherent(eth->dev,
+- cnt * sizeof(struct mtk_tx_dma),
+- &eth->phy_scratch_ring,
+- GFP_ATOMIC);
++ eth->scratch_ring = dma_alloc_coherent(eth->dev,
++ cnt * sizeof(struct mtk_tx_dma),
++ &eth->phy_scratch_ring,
++ GFP_ATOMIC | __GFP_ZERO);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+@@ -623,6 +628,7 @@ static int mtk_init_fq_dma(struct mtk_et
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ return -ENOMEM;
+
++ memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
+ phy_ring_tail = eth->phy_scratch_ring +
+ (sizeof(struct mtk_tx_dma) * (cnt - 1));
+
+@@ -673,7 +679,7 @@ static void mtk_tx_unmap(struct mtk_eth
+ }
+ tx_buf->flags = 0;
+ if (tx_buf->skb &&
+- (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC))
++ (tx_buf->skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC))
+ dev_kfree_skb_any(tx_buf->skb);
+ tx_buf->skb = NULL;
+ }
+@@ -689,6 +695,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ unsigned int nr_frags;
+ int i, n_desc = 1;
+ u32 txd4 = 0, fport;
++ u32 qid = 0;
+
+ itxd = ring->next_free;
+ if (itxd == ring->last_free)
+@@ -708,9 +715,10 @@ static int mtk_tx_map(struct sk_buff *sk
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd4 |= TX_DMA_CHKSUM;
+
+- /* VLAN header offload */
+- if (skb_vlan_tag_present(skb))
+- txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb);
++#if defined(CONFIG_NET_MEDIATEK_HW_QOS)
++ qid = skb->mark & (MTK_QDMA_TX_MASK);
++ qid += (!mac->id) ? (MTK_QDMA_TX_MASK + 1) : 0;
++#endif
+
+ mapped_addr = dma_map_single(eth->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+@@ -727,6 +735,7 @@ static int mtk_tx_map(struct sk_buff *sk
+ /* TX SG offload */
+ txd = itxd;
+ nr_frags = skb_shinfo(skb)->nr_frags;
++
+ for (i = 0; i < nr_frags; i++) {
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int offset = 0;
+@@ -753,10 +762,10 @@ static int mtk_tx_map(struct sk_buff *sk
+ last_frag = true;
+
+ WRITE_ONCE(txd->txd1, mapped_addr);
+- WRITE_ONCE(txd->txd3, (TX_DMA_SWC |
++ WRITE_ONCE(txd->txd3, (TX_DMA_SWC | QID_LOW_BITS(qid) |
+ TX_DMA_PLEN0(frag_map_size) |
+ last_frag * TX_DMA_LS0));
+- WRITE_ONCE(txd->txd4, fport);
++ WRITE_ONCE(txd->txd4, fport | QID_HIGH_BITS(qid));
+
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+@@ -775,9 +784,9 @@ static int mtk_tx_map(struct sk_buff *sk
+ /* store skb to cleanup */
+ itx_buf->skb = skb;
+
+- WRITE_ONCE(itxd->txd4, txd4);
+ WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+- (!nr_frags * TX_DMA_LS0)));
++ (!nr_frags * TX_DMA_LS0)) | QID_LOW_BITS(qid));
++ WRITE_ONCE(itxd->txd4, txd4 | QID_HIGH_BITS(qid));
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+@@ -922,7 +931,7 @@ drop:
+ return NETDEV_TX_OK;
+ }
+
+-static struct mtk_rx_ring *mtk_get_rx_ring(struct mtk_eth *eth)
++struct mtk_rx_ring *mtk_get_rx_ring(struct mtk_eth *eth)
+ {
+ int i;
+ struct mtk_rx_ring *ring;
+@@ -991,10 +1000,24 @@ static int mtk_poll_rx(struct napi_struc
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
++#if defined(CONFIG_NET_DSA)
++ mac = (trxd.rxd4 >> 22) & 0x1;
++ mac = (mac + 1) % 2;
++#else
+ mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+- RX_DMA_FPORT_MASK;
+- mac--;
+-
++ RX_DMA_FPORT_MASK;
++ /* From QDMA(5). This is a external interface case of HWNAT.
++ * When the incoming frame comes from an external interface
++ * rather than GMAC1/GMAC2, HWNAT driver sends the original
++ * frame to PPE via PPD(ping pong device) for HWNAT RX
++ * frame learning. After learning, PPE transmit the
++ * original frame back to PPD again to run SW NAT path.
++ */
++ if (mac == 5)
++ mac = 0;
++ else
++ mac--;
++#endif
+ if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT ||
+ !eth->netdev[mac]))
+ goto release_desc;
+@@ -1044,6 +1067,7 @@ static int mtk_poll_rx(struct napi_struc
+ RX_DMA_VID(trxd.rxd3))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ RX_DMA_VID(trxd.rxd3));
++
+ skb_record_rx_queue(skb, 0);
+ napi_gro_receive(napi, skb);
+
+@@ -1128,7 +1152,7 @@ static int mtk_poll_tx(struct mtk_eth *e
+ }
+
+ if (mtk_queue_stopped(eth) &&
+- (atomic_read(&ring->free_count) > ring->thresh))
++ (atomic_read(&ring->free_count) > ring->thresh))
+ mtk_wake_queue(eth);
+
+ return total;
+@@ -1220,11 +1244,14 @@ static int mtk_tx_alloc(struct mtk_eth *
+ if (!ring->buf)
+ goto no_tx_mem;
+
+- ring->dma = dma_zalloc_coherent(eth->dev, MTK_DMA_SIZE * sz,
+- &ring->phys, GFP_ATOMIC);
++ ring->dma = dma_alloc_coherent(eth->dev,
++ MTK_DMA_SIZE * sz,
++ &ring->phys,
++ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->dma)
+ goto no_tx_mem;
+
++ memset(ring->dma, 0, MTK_DMA_SIZE * sz);
+ for (i = 0; i < MTK_DMA_SIZE; i++) {
+ int next = (i + 1) % MTK_DMA_SIZE;
+ u32 next_ptr = ring->phys + next * sz;
+@@ -1317,9 +1344,10 @@ static int mtk_rx_alloc(struct mtk_eth *
+ return -ENOMEM;
+ }
+
+- ring->dma = dma_zalloc_coherent(eth->dev,
+- rx_dma_size * sizeof(*ring->dma),
+- &ring->phys, GFP_ATOMIC);
++ ring->dma = dma_alloc_coherent(eth->dev,
++ rx_dma_size * sizeof(*ring->dma),
++ &ring->phys,
++ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->dma)
+ return -ENOMEM;
+
+@@ -1516,8 +1544,8 @@ static int mtk_hwlro_add_ipaddr(struct n
+ int hwlro_idx;
+
+ if ((fsp->flow_type != TCP_V4_FLOW) ||
+- (!fsp->h_u.tcp_ip4_spec.ip4dst) ||
+- (fsp->location > 1))
++ (!fsp->h_u.tcp_ip4_spec.ip4dst) ||
++ (fsp->location > 1))
+ return -EINVAL;
+
+ mac->hwlro_ip[fsp->location] = htonl(fsp->h_u.tcp_ip4_spec.ip4dst);
+@@ -1744,6 +1772,34 @@ static void mtk_tx_timeout(struct net_de
+ schedule_work(&eth->pending_work);
+ }
+
++static irqreturn_t mtk_handle_irq_tx_rx(int irq, void *_eth)
++{
++ struct mtk_eth *eth = _eth;
++ u32 tx_status, rx_status;
++
++ tx_status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
++
++ if (tx_status & MTK_TX_DONE_INT) {
++ if (likely(napi_schedule_prep(&eth->tx_napi))) {
++ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
++ __napi_schedule(&eth->tx_napi);
++ }
++ mtk_w32(eth, tx_status, MTK_QMTK_INT_STATUS);
++ }
++
++ rx_status = mtk_r32(eth, MTK_PDMA_INT_STATUS);
++
++ if (rx_status & MTK_RX_DONE_INT) {
++ if (likely(napi_schedule_prep(&eth->rx_napi))) {
++ mtk_rx_irq_disable(eth, MTK_RX_DONE_INT);
++ __napi_schedule(&eth->rx_napi);
++ }
++ mtk_w32(eth, rx_status, MTK_PDMA_INT_STATUS);
++ }
++
++ return IRQ_HANDLED;
++}
++
+ static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
+ {
+ struct mtk_eth *eth = _eth;
+@@ -1784,8 +1840,8 @@ static void mtk_poll_controller(struct n
+
+ static int mtk_start_dma(struct mtk_eth *eth)
+ {
+- u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
+ int err;
++ u32 rx_2b_offet = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
+
+ err = mtk_dma_init(eth);
+ if (err) {
+@@ -1801,7 +1857,7 @@ static int mtk_start_dma(struct mtk_eth
+ MTK_QDMA_GLO_CFG);
+
+ mtk_w32(eth,
+- MTK_RX_DMA_EN | rx_2b_offset |
++ MTK_RX_DMA_EN | rx_2b_offet |
+ MTK_RX_BT_32DWORDS | MTK_MULTI_EN,
+ MTK_PDMA_GLO_CFG);
+
+@@ -1814,7 +1870,7 @@ static int mtk_open(struct net_device *d
+ struct mtk_eth *eth = mac->hw;
+
+ /* we run 2 netdevs on the same dma ring so we only bring it up once */
+- if (!refcount_read(&eth->dma_refcnt)) {
++ if (!atomic_read(&eth->dma_refcnt)) {
+ int err = mtk_start_dma(eth);
+
+ if (err)
+@@ -1824,10 +1880,8 @@ static int mtk_open(struct net_device *d
+ napi_enable(&eth->rx_napi);
+ mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
+ mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
+- refcount_set(&eth->dma_refcnt, 1);
+ }
+- else
+- refcount_inc(&eth->dma_refcnt);
++ atomic_inc(&eth->dma_refcnt);
+
+ phy_start(dev->phydev);
+ netif_start_queue(dev);
+@@ -1867,7 +1921,7 @@ static int mtk_stop(struct net_device *d
+ phy_stop(dev->phydev);
+
+ /* only shutdown DMA if this is the last user */
+- if (!refcount_dec_and_test(&eth->dma_refcnt))
++ if (!atomic_dec_and_test(&eth->dma_refcnt))
+ return 0;
+
+ mtk_tx_irq_disable(eth, MTK_TX_DONE_INT);
+@@ -1973,14 +2027,16 @@ static int mtk_hw_init(struct mtk_eth *e
+ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL);
+ mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL);
+
+- /* Enable RX VLan Offloading */
+- mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
++ /* Disable RX VLan Offloading */
++ mtk_w32(eth, 0, MTK_CDMP_EG_CTRL);
++
++#if defined(CONFIG_NET_DSA)
++ mtk_w32(eth, 0x81000001, MTK_CDMP_IG_CTRL);
++#endif
+
+- /* enable interrupt delay for RX */
+- mtk_w32(eth, MTK_PDMA_DELAY_RX_DELAY, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, 0x8f0f8f0f, MTK_PDMA_DELAY_INT);
++ mtk_w32(eth, 0x8f0f8f0f, MTK_QDMA_DELAY_INT);
+
+- /* disable delay and normal interrupt */
+- mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_tx_irq_disable(eth, ~0);
+ mtk_rx_irq_disable(eth, ~0);
+ mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
+@@ -2172,27 +2228,27 @@ static int mtk_cleanup(struct mtk_eth *e
+ }
+
+ static int mtk_get_link_ksettings(struct net_device *ndev,
+- struct ethtool_link_ksettings *cmd)
++ struct ethtool_link_ksettings *cmd)
+ {
+- struct mtk_mac *mac = netdev_priv(ndev);
++ struct mtk_mac *mac = netdev_priv(ndev);
+
+- if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
+- return -EBUSY;
++ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
++ return -EBUSY;
+
+- phy_ethtool_ksettings_get(ndev->phydev, cmd);
++ phy_ethtool_ksettings_get(ndev->phydev, cmd);
+
+- return 0;
++ return 0;
+ }
+
+ static int mtk_set_link_ksettings(struct net_device *ndev,
+- const struct ethtool_link_ksettings *cmd)
++ const struct ethtool_link_ksettings *cmd)
+ {
+- struct mtk_mac *mac = netdev_priv(ndev);
++ struct mtk_mac *mac = netdev_priv(ndev);
+
+- if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
+- return -EBUSY;
++ if (unlikely(test_bit(MTK_RESETTING, &mac->hw->state)))
++ return -EBUSY;
+
+- return phy_ethtool_ksettings_set(ndev->phydev, cmd);
++ return phy_ethtool_ksettings_set(ndev->phydev, cmd);
+ }
+
+ static void mtk_get_drvinfo(struct net_device *dev,
+@@ -2355,8 +2411,8 @@ static int mtk_set_rxnfc(struct net_devi
+ }
+
+ static const struct ethtool_ops mtk_ethtool_ops = {
+- .get_link_ksettings = mtk_get_link_ksettings,
+- .set_link_ksettings = mtk_set_link_ksettings,
++ .get_link_ksettings = mtk_get_link_ksettings,
++ .set_link_ksettings = mtk_set_link_ksettings,
+ .get_drvinfo = mtk_get_drvinfo,
+ .get_msglevel = mtk_get_msglevel,
+ .set_msglevel = mtk_set_msglevel,
+@@ -2366,7 +2422,7 @@ static const struct ethtool_ops mtk_etht
+ .get_sset_count = mtk_get_sset_count,
+ .get_ethtool_stats = mtk_get_ethtool_stats,
+ .get_rxnfc = mtk_get_rxnfc,
+- .set_rxnfc = mtk_set_rxnfc,
++ .set_rxnfc = mtk_set_rxnfc,
+ };
+
+ static const struct net_device_ops mtk_netdev_ops = {
+@@ -2463,6 +2519,7 @@ static int mtk_probe(struct platform_dev
+ {
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct device_node *mac_np;
++ const struct of_device_id *match;
+ struct mtk_eth *eth;
+ int err;
+ int i;
+@@ -2471,7 +2528,8 @@ static int mtk_probe(struct platform_dev
+ if (!eth)
+ return -ENOMEM;
+
+- eth->soc = of_device_get_match_data(&pdev->dev);
++ match = of_match_device(of_mtk_match, &pdev->dev);
++ eth->soc = (struct mtk_soc_data *)match->data;
+
+ eth->dev = &pdev->dev;
+ eth->base = devm_ioremap_resource(&pdev->dev, res);
+@@ -2489,26 +2547,37 @@ static int mtk_probe(struct platform_dev
+ return PTR_ERR(eth->ethsys);
+ }
+
+- if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
+- eth->sgmiisys =
+- syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+- "mediatek,sgmiisys");
+- if (IS_ERR(eth->sgmiisys)) {
+- dev_err(&pdev->dev, "no sgmiisys regmap found\n");
+- return PTR_ERR(eth->sgmiisys);
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_INFRA)) {
++ eth->infra = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
++ "mediatek,infracfg");
++ if (IS_ERR(eth->infra)) {
++ dev_info(&pdev->dev, "no ethsys regmap found\n");
++ return PTR_ERR(eth->infra);
+ }
+ }
+
++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) {
++ eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii),
++ GFP_KERNEL);
++ if (!eth->sgmii)
++ return -ENOMEM;
++
++ err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node,
++ eth->soc->ana_rgc3);
++ if (err)
++ return err;
++ }
++
+ if (eth->soc->required_pctl) {
+ eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,pctl");
+ if (IS_ERR(eth->pctl)) {
+- dev_err(&pdev->dev, "no pctl regmap found\n");
++ dev_info(&pdev->dev, "no pctl regmap found\n");
+ return PTR_ERR(eth->pctl);
+ }
+ }
+
+- for (i = 0; i < 3; i++) {
++ for (i = 0; i < eth->soc->irq_num; i++) {
+ eth->irq[i] = platform_get_irq(pdev, i);
+ if (eth->irq[i] < 0) {
+ dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
+@@ -2552,15 +2621,22 @@ static int mtk_probe(struct platform_dev
+ goto err_deinit_hw;
+ }
+
+- err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
+- dev_name(eth->dev), eth);
+- if (err)
+- goto err_free_dev;
++ if (eth->soc->irq_num > 1) {
++ err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
++ dev_name(eth->dev), eth);
++ if (err)
++ goto err_free_dev;
+
+- err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
+- dev_name(eth->dev), eth);
+- if (err)
+- goto err_free_dev;
++ err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
++ dev_name(eth->dev), eth);
++ if (err)
++ goto err_free_dev;
++ } else {
++ err = devm_request_irq(eth->dev, eth->irq[0], mtk_handle_irq_tx_rx, 0,
++ dev_name(eth->dev), eth);
++ if (err)
++ goto err_free_dev;
++ }
+
+ err = mtk_mdio_init(eth);
+ if (err)
+@@ -2626,27 +2702,48 @@ static int mtk_remove(struct platform_de
+ }
+
+ static const struct mtk_soc_data mt2701_data = {
+- .caps = MTK_GMAC1_TRGMII | MTK_HWLRO,
++ .caps = MT7623_CAPS | MTK_HWLRO,
+ .required_clks = MT7623_CLKS_BITMAP,
+ .required_pctl = true,
++ .irq_num = 3,
+ };
+
+ static const struct mtk_soc_data mt7622_data = {
+- .caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW | MTK_HWLRO,
++ .ana_rgc3 = 0x2028,
++ .caps = MT7622_CAPS | MTK_HWLRO,
+ .required_clks = MT7622_CLKS_BITMAP,
+ .required_pctl = false,
++ .irq_num = 3,
+ };
+
+ static const struct mtk_soc_data mt7623_data = {
+- .caps = MTK_GMAC1_TRGMII | MTK_HWLRO,
++ .caps = MT7623_CAPS | MTK_HWLRO,
+ .required_clks = MT7623_CLKS_BITMAP,
+ .required_pctl = true,
++ .irq_num = 3,
++};
++
++static const struct mtk_soc_data leopard_data = {
++ .ana_rgc3 = 0x128,
++ .caps = LEOPARD_CAPS | MTK_HWLRO,
++ .required_clks = LEOPARD_CLKS_BITMAP,
++ .required_pctl = false,
++ .irq_num = 3,
++};
++
++static const struct mtk_soc_data mt7621_data = {
++ .caps = MT7621_CAPS,
++ .required_clks = MT7621_CLKS_BITMAP,
++ .required_pctl = false,
++ .irq_num = 1,
+ };
+
+ const struct of_device_id of_mtk_match[] = {
+ { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data},
+ { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data},
+ { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data},
++ { .compatible = "mediatek,mt7629-eth", .data = &leopard_data},
++ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data},
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, of_mtk_match);
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+===================================================================
+--- linux-4.19.57.orig/drivers/net/ethernet/mediatek/mtk_eth_soc.h
++++ linux-4.19.57/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+@@ -15,13 +15,17 @@
+ #ifndef MTK_ETH_H
+ #define MTK_ETH_H
+
++#include <linux/dma-mapping.h>
++#include <linux/netdevice.h>
++#include <linux/of_net.h>
++#include <linux/u64_stats_sync.h>
+ #include <linux/refcount.h>
+
+ #define MTK_QDMA_PAGE_SIZE 2048
+ #define MTK_MAX_RX_LENGTH 1536
+ #define MTK_TX_DMA_BUF_LEN 0x3fff
+-#define MTK_DMA_SIZE 256
+-#define MTK_NAPI_WEIGHT 64
++#define MTK_DMA_SIZE 2048
++#define MTK_NAPI_WEIGHT 256
+ #define MTK_MAC_COUNT 2
+ #define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+ #define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
+@@ -36,8 +40,6 @@
+ NETIF_MSG_TX_ERR)
+ #define MTK_HW_FEATURES (NETIF_F_IP_CSUM | \
+ NETIF_F_RXCSUM | \
+- NETIF_F_HW_VLAN_CTAG_TX | \
+- NETIF_F_HW_VLAN_CTAG_RX | \
+ NETIF_F_SG | NETIF_F_TSO | \
+ NETIF_F_TSO6 | \
+ NETIF_F_IPV6_CSUM)
+@@ -76,6 +78,9 @@
+ #define MTK_CDMQ_IG_CTRL 0x1400
+ #define MTK_CDMQ_STAG_EN BIT(0)
+
++/* CDMP Ingress Control Register */
++#define MTK_CDMP_IG_CTRL 0x400
++
+ /* CDMP Exgress Control Register */
+ #define MTK_CDMP_EG_CTRL 0x404
+
+@@ -225,8 +230,9 @@
+ #define MTK_TX_DONE_INT1 BIT(1)
+ #define MTK_TX_DONE_INT0 BIT(0)
+ #define MTK_RX_DONE_INT MTK_RX_DONE_DLY
+-#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+- MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
++#define MTK_TX_DONE_DLY BIT(28)
++#define MTK_TX_DONE_INT MTK_TX_DONE_DLY
++
+
+ /* QDMA Interrupt grouping registers */
+ #define MTK_QDMA_INT_GRP1 0x1a20
+@@ -267,6 +273,12 @@
+ #define MTK_GDM1_TX_GBCNT 0x2400
+ #define MTK_STAT_OFFSET 0x40
+
++/* QDMA TX NUM */
++#define MTK_QDMA_TX_NUM 16
++#define MTK_QDMA_TX_MASK ((MTK_QDMA_TX_NUM / 2) - 1)
++#define QID_LOW_BITS(x) ((x) & 0xf)
++#define QID_HIGH_BITS(x) ((((x) >> 4) & 0x3) & GENMASK(21, 20))
++
+ /* QDMA descriptor txd4 */
+ #define TX_DMA_CHKSUM (0x7 << 29)
+ #define TX_DMA_TSO BIT(28)
+@@ -316,6 +328,8 @@
+ #define MAC_MCR_RX_EN BIT(13)
+ #define MAC_MCR_BACKOFF_EN BIT(9)
+ #define MAC_MCR_BACKPR_EN BIT(8)
++#define MAC_MCR_MDIO_EEE_1000T BIT(7)
++#define MAC_MCR_MDIO_EEE_100TX BIT(6)
+ #define MAC_MCR_FORCE_RX_FC BIT(5)
+ #define MAC_MCR_FORCE_TX_FC BIT(4)
+ #define MAC_MCR_SPEED_1000 BIT(3)
+@@ -368,9 +382,11 @@
+ #define ETHSYS_SYSCFG0 0x14
+ #define SYSCFG0_GE_MASK 0x3
+ #define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2)))
+-#define SYSCFG0_SGMII_MASK (3 << 8)
+-#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & GENMASK(9, 8))
+-#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & GENMASK(9, 8))
++#define SYSCFG0_SGMII_MASK GENMASK(9, 8)
++#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & SYSCFG0_SGMII_MASK)
++#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & SYSCFG0_SGMII_MASK)
++#define SYSCFG0_SGMII_GMAC1_V2 BIT(9)
++#define SYSCFG0_SGMII_GMAC2_V2 BIT(8)
+
+ /* ethernet subsystem clock register */
+ #define ETHSYS_CLKCFG0 0x2c
+@@ -398,6 +414,16 @@
+ #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
+ #define SGMII_PHYA_PWD BIT(4)
+
++/* Infrasys subsystem config registers */
++#define INFRA_MISC2 0x70c
++#define CO_QPHY_SEL BIT(0)
++#define GEPHY_MAC_SEL BIT(1)
++
++/*MDIO control*/
++#define MII_MMD_ACC_CTL_REG 0x0d
++#define MII_MMD_ADDR_DATA_REG 0x0e
++#define MMD_OP_MODE_DATA BIT(14)
++
+ struct mtk_rx_dma {
+ unsigned int rxd1;
+ unsigned int rxd2;
+@@ -462,15 +488,21 @@ enum mtk_tx_flags {
+ */
+ enum mtk_clks_map {
+ MTK_CLK_ETHIF,
++ MTK_CLK_SGMIITOP,
+ MTK_CLK_ESW,
+ MTK_CLK_GP0,
+ MTK_CLK_GP1,
+ MTK_CLK_GP2,
++ MTK_CLK_FE,
+ MTK_CLK_TRGPLL,
+ MTK_CLK_SGMII_TX_250M,
+ MTK_CLK_SGMII_RX_250M,
+ MTK_CLK_SGMII_CDR_REF,
+ MTK_CLK_SGMII_CDR_FB,
++ MTK_CLK_SGMII2_TX_250M,
++ MTK_CLK_SGMII2_RX_250M,
++ MTK_CLK_SGMII2_CDR_REF,
++ MTK_CLK_SGMII2_CDR_FB,
+ MTK_CLK_SGMII_CK,
+ MTK_CLK_ETH2PLL,
+ MTK_CLK_MAX
+@@ -488,6 +520,22 @@ enum mtk_clks_map {
+ BIT(MTK_CLK_SGMII_CDR_FB) | \
+ BIT(MTK_CLK_SGMII_CK) | \
+ BIT(MTK_CLK_ETH2PLL))
++#define LEOPARD_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \
++ BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \
++ BIT(MTK_CLK_GP2) | BIT(MTK_CLK_FE) | \
++ BIT(MTK_CLK_SGMII_TX_250M) | \
++ BIT(MTK_CLK_SGMII_RX_250M) | \
++ BIT(MTK_CLK_SGMII_CDR_REF) | \
++ BIT(MTK_CLK_SGMII_CDR_FB) | \
++ BIT(MTK_CLK_SGMII2_TX_250M) | \
++ BIT(MTK_CLK_SGMII2_RX_250M) | \
++ BIT(MTK_CLK_SGMII2_CDR_REF) | \
++ BIT(MTK_CLK_SGMII2_CDR_FB) | \
++ BIT(MTK_CLK_SGMII_CK) | \
++ BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP))
++
++#define MT7621_CLKS_BITMAP 0
++
+ enum mtk_dev_state {
+ MTK_HW_INIT,
+ MTK_RESETTING
+@@ -557,35 +605,149 @@ struct mtk_rx_ring {
+ u32 crx_idx_reg;
+ };
+
+-#define MTK_TRGMII BIT(0)
+-#define MTK_GMAC1_TRGMII (BIT(1) | MTK_TRGMII)
+-#define MTK_ESW BIT(4)
+-#define MTK_GMAC1_ESW (BIT(5) | MTK_ESW)
+-#define MTK_SGMII BIT(8)
+-#define MTK_GMAC1_SGMII (BIT(9) | MTK_SGMII)
+-#define MTK_GMAC2_SGMII (BIT(10) | MTK_SGMII)
+-#define MTK_DUAL_GMAC_SHARED_SGMII (BIT(11) | MTK_GMAC1_SGMII | \
+- MTK_GMAC2_SGMII)
++enum mtk_eth_mux {
++ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW,
++ MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY,
++ MTK_ETH_MUX_U3_GMAC2_TO_QPHY,
++ MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII,
++ MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII,
++ MTK_ETH_MUX_MAX,
++};
++
++enum mtk_eth_path {
++ MTK_ETH_PATH_GMAC1_RGMII,
++ MTK_ETH_PATH_GMAC1_TRGMII,
++ MTK_ETH_PATH_GMAC1_SGMII,
++ MTK_ETH_PATH_GMAC2_RGMII,
++ MTK_ETH_PATH_GMAC2_SGMII,
++ MTK_ETH_PATH_GMAC2_GEPHY,
++ MTK_ETH_PATH_GDM1_ESW,
++ MTK_ETH_PATH_MAX,
++};
++
++/* Capability for function group */
++#define MTK_RGMII BIT(0)
++#define MTK_TRGMII BIT(1)
++#define MTK_SGMII BIT(2)
++#define MTK_ESW BIT(3)
++#define MTK_GEPHY BIT(4)
++#define MTK_MUX BIT(5)
++#define MTK_INFRA BIT(6)
++#define MTK_SHARED_SGMII BIT(7)
++
++/* Capability for features on SoCs */
++#define MTK_PATH_BIT(x) BIT((x) + 10)
++
++#define MTK_GMAC1_RGMII \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC1_RGMII) | MTK_RGMII)
++
++#define MTK_GMAC1_TRGMII \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC1_TRGMII) | MTK_TRGMII)
++
++#define MTK_GMAC1_SGMII \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC1_SGMII) | MTK_SGMII)
++
++#define MTK_GMAC2_RGMII \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC2_RGMII) | MTK_RGMII)
++
++#define MTK_GMAC2_SGMII \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC2_SGMII) | MTK_SGMII)
++
++#define MTK_GMAC2_GEPHY \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GMAC2_GEPHY) | MTK_GEPHY)
++
++#define MTK_GDM1_ESW \
++ (MTK_PATH_BIT(MTK_ETH_PATH_GDM1_ESW) | MTK_ESW)
++
++#define MTK_MUX_BIT(x) BIT((x) + 20)
++
++/* Capability for MUXes present on SoCs */
++/* 0: GDM1 -> GMAC1, 1: GDM1 -> ESW */
++#define MTK_MUX_GDM1_TO_GMAC1_ESW \
++ (MTK_MUX_BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW) | MTK_MUX)
++
++/* 0: GMAC2 -> GEPHY, 1: GMAC0 -> GePHY */
++#define MTK_MUX_GMAC2_GMAC0_TO_GEPHY \
++ (MTK_MUX_BIT(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY) | MTK_MUX | MTK_INFRA)
++
++/* 0: U3 -> QPHY, 1: GMAC2 -> QPHY */
++#define MTK_MUX_U3_GMAC2_TO_QPHY \
++ (MTK_MUX_BIT(MTK_ETH_MUX_U3_GMAC2_TO_QPHY) | MTK_MUX | MTK_INFRA)
++
++/* 2: GMAC1 -> SGMII, 3: GMAC2 -> SGMII */
++#define MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \
++ (MTK_MUX_BIT(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII) | MTK_MUX | \
++ MTK_SHARED_SGMII)
++
++/* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */
++#define MTK_MUX_GMAC12_TO_GEPHY_SGMII \
++ (MTK_MUX_BIT(MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII) | MTK_MUX)
++
+ #define MTK_HWLRO BIT(12)
++
+ #define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x))
+
++#define MT7622_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_SGMII | MTK_GMAC2_RGMII | \
++ MTK_GMAC2_SGMII | MTK_GDM1_ESW | \
++ MTK_MUX_GDM1_TO_GMAC1_ESW | \
++ MTK_MUX_GMAC1_GMAC2_TO_SGMII_RGMII)
++
++#define MT7623_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII)
++
++#define LEOPARD_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \
++ MTK_GDM1_ESW | MTK_MUX_GDM1_TO_GMAC1_ESW | \
++ MTK_MUX_GMAC2_GMAC0_TO_GEPHY | \
++ MTK_MUX_U3_GMAC2_TO_QPHY | \
++ MTK_MUX_GMAC12_TO_GEPHY_SGMII)
++
++#define MT7621_CAPS (MTK_GMAC1_RGMII | MTK_GMAC1_TRGMII | MTK_GMAC2_RGMII)
++
+ /* struct mtk_eth_data - This is the structure holding all differences
+ * among various plaforms
++ * @ana_rgc3: The offset for register ANA_RGC3 related to
++ * sgmiisys syscon
+ * @caps Flags shown the extra capability for the SoC
+ * @required_clks Flags shown the bitmap for required clocks on
+ * the target SoC
+ * @required_pctl A bool value to show whether the SoC requires
+ * the extra setup for those pins used by GMAC.
++ * @irq_num total eth irq num support in target SoC
+ */
+ struct mtk_soc_data {
++ u32 ana_rgc3;
+ u32 caps;
+ u32 required_clks;
+ bool required_pctl;
++ u32 irq_num;
+ };
+
+ /* currently no SoC has more than 2 macs */
+ #define MTK_MAX_DEVS 2
+
++struct mtk_eth_debug {
++ struct dentry *root;
++};
++
++#define MTK_SGMII_PHYSPEED_AN BIT(31)
++#define MTK_SGMII_PHYSPEED_MASK GENMASK(0, 2)
++#define MTK_SGMII_PHYSPEED_1000 BIT(0)
++#define MTK_SGMII_PHYSPEED_2500 BIT(1)
++#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
++
++/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
++ * characteristics
++ * @regmap: The register map pointing at the range used to setup
++ * SGMII modes
++ * @flags: The enum refers to which mode the sgmii wants to run on
++ * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
++ */
++
++struct mtk_sgmii {
++ struct regmap *regmap[MTK_MAX_DEVS];
++ u32 flags[MTK_MAX_DEVS];
++ u32 ana_rgc3;
++};
++
+ /* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
+@@ -601,14 +763,15 @@ struct mtk_soc_data {
+ * @msg_enable: Ethtool msg level
+ * @ethsys: The register map pointing at the range used to setup
+ * MII modes
+- * @sgmiisys: The register map pointing at the range used to setup
+- * SGMII modes
++ * @infra: The register map pointing at the range used to setup
++ * SGMII and GePHY path
+ * @pctl: The register map pointing at the range used to setup
+ * GMAC port drive/slew values
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+ * @tx_ring: Pointer to the memory holding info about the TX ring
+ * @rx_ring: Pointer to the memory holding info about the RX ring
+- * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX ring
++ * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX
++ * ring
+ * @tx_napi: The TX NAPI struct
+ * @rx_napi: The RX NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+@@ -619,13 +782,16 @@ struct mtk_soc_data {
+ * @pending_work: The workqueue used to reset the dma ring
+ * @state: Initialization and runtime state of the device
+ * @soc: Holding specific data among vaious SoCs
++ * @debug: Holding specific data for mtk_eth_dbg usage.
+ */
+
+ struct mtk_eth {
+ struct device *dev;
+ void __iomem *base;
+ spinlock_t page_lock;
++ /* spin_lock for enable/disable tx irq critial section */
+ spinlock_t tx_irq_lock;
++ /* spin_lock for enable/disable rx irq critial section */
+ spinlock_t rx_irq_lock;
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+@@ -634,10 +800,11 @@ struct mtk_eth {
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+- struct regmap *sgmiisys;
++ struct regmap *infra;
++ struct mtk_sgmii *sgmii;
+ struct regmap *pctl;
+ bool hwlro;
+- refcount_t dma_refcnt;
++ atomic_t dma_refcnt;
+ struct mtk_tx_ring tx_ring;
+ struct mtk_rx_ring rx_ring[MTK_MAX_RX_RING_NUM];
+ struct mtk_rx_ring rx_ring_qdma;
+@@ -653,6 +820,7 @@ struct mtk_eth {
+ unsigned long state;
+
+ const struct mtk_soc_data *soc;
++ struct mtk_eth_debug debug;
+ };
+
+ /* struct mtk_mac - the structure that holds the info about the MACs of the
+@@ -664,6 +832,7 @@ struct mtk_eth {
+ * @hw_stats: Packet statistics counter
+ * @trgmii Indicate if the MAC uses TRGMII connected to internal
+ switch
++ * @phy_dev: The attached PHY if available
+ */
+ struct mtk_mac {
+ int id;
+@@ -674,6 +843,7 @@ struct mtk_mac {
+ __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
+ int hwlro_ip_cnt;
+ bool trgmii;
++ struct phy_device *phy_dev;
+ };
+
+ /* the struct describing the SoC. these are declared in the soc_xyz.c files */
+@@ -685,4 +855,10 @@ void mtk_stats_update_mac(struct mtk_mac
+ void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+ u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
++ u32 ana_rgc3);
++int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
++int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id);
++int mtk_setup_hw_path(struct mtk_eth *eth, int mac_id, int phymode);
++
+ #endif /* MTK_ETH_H */
+Index: linux-4.19.57/drivers/net/ethernet/mediatek/mtk_sgmii.c
+===================================================================
+--- /dev/null
++++ linux-4.19.57/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -0,0 +1,114 @@
++/*
++ * Copyright (C) 2018 MediaTek Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * Copyright (C) 2018 Sean Wang <sean.wang@mediatek.com>
++ */
++
++#include <linux/mfd/syscon.h>
++#include <linux/of.h>
++#include <linux/regmap.h>
++
++#include "mtk_eth_soc.h"
++
++int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
++{
++ struct device_node *np;
++ const char *str;
++ int i, err;
++
++ ss->ana_rgc3 = ana_rgc3;
++
++ for (i = 0; i < MTK_MAX_DEVS; i++) {
++ np = of_parse_phandle(r, "mediatek,sgmiisys", i);
++ if (!np)
++ break;
++
++ ss->regmap[i] = syscon_node_to_regmap(np);
++ if (IS_ERR(ss->regmap[i]))
++ return PTR_ERR(ss->regmap[i]);
++
++ err = of_property_read_string(np, "mediatek,physpeed", &str);
++ if (err)
++ return err;
++
++ if (!strcmp(str, "2500"))
++ pr_info("sean debug physpeed = 2500\n");
++
++ if (!strcmp(str, "2500"))
++ ss->flags[i] |= MTK_SGMII_PHYSPEED_2500;
++ else if (!strcmp(str, "1000"))
++ ss->flags[i] |= MTK_SGMII_PHYSPEED_1000;
++ else if (!strcmp(str, "auto"))
++ ss->flags[i] |= MTK_SGMII_PHYSPEED_AN;
++ else
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
++{
++ unsigned int val;
++
++ if (!ss->regmap[id])
++ return -EINVAL;
++
++ /* Setup the link timer and QPHY power up inside SGMIISYS */
++ regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
++ SGMII_LINK_TIMER_DEFAULT);
++
++ regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
++ val |= SGMII_REMOTE_FAULT_DIS;
++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++
++ regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ val |= SGMII_AN_RESTART;
++ regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++
++ regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ val &= ~SGMII_PHYA_PWD;
++ regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++
++ return 0;
++}
++
++int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id)
++{
++ unsigned int val;
++ int mode;
++
++ if (!ss->regmap[id])
++ return -EINVAL;
++
++ regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
++ val &= ~GENMASK(2, 3);
++ mode = ss->flags[id] & MTK_SGMII_PHYSPEED_MASK;
++ val |= (mode == MTK_SGMII_PHYSPEED_1000) ? 0 : BIT(2);
++ regmap_write(ss->regmap[id], ss->ana_rgc3, val);
++
++ /* disable SGMII AN */
++ regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
++ val &= ~BIT(12);
++ regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
++
++ /* SGMII force mode setting */
++ val = 0x31120019;
++ regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
++
++ /* Release PHYA power down state */
++ regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
++ val &= ~SGMII_PHYA_PWD;
++ regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
++
++ return 0;
++}
diff --git a/target/linux/mediatek/patches-4.19/0003-mt7531-gsw-internal_phy_calibration.patch b/target/linux/mediatek/patches-4.19/0003-mt7531-gsw-internal_phy_calibration.patch
new file mode 100644
index 0000000000..6eba4017d7
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0003-mt7531-gsw-internal_phy_calibration.patch
@@ -0,0 +1,1259 @@
+--- a/drivers/net/phy/mtk/mt753x/Makefile
++++ b/drivers/net/phy/mtk/mt753x/Makefile
+@@ -5,6 +5,6 @@
+ obj-$(CONFIG_MT753X_GSW) += mt753x.o
+
+ mt753x-y += mt753x_mdio.o mt7530.o mt7531.o \
+- mt753x_common.o mt753x_nl.o
++ mt753x_common.o mt753x_nl.o mt753x_phy.o
+
+
+--- a/drivers/net/phy/mtk/mt753x/mt7531.c
++++ b/drivers/net/phy/mtk/mt753x/mt7531.c
+@@ -454,6 +454,21 @@ static void mt7531_core_pll_setup(struct gsw_mt753x *gsw)
+
+ static int mt7531_internal_phy_calibration(struct gsw_mt753x *gsw)
+ {
++ u32 i;
++ int ret;
++
++ dev_info(gsw->dev,">>>>>>>>>>>>>>>>>>>>>>>>>>>>> START CALIBRATION:\n");
++ 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;
+ }
+
+--- a/drivers/net/phy/mtk/mt753x/mt753x.h
++++ b/drivers/net/phy/mtk/mt753x/mt753x.h
+@@ -141,6 +141,8 @@ void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg,
+ 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);
++
+ /* MDIO Indirect Access Registers */
+ #define MII_MMD_ACC_CTL_REG 0x0d
+ #define MMD_CMD_S 14
+--- /dev/null
++++ b/drivers/net/phy/mtk/mt753x/mt753x_phy.c
+@@ -0,0 +1,1061 @@
++// 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);
++}
++
++//-----------------------------------------------------------------
++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, phyaddr, 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);
++}
++
++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
++}
++
++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;
++ 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
++
++ 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);
++ 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 = (reg_backup & (~0xfc00));
++ reg_backup |= (reg_tmp << 10);
++ 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 = (reg_backup & (~0x3f00));
++ reg_backup |= (reg_tmp << 8);
++ 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);
++
++}
++
++//-----------------------------------------------------------------
++
++int phy_calibration(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++ u32 reg_tmp,reg_tmp0, reg_tmp1, i;
++ u32 CALDLY = 40;
++ 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 */
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, RG_185, 0);/* disable tx slew control */
++ 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*/
++
++ 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);
++ 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);
++#endif
++}
++
++void rx_dc_offset(struct gsw_mt753x *gsw, u8 phyaddr)
++{
++ u32 reg_tmp1;
++
++ 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;
++ u8 reg_val;
++
++ 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;
++ u8 reg_val;
++
++ 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;
++ u8 reg_val;
++
++ 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;
++ u8 reg_val;
++
++ 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;
++
++ phy_calibration(gsw, phyaddr);
++
++ /*eye pic*/
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x0, 0x187);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x1, 0x1c9);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x2, 0x1c6);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x3, 0x182);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x4, 0x208);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x5, 0x205);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x6, 0x384);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x7, 0x3cb);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x8, 0x3c4);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0x9, 0x30a);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0xa, 0x00b);
++ tc_phy_write_dev_reg(gsw, phyaddr, 0x1e, 0xb, 0x002);
++
++ 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;
++}
+diff --git a/target/linux/generic/files/drivers/net/phy/mtk/mt753x/mt753x_phy.h b/target/linux/generic/files/drivers/net/phy/mtk/mt753x/mt753x_phy.h
+new file mode 100755
+--- /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-4.19/0003-switch-add-mt7531.patch b/target/linux/mediatek/patches-4.19/0003-switch-add-mt7531.patch
new file mode 100644
index 0000000000..65e56e4940
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0003-switch-add-mt7531.patch
@@ -0,0 +1,23 @@
+Index: linux-4.19.57/drivers/net/phy/Kconfig
+===================================================================
+--- linux-4.19.57.orig/drivers/net/phy/Kconfig
++++ linux-4.19.57/drivers/net/phy/Kconfig
+@@ -292,6 +292,8 @@ config RTL8367B_PHY
+
+ endif # RTL8366_SMI
+
++source "drivers/net/phy/mtk/mt753x/Kconfig"
++
+ comment "MII PHY device drivers"
+
+ config SFP
+Index: linux-4.19.57/drivers/net/phy/Makefile
+===================================================================
+--- linux-4.19.57.orig/drivers/net/phy/Makefile
++++ linux-4.19.57/drivers/net/phy/Makefile
+@@ -100,3 +100,5 @@ obj-$(CONFIG_STE10XP) += ste10Xp.o
+ obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
+ obj-$(CONFIG_VITESSE_PHY) += vitesse.o
+ obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
++obj-$(CONFIG_MT753X_GSW) += mtk/mt753x/
++
diff --git a/target/linux/mediatek/patches-4.19/0227-arm-dts-Add-Unielec-U7623-DTS.patch b/target/linux/mediatek/patches-4.19/0227-arm-dts-Add-Unielec-U7623-DTS.patch
new file mode 100644
index 0000000000..d6e63714e3
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0227-arm-dts-Add-Unielec-U7623-DTS.patch
@@ -0,0 +1,419 @@
+From 004eb24e939b5b31f828333f37fb5cb2a877d6f2 Mon Sep 17 00:00:00 2001
+From: Kristian Evensen <kristian.evensen@gmail.com>
+Date: Sun, 17 Jun 2018 14:41:47 +0200
+Subject: [PATCH] arm: dts: Add Unielec U7623 DTS
+
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ .../dts/mt7623a-unielec-u7623-02-emmc-512M.dts | 18 +
+ .../boot/dts/mt7623a-unielec-u7623-02-emmc.dtsi | 366 +++++++++++++++++++++
+ 3 files changed, 385 insertions(+)
+ create mode 100644 arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc-512M.dts
+ create mode 100644 arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc.dtsi
+
+Index: linux-4.19.57/arch/arm/boot/dts/Makefile
+===================================================================
+--- linux-4.19.57.orig/arch/arm/boot/dts/Makefile
++++ linux-4.19.57/arch/arm/boot/dts/Makefile
+@@ -1193,6 +1193,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
+ mt7623a-rfb-nand.dtb \
+ mt7623n-rfb-emmc.dtb \
+ mt7623n-bananapi-bpi-r2.dtb \
++ mt7623a-unielec-u7623-02-emmc-512M.dtb \
+ mt8127-moose.dtb \
+ mt8135-evbp1.dtb
+ dtb-$(CONFIG_ARCH_ZX) += zx296702-ad1.dtb
+Index: linux-4.19.57/arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc-512M.dts
+===================================================================
+--- /dev/null
++++ linux-4.19.57/arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc-512M.dts
+@@ -0,0 +1,18 @@
++/*
++ * Copyright 2018 Kristian Evensen <kristian.evensen@gmail.com>
++ *
++ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++ */
++
++/dts-v1/;
++#include "mt7623a-unielec-u7623-02-emmc.dtsi"
++
++/ {
++ model = "UniElec U7623-02 eMMC (512M RAM)";
++ compatible = "unielec,u7623-02-emmc-512m", "unielec,u7623-02-emmc", "mediatek,mt7623";
++
++ memory@80000000 {
++ device_type = "memory";
++ reg = <0 0x80000000 0 0x20000000>;
++ };
++};
+Index: linux-4.19.57/arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc.dtsi
+===================================================================
+--- /dev/null
++++ linux-4.19.57/arch/arm/boot/dts/mt7623a-unielec-u7623-02-emmc.dtsi
+@@ -0,0 +1,366 @@
++/*
++ * Copyright 2018 Kristian Evensen <kristian.evensen@gmail.com>
++ *
++ * SPDX-License-Identifier: (GPL-2.0+ OR MIT)
++ */
++
++#include <dt-bindings/input/input.h>
++#include "mt7623.dtsi"
++#include "mt6323.dtsi"
++
++/ {
++ compatible = "unielec,u7623-02-emmc", "mediatek,mt7623";
++
++ aliases {
++ serial2 = &uart2;
++ };
++
++ chosen {
++ bootargs = "root=/dev/mmcblk0p2 rootfstype=squashfs,f2fs";
++ stdout-path = "serial2:115200n8";
++ };
++
++ cpus {
++ cpu@0 {
++ proc-supply = <&mt6323_vproc_reg>;
++ };
++
++ cpu@1 {
++ proc-supply = <&mt6323_vproc_reg>;
++ };
++
++ cpu@2 {
++ proc-supply = <&mt6323_vproc_reg>;
++ };
++
++ cpu@3 {
++ proc-supply = <&mt6323_vproc_reg>;
++ };
++ };
++
++ reg_1p8v: regulator-1p8v {
++ compatible = "regulator-fixed";
++ regulator-name = "fixed-1.8V";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++ regulator-boot-on;
++ regulator-always-on;
++ };
++
++ reg_3p3v: regulator-3p3v {
++ compatible = "regulator-fixed";
++ regulator-name = "fixed-3.3V";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-boot-on;
++ regulator-always-on;
++ };
++
++ reg_5v: regulator-5v {
++ compatible = "regulator-fixed";
++ regulator-name = "fixed-5V";
++ regulator-min-microvolt = <5000000>;
++ regulator-max-microvolt = <5000000>;
++ regulator-boot-on;
++ regulator-always-on;
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++ pinctrl-names = "default";
++ pinctrl-0 = <&key_pins_a>;
++
++ factory {
++ label = "factory";
++ linux,code = <KEY_RESTART>;
++ gpios = <&pio 256 GPIO_ACTIVE_LOW>;
++ };
++ };
++
++ leds {
++ compatible = "gpio-leds";
++ pinctrl-names = "default";
++ pinctrl-0 = <&led_pins_unielec>;
++
++ led3 {
++ label = "u7623-01:green:led3";
++ gpios = <&pio 14 GPIO_ACTIVE_LOW>;
++ default-state = "off";
++ };
++
++ led4 {
++ label = "u7623-01:green:led4";
++ gpios = <&pio 15 GPIO_ACTIVE_LOW>;
++ default-state = "off";
++ };
++ };
++
++ mt7530: switch@0 {
++ compatible = "mediatek,mt7530";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++};
++
++&crypto {
++ status = "okay";
++};
++
++&eth {
++ status = "okay";
++
++ gmac0: mac@0 {
++ compatible = "mediatek,eth-mac";
++ reg = <0>;
++ phy-mode = "trgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ pause;
++ };
++ };
++
++ mdio: mdio-bus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ phy5: ethernet-phy@5 {
++ reg = <5>;
++ phy-mode = "rgmii-rxid";
++ };
++ };
++};
++
++&mt7530 {
++ compatible = "mediatek,mt7530";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0>;
++ pinctrl-names = "default";
++ mediatek,mcm;
++ resets = <&ethsys 2>;
++ reset-names = "mcm";
++ core-supply = <&mt6323_vpa_reg>;
++ io-supply = <&mt6323_vemc3v3_reg>;
++
++ dsa,mii-bus = <&mdio>;
++
++ ports {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0>;
++
++ port@0 {
++ reg = <0>;
++ label = "lan0";
++ cpu = <&cpu_port0>;
++ };
++
++ port@1 {
++ reg = <1>;
++ label = "lan1";
++ cpu = <&cpu_port0>;
++ };
++
++ port@2 {
++ reg = <2>;
++ label = "lan2";
++ cpu = <&cpu_port0>;
++ };
++
++ port@3 {
++ reg = <3>;
++ label = "lan3";
++ cpu = <&cpu_port0>;
++ };
++
++ port@4 {
++ reg = <4>;
++ label = "wan";
++ cpu = <&cpu_port0>;
++ };
++
++ cpu_port0: port@6 {
++ reg = <6>;
++ label = "cpu";
++ ethernet = <&gmac0>;
++ phy-mode = "trgmii";
++
++ fixed-link {
++ speed = <1000>;
++ full-duplex;
++ };
++ };
++ };
++};
++
++&mmc0 {
++ pinctrl-names = "default", "state_uhs";
++ pinctrl-0 = <&mmc0_pins_default>;
++ pinctrl-1 = <&mmc0_pins_uhs>;
++ status = "okay";
++ bus-width = <8>;
++ max-frequency = <50000000>;
++ cap-mmc-highspeed;
++ vmmc-supply = <&reg_3p3v>;
++ vqmmc-supply = <&reg_1p8v>;
++ non-removable;
++};
++
++&pio {
++ key_pins_a: keys-alt {
++ pins-keys {
++ pinmux = <MT7623_PIN_256_GPIO256_FUNC_GPIO256>,
++ <MT7623_PIN_257_GPIO257_FUNC_GPIO257>;
++ input-enable;
++ };
++ };
++
++ led_pins_unielec: leds-unielec {
++ pins-leds {
++ pinmux = <MT7623_PIN_14_GPIO14_FUNC_GPIO14>,
++ <MT7623_PIN_15_GPIO15_FUNC_GPIO15>;
++ };
++ };
++
++ mmc0_pins_default: mmc0default {
++ pins_cmd_dat {
++ pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_MSDC0_DAT7>,
++ <MT7623_PIN_112_MSDC0_DAT6_FUNC_MSDC0_DAT6>,
++ <MT7623_PIN_113_MSDC0_DAT5_FUNC_MSDC0_DAT5>,
++ <MT7623_PIN_114_MSDC0_DAT4_FUNC_MSDC0_DAT4>,
++ <MT7623_PIN_118_MSDC0_DAT3_FUNC_MSDC0_DAT3>,
++ <MT7623_PIN_119_MSDC0_DAT2_FUNC_MSDC0_DAT2>,
++ <MT7623_PIN_120_MSDC0_DAT1_FUNC_MSDC0_DAT1>,
++ <MT7623_PIN_121_MSDC0_DAT0_FUNC_MSDC0_DAT0>,
++ <MT7623_PIN_116_MSDC0_CMD_FUNC_MSDC0_CMD>;
++ input-enable;
++ bias-pull-up;
++ };
++
++ pins_clk {
++ pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_MSDC0_CLK>;
++ bias-pull-down;
++ };
++
++ pins_rst {
++ pinmux = <MT7623_PIN_115_MSDC0_RSTB_FUNC_MSDC0_RSTB>;
++ bias-pull-up;
++ };
++ };
++
++ mmc0_pins_uhs: mmc0 {
++ pins_cmd_dat {
++ pinmux = <MT7623_PIN_111_MSDC0_DAT7_FUNC_MSDC0_DAT7>,
++ <MT7623_PIN_112_MSDC0_DAT6_FUNC_MSDC0_DAT6>,
++ <MT7623_PIN_113_MSDC0_DAT5_FUNC_MSDC0_DAT5>,
++ <MT7623_PIN_114_MSDC0_DAT4_FUNC_MSDC0_DAT4>,
++ <MT7623_PIN_118_MSDC0_DAT3_FUNC_MSDC0_DAT3>,
++ <MT7623_PIN_119_MSDC0_DAT2_FUNC_MSDC0_DAT2>,
++ <MT7623_PIN_120_MSDC0_DAT1_FUNC_MSDC0_DAT1>,
++ <MT7623_PIN_121_MSDC0_DAT0_FUNC_MSDC0_DAT0>,
++ <MT7623_PIN_116_MSDC0_CMD_FUNC_MSDC0_CMD>;
++ input-enable;
++ drive-strength = <MTK_DRIVE_2mA>;
++ bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
++ };
++
++ pins_clk {
++ pinmux = <MT7623_PIN_117_MSDC0_CLK_FUNC_MSDC0_CLK>;
++ drive-strength = <MTK_DRIVE_2mA>;
++ bias-pull-down = <MTK_PUPD_SET_R1R0_01>;
++ };
++
++ pins_rst {
++ pinmux = <MT7623_PIN_115_MSDC0_RSTB_FUNC_MSDC0_RSTB>;
++ bias-pull-up;
++ };
++ };
++
++ pwm_pins_a: pwm@0 {
++ pins_pwm {
++ pinmux = <MT7623_PIN_203_PWM0_FUNC_PWM0>,
++ <MT7623_PIN_204_PWM1_FUNC_PWM1>,
++ <MT7623_PIN_205_PWM2_FUNC_PWM2>,
++ <MT7623_PIN_206_PWM3_FUNC_PWM3>,
++ <MT7623_PIN_207_PWM4_FUNC_PWM4>;
++ };
++ };
++
++ uart2_pins_b: uart@2 {
++ pins_dat {
++ pinmux = <MT7623_PIN_200_URXD2_FUNC_URXD2>,
++ <MT7623_PIN_201_UTXD2_FUNC_UTXD2>;
++ };
++ };
++
++ pcie_default: pcie_pin_default {
++ pins_cmd_dat {
++ pinmux = <MT7623_PIN_208_AUD_EXT_CK1_FUNC_PCIE0_PERST_N>,
++ <MT7623_PIN_209_AUD_EXT_CK2_FUNC_PCIE1_PERST_N>;
++ bias-disable;
++ };
++ };
++};
++
++&pwm {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwm_pins_a>;
++ status = "okay";
++};
++
++&pwrap {
++ mt6323 {
++ mt6323led: led {
++ compatible = "mediatek,mt6323-led";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ led@0 {
++ reg = <0>;
++ label = "led0";
++ default-state = "off";
++ };
++ };
++ };
++};
++
++&uart2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&uart2_pins_b>;
++ status = "okay";
++};
++
++&usb1 {
++ vusb33-supply = <&reg_3p3v>;
++ vbus-supply = <&reg_3p3v>;
++ status = "okay";
++};
++
++&u3phy1 {
++ status = "okay";
++};
++
++&u3phy2 {
++ status = "okay";
++ mediatek,phy-switch = <&hifsys>;
++};
++
++&pcie {
++ pinctrl-names = "default";
++ pinctrl-0 = <&pcie_default>;
++ status = "okay";
++
++ pcie@1,0 {
++ status = "okay";
++ };
++
++ pcie@2,0 {
++ status = "okay";
++ };
++};
++
++&pcie1_phy {
++ status = "okay";
++};
++
diff --git a/target/linux/mediatek/patches-4.19/0301-mtd-mtk-ecc-move-mtk-ecc-header-file-to-include-mtd.patch b/target/linux/mediatek/patches-4.19/0301-mtd-mtk-ecc-move-mtk-ecc-header-file-to-include-mtd.patch
new file mode 100755
index 0000000000..fcfb3dc368
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0301-mtd-mtk-ecc-move-mtk-ecc-header-file-to-include-mtd.patch
@@ -0,0 +1,48 @@
+From a2479dc254ebe31c84fbcfda73f35e2321576494 Mon Sep 17 00:00:00 2001
+From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+Date: Tue, 19 Mar 2019 13:57:38 +0800
+Subject: [PATCH 1/6] mtd: mtk ecc: move mtk ecc header file to include/mtd
+
+Change-Id: I8dc1d30e21b40d68ef5efd9587012f82970156a5
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ drivers/mtd/nand/raw/mtk_ecc.c | 3 +--
+ drivers/mtd/nand/raw/mtk_nand.c | 2 +-
+ {drivers/mtd/nand/raw => include/linux/mtd}/mtk_ecc.h | 0
+ 3 files changed, 2 insertions(+), 3 deletions(-)
+ rename {drivers/mtd/nand/raw => include/linux/mtd}/mtk_ecc.h (100%)
+
+diff --git a/drivers/mtd/nand/raw/mtk_ecc.c b/drivers/mtd/nand/raw/mtk_ecc.c
+index 6432bd70c3b3..32e9784b0d4f 100644
+--- a/drivers/mtd/nand/raw/mtk_ecc.c
++++ b/drivers/mtd/nand/raw/mtk_ecc.c
+@@ -23,8 +23,7 @@
+ #include <linux/of.h>
+ #include <linux/of_platform.h>
+ #include <linux/mutex.h>
+-
+-#include "mtk_ecc.h"
++#include <linux/mtd/mtk_ecc.h>
+
+ #define ECC_IDLE_MASK BIT(0)
+ #define ECC_IRQ_EN BIT(0)
+diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c
+index 57b5ed1699e3..e201f1417fba 100644
+--- a/drivers/mtd/nand/raw/mtk_nand.c
++++ b/drivers/mtd/nand/raw/mtk_nand.c
+@@ -25,7 +25,7 @@
+ #include <linux/iopoll.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+-#include "mtk_ecc.h"
++#include <linux/mtd/mtk_ecc.h>
+
+ /* NAND controller register definition */
+ #define NFI_CNFG (0x00)
+diff --git a/drivers/mtd/nand/raw/mtk_ecc.h b/include/linux/mtd/mtk_ecc.h
+similarity index 100%
+rename from drivers/mtd/nand/raw/mtk_ecc.h
+rename to include/linux/mtd/mtk_ecc.h
+--
+2.20.1
+
diff --git a/target/linux/mediatek/patches-4.19/0303-mtd-spinand-disable-on-die-ECC.patch b/target/linux/mediatek/patches-4.19/0303-mtd-spinand-disable-on-die-ECC.patch
new file mode 100755
index 0000000000..eeb096acda
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0303-mtd-spinand-disable-on-die-ECC.patch
@@ -0,0 +1,36 @@
+From b341f120cfc9ca1dfd48364b7f36ac2c1fbdea43 Mon Sep 17 00:00:00 2001
+From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+Date: Wed, 3 Apr 2019 16:30:01 +0800
+Subject: [PATCH 3/6] mtd: spinand: disable on-die ECC
+
+Change-Id: I9745adaed5295202fabbe8ab8947885c57a5b847
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ drivers/mtd/nand/spi/core.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index 30f83649c481..3b6eac391350 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -554,7 +554,7 @@ static int spinand_mtd_read(struct mtd_info *mtd, loff_t from,
+ int ret = 0;
+
+ if (ops->mode != MTD_OPS_RAW && spinand->eccinfo.ooblayout)
+- enable_ecc = true;
++ enable_ecc = false;
+
+ mutex_lock(&spinand->lock);
+
+@@ -602,7 +602,7 @@ static int spinand_mtd_write(struct mtd_info *mtd, loff_t to,
+ int ret = 0;
+
+ if (ops->mode != MTD_OPS_RAW && mtd->ooblayout)
+- enable_ecc = true;
++ enable_ecc = false;
+
+ mutex_lock(&spinand->lock);
+
+--
+2.20.1
+
diff --git a/target/linux/mediatek/patches-4.19/0304-dt-bindings-ARM-MediaTek-Document-devicetree-binding.patch b/target/linux/mediatek/patches-4.19/0304-dt-bindings-ARM-MediaTek-Document-devicetree-binding.patch
new file mode 100755
index 0000000000..a605fd1190
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0304-dt-bindings-ARM-MediaTek-Document-devicetree-binding.patch
@@ -0,0 +1,66 @@
+From 28ec0b7e48bb27435a8b3134019b88628faf497f Mon Sep 17 00:00:00 2001
+From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+Date: Tue, 11 Dec 2018 17:37:28 +0800
+Subject: [PATCH 4/6] dt-bindings: ARM: MediaTek: Document devicetree bindings
+ for SPI NAND interface
+
+Change-Id: I9ece142055ae27100da95826fb3ea1960c2994e6
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ .../devicetree/bindings/spi/spi-mtk-snfi.txt | 44 +++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/spi/spi-mtk-snfi.txt
+
+diff --git a/Documentation/devicetree/bindings/spi/spi-mtk-snfi.txt b/Documentation/devicetree/bindings/spi/spi-mtk-snfi.txt
+new file mode 100644
+index 000000000000..a09c476c5289
+--- /dev/null
++++ b/Documentation/devicetree/bindings/spi/spi-mtk-snfi.txt
+@@ -0,0 +1,44 @@
++MediaTek SoCs SPI NAND FLASH interface (SNFI) DT binding
++
++This file documents the device tree bindings for MTK SoCs SPI NAND controller.
++Note that Parallel Nand and SPI NAND is alternative on MTK SoCs.
++
++Required properties:
++- compatible: should be "mediatek,mt7622-snfi"
++- reg: base physical address and size of SNFI.
++- interrupts: interrupts of SNFI.
++- clocks: SNFI required clocks.
++- clock-names: SNFI clocks internal names.
++- #address-cells: NAND chip index, should be 1.
++- #size-cells: Should be 0.
++
++Example:
++ snfi: spi@1100d000 {
++ compatible = "mediatek,mt7622-snfi";
++ reg = <0 0x1100d000 0 0x1000>;
++ interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_LOW>;
++ clocks = <&pericfg CLK_PERI_NFI_PD>,
++ <&pericfg CLK_PERI_SNFI_PD>;
++ clock-names = "nfi_clk", "spi_clk";
++ ecc-engine = <&bch>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
++Subnodes properties:
++- Should use spi-nand framework, see Documentation/devicetree/bindings/mtd/spi-nand.txt
++
++Example:
++&snfi {
++ pinctrl-names = "default";
++ pinctrl-0 = <&serial_nand_pins>;
++ status = "okay";
++
++ spi_nand@0 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ compatible = "spi-nand";
++ spi-max-frequency = <104000000>;
++ reg = <0>;
++ };
++};
+--
+2.20.1
+
diff --git a/target/linux/mediatek/patches-4.19/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch b/target/linux/mediatek/patches-4.19/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
new file mode 100644
index 0000000000..c307abead9
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
@@ -0,0 +1,1235 @@
+From 1ecb38eabd90efe93957d0a822a167560c39308a Mon Sep 17 00:00:00 2001
+From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+Date: Wed, 20 Mar 2019 16:19:51 +0800
+Subject: [PATCH 6/6] spi: spi-mem: MediaTek: Add SPI NAND Flash interface
+ driver for MediaTek MT7622
+
+Change-Id: I3e78406bb9b46b0049d3988a5c71c7069e4f809c
+Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
+---
+ drivers/spi/Kconfig | 9 +
+ drivers/spi/Makefile | 1 +
+ drivers/spi/spi-mtk-snfi.c | 1183 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 1193 insertions(+)
+ create mode 100644 drivers/spi/spi-mtk-snfi.c
+
+Index: linux-4.19.48/drivers/spi/spi-mtk-snfi.c
+===================================================================
+--- /dev/null
++++ linux-4.19.48/drivers/spi/spi-mtk-snfi.c
+@@ -0,0 +1,1183 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Driver for MediaTek SPI Nand interface
++ *
++ * Copyright (C) 2018 MediaTek Inc.
++ * Authors: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
++ *
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/iopoll.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/mtk_ecc.h>
++#include <linux/mtd/spinand.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi-mem.h>
++
++/* NAND controller register definition */
++/* NFI control */
++#define NFI_CNFG 0x00
++#define CNFG_DMA BIT(0)
++#define CNFG_READ_EN BIT(1)
++#define CNFG_DMA_BURST_EN BIT(2)
++#define CNFG_BYTE_RW BIT(6)
++#define CNFG_HW_ECC_EN BIT(8)
++#define CNFG_AUTO_FMT_EN BIT(9)
++#define CNFG_OP_PROGRAM (3UL << 12)
++#define CNFG_OP_CUST (6UL << 12)
++#define NFI_PAGEFMT 0x04
++#define PAGEFMT_512 0
++#define PAGEFMT_2K 1
++#define PAGEFMT_4K 2
++#define PAGEFMT_FDM_SHIFT 8
++#define PAGEFMT_FDM_ECC_SHIFT 12
++#define NFI_CON 0x08
++#define CON_FIFO_FLUSH BIT(0)
++#define CON_NFI_RST BIT(1)
++#define CON_BRD BIT(8)
++#define CON_BWR BIT(9)
++#define CON_SEC_SHIFT 12
++#define NFI_INTR_EN 0x10
++#define INTR_AHB_DONE_EN BIT(6)
++#define NFI_INTR_STA 0x14
++#define NFI_CMD 0x20
++#define NFI_STA 0x60
++#define STA_EMP_PAGE BIT(12)
++#define NAND_FSM_MASK (0x1f << 24)
++#define NFI_FSM_MASK (0xf << 16)
++#define NFI_ADDRCNTR 0x70
++#define CNTR_MASK GENMASK(16, 12)
++#define ADDRCNTR_SEC_SHIFT 12
++#define ADDRCNTR_SEC(val) \
++ (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT)
++#define NFI_STRADDR 0x80
++#define NFI_BYTELEN 0x84
++#define NFI_CSEL 0x90
++#define NFI_FDML(x) (0xa0 + (x) * sizeof(u32) * 2)
++#define NFI_FDMM(x) (0xa4 + (x) * sizeof(u32) * 2)
++#define NFI_MASTER_STA 0x224
++#define MASTER_STA_MASK 0x0fff
++/* NFI_SPI control */
++#define SNFI_MAC_OUTL 0x504
++#define SNFI_MAC_INL 0x508
++#define SNFI_RD_CTL2 0x510
++#define RD_CMD_MASK 0x00ff
++#define RD_DUMMY_SHIFT 8
++#define SNFI_RD_CTL3 0x514
++#define RD_ADDR_MASK 0xffff
++#define SNFI_MISC_CTL 0x538
++#define RD_MODE_X2 BIT(16)
++#define RD_MODE_X4 (2UL << 16)
++#define RD_QDUAL_IO (4UL << 16)
++#define RD_MODE_MASK (7UL << 16)
++#define RD_CUSTOM_EN BIT(6)
++#define WR_CUSTOM_EN BIT(7)
++#define WR_X4_EN BIT(20)
++#define SW_RST BIT(28)
++#define SNFI_MISC_CTL2 0x53c
++#define WR_LEN_SHIFT 16
++#define SNFI_PG_CTL1 0x524
++#define WR_LOAD_CMD_SHIFT 8
++#define SNFI_PG_CTL2 0x528
++#define WR_LOAD_ADDR_MASK 0xffff
++#define SNFI_MAC_CTL 0x500
++#define MAC_WIP BIT(0)
++#define MAC_WIP_READY BIT(1)
++#define MAC_TRIG BIT(2)
++#define MAC_EN BIT(3)
++#define MAC_SIO_SEL BIT(4)
++#define SNFI_STA_CTL1 0x550
++#define SPI_STATE_IDLE 0xf
++#define SNFI_CNFG 0x55c
++#define SNFI_MODE_EN BIT(0)
++#define SNFI_GPRAM_DATA 0x800
++#define SNFI_GPRAM_MAX_LEN 16
++
++/* Dummy command trigger NFI to spi mode */
++#define NAND_CMD_DUMMYREAD 0x00
++#define NAND_CMD_DUMMYPROG 0x80
++
++#define MTK_TIMEOUT 500000
++#define MTK_RESET_TIMEOUT 1000000
++#define MTK_SNFC_MIN_SPARE 16
++#define KB(x) ((x) * 1024UL)
++
++/*
++ * supported spare size of each IP.
++ * order should be the same with the spare size bitfiled defination of
++ * register NFI_PAGEFMT.
++ */
++static const u8 spare_size_mt7622[] = {
++ 16, 26, 27, 28
++};
++
++struct mtk_snfi_caps {
++ const u8 *spare_size;
++ u8 num_spare_size;
++ u32 nand_sec_size;
++ u8 nand_fdm_size;
++ u8 nand_fdm_ecc_size;
++ u8 ecc_parity_bits;
++ u8 pageformat_spare_shift;
++ u8 bad_mark_swap;
++};
++
++struct mtk_snfi_bad_mark_ctl {
++ void (*bm_swap)(struct spi_mem *mem, u8 *buf, int raw);
++ u32 sec;
++ u32 pos;
++};
++
++struct mtk_snfi_nand_chip {
++ struct mtk_snfi_bad_mark_ctl bad_mark;
++ u32 spare_per_sector;
++};
++
++struct mtk_snfi_clk {
++ struct clk *nfi_clk;
++ struct clk *spi_clk;
++};
++
++struct mtk_snfi {
++ const struct mtk_snfi_caps *caps;
++ struct mtk_snfi_nand_chip snfi_nand;
++ struct mtk_snfi_clk clk;
++ struct mtk_ecc_config ecc_cfg;
++ struct mtk_ecc *ecc;
++ struct completion done;
++ struct device *dev;
++
++ void __iomem *regs;
++
++ u8 *buffer;
++};
++
++static inline u8 *oob_ptr(struct spi_mem *mem, int i)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u8 *poi;
++
++ /* map the sector's FDM data to free oob:
++ * the beginning of the oob area stores the FDM data of bad mark
++ */
++
++ if (i < snfi_nand->bad_mark.sec)
++ poi = spinand->oobbuf + (i + 1) * snfi->caps->nand_fdm_size;
++ else if (i == snfi_nand->bad_mark.sec)
++ poi = spinand->oobbuf;
++ else
++ poi = spinand->oobbuf + i * snfi->caps->nand_fdm_size;
++
++ return poi;
++}
++
++static inline int mtk_data_len(struct spi_mem *mem)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++
++ return snfi->caps->nand_sec_size + snfi_nand->spare_per_sector;
++}
++
++static inline u8 *mtk_oob_ptr(struct spi_mem *mem,
++ const u8 *p, int i)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++
++ return (u8 *)p + i * mtk_data_len(mem) + snfi->caps->nand_sec_size;
++}
++
++static void mtk_snfi_bad_mark_swap(struct spi_mem *mem,
++ u8 *buf, int raw)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 bad_pos = snfi_nand->bad_mark.pos;
++
++ if (raw)
++ bad_pos += snfi_nand->bad_mark.sec * mtk_data_len(mem);
++ else
++ bad_pos += snfi_nand->bad_mark.sec * snfi->caps->nand_sec_size;
++
++ swap(spinand->oobbuf[0], buf[bad_pos]);
++}
++
++static void mtk_snfi_set_bad_mark_ctl(struct mtk_snfi_bad_mark_ctl *bm_ctl,
++ struct spi_mem *mem)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++
++ bm_ctl->bm_swap = mtk_snfi_bad_mark_swap;
++ bm_ctl->sec = mtd->writesize / mtk_data_len(mem);
++ bm_ctl->pos = mtd->writesize % mtk_data_len(mem);
++}
++
++static void mtk_snfi_mac_enable(struct mtk_snfi *snfi)
++{
++ u32 mac;
++
++ mac = readl(snfi->regs + SNFI_MAC_CTL);
++ mac &= ~MAC_SIO_SEL;
++ mac |= MAC_EN;
++
++ writel(mac, snfi->regs + SNFI_MAC_CTL);
++}
++
++static int mtk_snfi_mac_trigger(struct mtk_snfi *snfi)
++{
++ u32 mac, reg;
++ int ret = 0;
++
++ mac = readl(snfi->regs + SNFI_MAC_CTL);
++ mac |= MAC_TRIG;
++ writel(mac, snfi->regs + SNFI_MAC_CTL);
++
++ ret = readl_poll_timeout_atomic(snfi->regs + SNFI_MAC_CTL, reg,
++ reg & MAC_WIP_READY, 10,
++ MTK_TIMEOUT);
++ if (ret < 0) {
++ dev_err(snfi->dev, "polling wip ready for read timeout\n");
++ return -EIO;
++ }
++
++ ret = readl_poll_timeout_atomic(snfi->regs + SNFI_MAC_CTL, reg,
++ !(reg & MAC_WIP), 10,
++ MTK_TIMEOUT);
++ if (ret < 0) {
++ dev_err(snfi->dev, "polling flash update timeout\n");
++ return -EIO;
++ }
++
++ return ret;
++}
++
++static void mtk_snfi_mac_leave(struct mtk_snfi *snfi)
++{
++ u32 mac;
++
++ mac = readl(snfi->regs + SNFI_MAC_CTL);
++ mac &= ~(MAC_TRIG | MAC_EN | MAC_SIO_SEL);
++ writel(mac, snfi->regs + SNFI_MAC_CTL);
++}
++
++static int mtk_snfi_mac_op(struct mtk_snfi *snfi)
++{
++ int ret = 0;
++
++ mtk_snfi_mac_enable(snfi);
++
++ ret = mtk_snfi_mac_trigger(snfi);
++ if (ret)
++ return ret;
++
++ mtk_snfi_mac_leave(snfi);
++
++ return ret;
++}
++
++static irqreturn_t mtk_snfi_irq(int irq, void *id)
++{
++ struct mtk_snfi *snfi = id;
++ u16 sta, ien;
++
++ sta = readw(snfi->regs + NFI_INTR_STA);
++ ien = readw(snfi->regs + NFI_INTR_EN);
++
++ if (!(sta & ien))
++ return IRQ_NONE;
++
++ writew(~sta & ien, snfi->regs + NFI_INTR_EN);
++ complete(&snfi->done);
++
++ return IRQ_HANDLED;
++}
++
++static int mtk_snfi_enable_clk(struct device *dev, struct mtk_snfi_clk *clk)
++{
++ int ret;
++
++ ret = clk_prepare_enable(clk->nfi_clk);
++ if (ret) {
++ dev_err(dev, "failed to enable nfi clk\n");
++ return ret;
++ }
++
++ ret = clk_prepare_enable(clk->spi_clk);
++ if (ret) {
++ dev_err(dev, "failed to enable spi clk\n");
++ clk_disable_unprepare(clk->nfi_clk);
++ return ret;
++ }
++
++ return 0;
++}
++
++static void mtk_snfi_disable_clk(struct mtk_snfi_clk *clk)
++{
++ clk_disable_unprepare(clk->nfi_clk);
++ clk_disable_unprepare(clk->spi_clk);
++}
++
++static int mtk_snfi_reset(struct mtk_snfi *snfi)
++{
++ u32 val;
++ int ret;
++
++ /* SW reset controller */
++ val = readl(snfi->regs + SNFI_MISC_CTL) | SW_RST;
++ writel(val, snfi->regs + SNFI_MISC_CTL);
++
++ ret = readw_poll_timeout(snfi->regs + SNFI_STA_CTL1, val,
++ !(val & SPI_STATE_IDLE), 50,
++ MTK_RESET_TIMEOUT);
++ if (ret) {
++ dev_warn(snfi->dev, "spi state active in reset [0x%x] = 0x%x\n",
++ SNFI_STA_CTL1, val);
++ return ret;
++ }
++
++ val = readl(snfi->regs + SNFI_MISC_CTL);
++ val &= ~SW_RST;
++ writel(val, snfi->regs + SNFI_MISC_CTL);
++
++ /* reset all registers and force the NFI master to terminate */
++ writew(CON_FIFO_FLUSH | CON_NFI_RST, snfi->regs + NFI_CON);
++ ret = readw_poll_timeout(snfi->regs + NFI_STA, val,
++ !(val & (NFI_FSM_MASK | NAND_FSM_MASK)), 50,
++ MTK_RESET_TIMEOUT);
++ if (ret) {
++ dev_warn(snfi->dev, "nfi active in reset [0x%x] = 0x%x\n",
++ NFI_STA, val);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int mtk_snfi_set_spare_per_sector(struct spinand_device *spinand,
++ const struct mtk_snfi_caps *caps,
++ u32 *sps)
++{
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ const u8 *spare = caps->spare_size;
++ u32 sectors, i, closest_spare = 0;
++
++ sectors = mtd->writesize / caps->nand_sec_size;
++ *sps = mtd->oobsize / sectors;
++
++ if (*sps < MTK_SNFC_MIN_SPARE)
++ return -EINVAL;
++
++ for (i = 0; i < caps->num_spare_size; i++) {
++ if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) {
++ closest_spare = i;
++ if (*sps == spare[i])
++ break;
++ }
++ }
++
++ *sps = spare[closest_spare];
++
++ return 0;
++}
++
++static void mtk_snfi_read_fdm_data(struct spi_mem *mem,
++ u32 sectors)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ const struct mtk_snfi_caps *caps = snfi->caps;
++ u32 vall, valm;
++ int i, j;
++ u8 *oobptr;
++
++ for (i = 0; i < sectors; i++) {
++ oobptr = oob_ptr(mem, i);
++ vall = readl(snfi->regs + NFI_FDML(i));
++ valm = readl(snfi->regs + NFI_FDMM(i));
++
++ for (j = 0; j < caps->nand_fdm_size; j++)
++ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
++ }
++}
++
++static void mtk_snfi_write_fdm_data(struct spi_mem *mem,
++ u32 sectors)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ const struct mtk_snfi_caps *caps = snfi->caps;
++ u32 vall, valm;
++ int i, j;
++ u8 *oobptr;
++
++ for (i = 0; i < sectors; i++) {
++ oobptr = oob_ptr(mem, i);
++ vall = 0;
++ valm = 0;
++ for (j = 0; j < 8; j++) {
++ if (j < 4)
++ vall |= (j < caps->nand_fdm_size ? oobptr[j] :
++ 0xff) << (j * 8);
++ else
++ valm |= (j < caps->nand_fdm_size ? oobptr[j] :
++ 0xff) << ((j - 4) * 8);
++ }
++ writel(vall, snfi->regs + NFI_FDML(i));
++ writel(valm, snfi->regs + NFI_FDMM(i));
++ }
++}
++
++static int mtk_snfi_update_ecc_stats(struct spi_mem *mem,
++ u8 *buf, u32 sectors)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtk_ecc_stats stats;
++ int rc, i;
++
++ rc = readl(snfi->regs + NFI_STA) & STA_EMP_PAGE;
++ if (rc) {
++ memset(buf, 0xff, sectors * snfi->caps->nand_sec_size);
++ for (i = 0; i < sectors; i++)
++ memset(spinand->oobbuf, 0xff,
++ snfi->caps->nand_fdm_size);
++ return 0;
++ }
++
++ mtk_ecc_get_stats(snfi->ecc, &stats, sectors);
++ mtd->ecc_stats.corrected += stats.corrected;
++ mtd->ecc_stats.failed += stats.failed;
++
++ return 0;
++}
++
++static int mtk_snfi_hw_runtime_config(struct spi_mem *mem)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ struct nand_device *nand = mtd_to_nanddev(mtd);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ const struct mtk_snfi_caps *caps = snfi->caps;
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 fmt, spare, i = 0;
++ int ret;
++
++ ret = mtk_snfi_set_spare_per_sector(spinand, caps, &spare);
++ if (ret)
++ return ret;
++
++ /* calculate usable oob bytes for ecc parity data */
++ snfi_nand->spare_per_sector = spare;
++ spare -= caps->nand_fdm_size;
++
++ nand->memorg.oobsize = snfi_nand->spare_per_sector
++ * (mtd->writesize / caps->nand_sec_size);
++ mtd->oobsize = nanddev_per_page_oobsize(nand);
++
++ snfi->ecc_cfg.strength = (spare << 3) / caps->ecc_parity_bits;
++ mtk_ecc_adjust_strength(snfi->ecc, &snfi->ecc_cfg.strength);
++
++ switch (mtd->writesize) {
++ case 512:
++ fmt = PAGEFMT_512;
++ break;
++ case KB(2):
++ fmt = PAGEFMT_2K;
++ break;
++ case KB(4):
++ fmt = PAGEFMT_4K;
++ break;
++ default:
++ dev_err(snfi->dev, "invalid page len: %d\n", mtd->writesize);
++ return -EINVAL;
++ }
++
++ /* Setup PageFormat */
++ while (caps->spare_size[i] != snfi_nand->spare_per_sector) {
++ i++;
++ if (i == (caps->num_spare_size - 1)) {
++ dev_err(snfi->dev, "invalid spare size %d\n",
++ snfi_nand->spare_per_sector);
++ return -EINVAL;
++ }
++ }
++
++ fmt |= i << caps->pageformat_spare_shift;
++ fmt |= caps->nand_fdm_size << PAGEFMT_FDM_SHIFT;
++ fmt |= caps->nand_fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT;
++ writel(fmt, snfi->regs + NFI_PAGEFMT);
++
++ snfi->ecc_cfg.len = caps->nand_sec_size + caps->nand_fdm_ecc_size;
++
++ mtk_snfi_set_bad_mark_ctl(&snfi_nand->bad_mark, mem);
++
++ return 0;
++}
++
++static int mtk_snfi_read_from_cache(struct spi_mem *mem,
++ const struct spi_mem_op *op, int oob_on)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ u32 sectors = mtd->writesize / snfi->caps->nand_sec_size;
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 reg, len, col_addr = 0;
++ int dummy_cycle, ret;
++ dma_addr_t dma_addr;
++
++ len = sectors * (snfi->caps->nand_sec_size
++ + snfi_nand->spare_per_sector);
++
++ dma_addr = dma_map_single(snfi->dev, snfi->buffer,
++ len, DMA_FROM_DEVICE);
++ ret = dma_mapping_error(snfi->dev, dma_addr);
++ if (ret) {
++ dev_err(snfi->dev, "dma mapping error\n");
++ return -EINVAL;
++ }
++
++ /* set Read cache command and dummy cycle */
++ dummy_cycle = (op->dummy.nbytes << 3) >> (ffs(op->dummy.buswidth) - 1);
++ reg = ((op->cmd.opcode & RD_CMD_MASK) |
++ (dummy_cycle << RD_DUMMY_SHIFT));
++ writel(reg, snfi->regs + SNFI_RD_CTL2);
++
++ writel((col_addr & RD_ADDR_MASK), snfi->regs + SNFI_RD_CTL3);
++
++ reg = readl(snfi->regs + SNFI_MISC_CTL);
++ reg |= RD_CUSTOM_EN;
++ reg &= ~(RD_MODE_MASK | WR_X4_EN);
++
++ /* set data and addr buswidth */
++ if (op->data.buswidth == 4)
++ reg |= RD_MODE_X4;
++ else if (op->data.buswidth == 2)
++ reg |= RD_MODE_X2;
++
++ if (op->addr.buswidth == 4 || op->addr.buswidth == 2)
++ reg |= RD_QDUAL_IO;
++ writel(reg, snfi->regs + SNFI_MISC_CTL);
++
++ writel(len, snfi->regs + SNFI_MISC_CTL2);
++ writew(sectors << CON_SEC_SHIFT, snfi->regs + NFI_CON);
++ reg = readw(snfi->regs + NFI_CNFG);
++ reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_DMA | CNFG_OP_CUST;
++
++ if (!oob_on) {
++ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
++ writew(reg, snfi->regs + NFI_CNFG);
++
++ snfi->ecc_cfg.mode = ECC_NFI_MODE;
++ snfi->ecc_cfg.sectors = sectors;
++ snfi->ecc_cfg.op = ECC_DECODE;
++ ret = mtk_ecc_enable(snfi->ecc, &snfi->ecc_cfg);
++ if (ret) {
++ dev_err(snfi->dev, "ecc enable failed\n");
++ /* clear NFI_CNFG */
++ reg &= ~(CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_DMA |
++ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++ writew(reg, snfi->regs + NFI_CNFG);
++ goto out;
++ }
++ } else {
++ writew(reg, snfi->regs + NFI_CNFG);
++ }
++
++ writel(lower_32_bits(dma_addr), snfi->regs + NFI_STRADDR);
++ readw(snfi->regs + NFI_INTR_STA);
++ writew(INTR_AHB_DONE_EN, snfi->regs + NFI_INTR_EN);
++
++ init_completion(&snfi->done);
++
++ /* set dummy command to trigger NFI enter SPI mode */
++ writew(NAND_CMD_DUMMYREAD, snfi->regs + NFI_CMD);
++ reg = readl(snfi->regs + NFI_CON) | CON_BRD;
++ writew(reg, snfi->regs + NFI_CON);
++
++ ret = wait_for_completion_timeout(&snfi->done, msecs_to_jiffies(500));
++ if (!ret) {
++ dev_err(snfi->dev, "read ahb done timeout\n");
++ writew(0, snfi->regs + NFI_INTR_EN);
++ ret = -ETIMEDOUT;
++ goto out;
++ }
++
++ ret = readl_poll_timeout_atomic(snfi->regs + NFI_BYTELEN, reg,
++ ADDRCNTR_SEC(reg) >= sectors, 10,
++ MTK_TIMEOUT);
++ if (ret < 0) {
++ dev_err(snfi->dev, "polling read byte len timeout\n");
++ ret = -EIO;
++ } else {
++ if (!oob_on) {
++ ret = mtk_ecc_wait_done(snfi->ecc, ECC_DECODE);
++ if (ret) {
++ dev_warn(snfi->dev, "wait ecc done timeout\n");
++ } else {
++ mtk_snfi_update_ecc_stats(mem, snfi->buffer,
++ sectors);
++ mtk_snfi_read_fdm_data(mem, sectors);
++ }
++ }
++ }
++
++ if (oob_on)
++ goto out;
++
++ mtk_ecc_disable(snfi->ecc);
++out:
++ dma_unmap_single(snfi->dev, dma_addr, len, DMA_FROM_DEVICE);
++ writel(0, snfi->regs + NFI_CON);
++ writel(0, snfi->regs + NFI_CNFG);
++ reg = readl(snfi->regs + SNFI_MISC_CTL);
++ reg &= ~RD_CUSTOM_EN;
++ writel(reg, snfi->regs + SNFI_MISC_CTL);
++
++ return ret;
++}
++
++static int mtk_snfi_write_to_cache(struct spi_mem *mem,
++ const struct spi_mem_op *op,
++ int oob_on)
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ u32 sectors = mtd->writesize / snfi->caps->nand_sec_size;
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 reg, len, col_addr = 0;
++ dma_addr_t dma_addr;
++ int ret;
++
++ len = sectors * (snfi->caps->nand_sec_size
++ + snfi_nand->spare_per_sector);
++
++ dma_addr = dma_map_single(snfi->dev, snfi->buffer, len,
++ DMA_TO_DEVICE);
++ ret = dma_mapping_error(snfi->dev, dma_addr);
++ if (ret) {
++ dev_err(snfi->dev, "dma mapping error\n");
++ return -EINVAL;
++ }
++
++ /* set program load cmd and address */
++ reg = (op->cmd.opcode << WR_LOAD_CMD_SHIFT);
++ writel(reg, snfi->regs + SNFI_PG_CTL1);
++ writel(col_addr & WR_LOAD_ADDR_MASK, snfi->regs + SNFI_PG_CTL2);
++
++ reg = readl(snfi->regs + SNFI_MISC_CTL);
++ reg |= WR_CUSTOM_EN;
++ reg &= ~(RD_MODE_MASK | WR_X4_EN);
++
++ if (op->data.buswidth == 4)
++ reg |= WR_X4_EN;
++ writel(reg, snfi->regs + SNFI_MISC_CTL);
++
++ writel(len << WR_LEN_SHIFT, snfi->regs + SNFI_MISC_CTL2);
++ writew(sectors << CON_SEC_SHIFT, snfi->regs + NFI_CON);
++
++ reg = readw(snfi->regs + NFI_CNFG);
++ reg &= ~(CNFG_READ_EN | CNFG_BYTE_RW);
++ reg |= CNFG_DMA | CNFG_DMA_BURST_EN | CNFG_OP_PROGRAM;
++
++ if (!oob_on) {
++ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
++ writew(reg, snfi->regs + NFI_CNFG);
++
++ snfi->ecc_cfg.mode = ECC_NFI_MODE;
++ snfi->ecc_cfg.op = ECC_ENCODE;
++ ret = mtk_ecc_enable(snfi->ecc, &snfi->ecc_cfg);
++ if (ret) {
++ dev_err(snfi->dev, "ecc enable failed\n");
++ /* clear NFI_CNFG */
++ reg &= ~(CNFG_DMA_BURST_EN | CNFG_DMA |
++ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
++ writew(reg, snfi->regs + NFI_CNFG);
++ dma_unmap_single(snfi->dev, dma_addr, len,
++ DMA_FROM_DEVICE);
++ goto out;
++ }
++ /* write OOB into the FDM registers (OOB area in MTK NAND) */
++ mtk_snfi_write_fdm_data(mem, sectors);
++ } else {
++ writew(reg, snfi->regs + NFI_CNFG);
++ }
++ writel(lower_32_bits(dma_addr), snfi->regs + NFI_STRADDR);
++ readw(snfi->regs + NFI_INTR_STA);
++ writew(INTR_AHB_DONE_EN, snfi->regs + NFI_INTR_EN);
++
++ init_completion(&snfi->done);
++
++ /* set dummy command to trigger NFI enter SPI mode */
++ writew(NAND_CMD_DUMMYPROG, snfi->regs + NFI_CMD);
++ reg = readl(snfi->regs + NFI_CON) | CON_BWR;
++ writew(reg, snfi->regs + NFI_CON);
++
++ ret = wait_for_completion_timeout(&snfi->done, msecs_to_jiffies(500));
++ if (!ret) {
++ dev_err(snfi->dev, "custom program done timeout\n");
++ writew(0, snfi->regs + NFI_INTR_EN);
++ ret = -ETIMEDOUT;
++ goto ecc_disable;
++ }
++
++ ret = readl_poll_timeout_atomic(snfi->regs + NFI_ADDRCNTR, reg,
++ ADDRCNTR_SEC(reg) >= sectors,
++ 10, MTK_TIMEOUT);
++ if (ret)
++ dev_err(snfi->dev, "hwecc write timeout\n");
++
++ecc_disable:
++ mtk_ecc_disable(snfi->ecc);
++
++out:
++ dma_unmap_single(snfi->dev, dma_addr, len, DMA_TO_DEVICE);
++ writel(0, snfi->regs + NFI_CON);
++ writel(0, snfi->regs + NFI_CNFG);
++ reg = readl(snfi->regs + SNFI_MISC_CTL);
++ reg &= ~WR_CUSTOM_EN;
++ writel(reg, snfi->regs + SNFI_MISC_CTL);
++
++ return ret;
++}
++
++static int mtk_snfi_read(struct spi_mem *mem,
++ const struct spi_mem_op *op)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 col_addr = op->addr.val;
++ int i, ret, sectors, oob_on = false;
++
++ if (col_addr == mtd->writesize)
++ oob_on = true;
++
++ ret = mtk_snfi_read_from_cache(mem, op, oob_on);
++ if (ret) {
++ dev_warn(snfi->dev, "read from cache fail\n");
++ return ret;
++ }
++
++ sectors = mtd->writesize / snfi->caps->nand_sec_size;
++ for (i = 0; i < sectors; i++) {
++ if (oob_on)
++ memcpy(oob_ptr(mem, i),
++ mtk_oob_ptr(mem, snfi->buffer, i),
++ snfi->caps->nand_fdm_size);
++
++ if (i == snfi_nand->bad_mark.sec && snfi->caps->bad_mark_swap)
++ snfi_nand->bad_mark.bm_swap(mem, snfi->buffer,
++ oob_on);
++ }
++
++ if (!oob_on)
++ memcpy(spinand->databuf, snfi->buffer, mtd->writesize);
++
++ return ret;
++}
++
++static int mtk_snfi_write(struct spi_mem *mem,
++ const struct spi_mem_op *op)
++{
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
++ u32 ret, i, sectors, col_addr = op->addr.val;
++ int oob_on = false;
++
++ if (col_addr == mtd->writesize)
++ oob_on = true;
++
++ sectors = mtd->writesize / snfi->caps->nand_sec_size;
++ memset(snfi->buffer, 0xff, mtd->writesize + mtd->oobsize);
++
++ if (!oob_on)
++ memcpy(snfi->buffer, spinand->databuf, mtd->writesize);
++
++ for (i = 0; i < sectors; i++) {
++ if (i == snfi_nand->bad_mark.sec && snfi->caps->bad_mark_swap)
++ snfi_nand->bad_mark.bm_swap(mem, snfi->buffer, oob_on);
++
++ if (oob_on)
++ memcpy(mtk_oob_ptr(mem, snfi->buffer, i),
++ oob_ptr(mem, i),
++ snfi->caps->nand_fdm_size);
++ }
++
++ ret = mtk_snfi_write_to_cache(mem, op, oob_on);
++ if (ret)
++ dev_warn(snfi->dev, "write to cache fail\n");
++
++ return ret;
++}
++
++static int mtk_snfi_command_exec(struct mtk_snfi *snfi,
++ const u8 *txbuf, u8 *rxbuf,
++ const u32 txlen, const u32 rxlen)
++{
++ u32 tmp, i, j, reg, m;
++ u8 *p_tmp = (u8 *)(&tmp);
++ int ret = 0;
++
++ /* Moving tx data to NFI_SPI GPRAM */
++ for (i = 0, m = 0; i < txlen; ) {
++ for (j = 0, tmp = 0; i < txlen && j < 4; i++, j++)
++ p_tmp[j] = txbuf[i];
++
++ writel(tmp, snfi->regs + SNFI_GPRAM_DATA + m);
++ m += 4;
++ }
++
++ writel(txlen, snfi->regs + SNFI_MAC_OUTL);
++ writel(rxlen, snfi->regs + SNFI_MAC_INL);
++ ret = mtk_snfi_mac_op(snfi);
++ if (ret)
++ return ret;
++
++ /* For NULL input data, this loop will be skipped */
++ if (rxlen)
++ for (i = 0, m = 0; i < rxlen; ) {
++ reg = readl(snfi->regs +
++ SNFI_GPRAM_DATA + m);
++ for (j = 0; i < rxlen && j < 4; i++, j++, rxbuf++) {
++ if (m == 0 && i == 0)
++ j = i + txlen;
++ *rxbuf = (reg >> (j * 8)) & 0xFF;
++ }
++ m += 4;
++ }
++
++ return ret;
++}
++
++/*
++ * mtk_snfi_exec_op - to process command/data to send to the
++ * SPI NAND by mtk controller
++ */
++static int mtk_snfi_exec_op(struct spi_mem *mem,
++ const struct spi_mem_op *op)
++
++{
++ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
++ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
++ struct mtd_info *mtd = spinand_to_mtd(spinand);
++ struct nand_device *nand = mtd_to_nanddev(mtd);
++ const struct spi_mem_op *read_cache;
++ const struct spi_mem_op *write_cache;
++ u32 tmpbufsize, txlen = 0, rxlen = 0;
++ u8 *txbuf, *rxbuf = NULL, *buf;
++ int i, ret = 0;
++
++ ret = mtk_snfi_reset(snfi);
++ if (ret) {
++ dev_warn(snfi->dev, "reset spi memory controller fail\n");
++ return ret;
++ }
++
++ /*if bbt initial, framework have detect nand information */
++ if (nand->bbt.cache) {
++ read_cache = spinand->op_templates.read_cache;
++ write_cache = spinand->op_templates.write_cache;
++
++ ret = mtk_snfi_hw_runtime_config(mem);
++ if (ret)
++ return ret;
++
++ /* For Read/Write with cache, Erase use framework flow */
++ if (op->cmd.opcode == read_cache->cmd.opcode) {
++ ret = mtk_snfi_read(mem, op);
++ if (ret)
++ dev_warn(snfi->dev, "snfi read fail\n");
++ return ret;
++ } else if (op->cmd.opcode == write_cache->cmd.opcode) {
++ ret = mtk_snfi_write(mem, op);
++ if (ret)
++ dev_warn(snfi->dev, "snfi write fail\n");
++ return ret;
++ }
++ }
++
++ tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
++ op->dummy.nbytes + op->data.nbytes;
++
++ txbuf = kzalloc(tmpbufsize, GFP_KERNEL);
++ if (!txbuf)
++ return -ENOMEM;
++
++ txbuf[txlen++] = op->cmd.opcode;
++
++ if (op->addr.nbytes)
++ for (i = 0; i < op->addr.nbytes; i++)
++ txbuf[txlen++] = op->addr.val >>
++ (8 * (op->addr.nbytes - i - 1));
++
++ txlen += op->dummy.nbytes;
++
++ if (op->data.dir == SPI_MEM_DATA_OUT)
++ for (i = 0; i < op->data.nbytes; i++) {
++ buf = (u8 *)op->data.buf.out;
++ txbuf[txlen++] = buf[i];
++ }
++
++ if (op->data.dir == SPI_MEM_DATA_IN) {
++ rxbuf = (u8 *)op->data.buf.in;
++ rxlen += op->data.nbytes;
++ }
++
++ ret = mtk_snfi_command_exec(snfi, txbuf, rxbuf, txlen, rxlen);
++ kfree(txbuf);
++
++ return ret;
++}
++
++static int mtk_snfi_init(struct mtk_snfi *snfi)
++{
++ int ret;
++
++ /* Reset the state machine and data FIFO */
++ ret = mtk_snfi_reset(snfi);
++ if (ret) {
++ dev_warn(snfi->dev, "MTK reset controller fail\n");
++ return ret;
++ }
++
++ snfi->buffer = devm_kzalloc(snfi->dev, 4096 + 256, GFP_KERNEL);
++ if (!snfi->buffer)
++ return -ENOMEM;
++
++ /* Clear interrupt, read clear. */
++ readw(snfi->regs + NFI_INTR_STA);
++ writew(0, snfi->regs + NFI_INTR_EN);
++
++ writel(0, snfi->regs + NFI_CON);
++ writel(0, snfi->regs + NFI_CNFG);
++
++ /* Change to NFI_SPI mode. */
++ writel(SNFI_MODE_EN, snfi->regs + SNFI_CNFG);
++
++ return 0;
++}
++
++static int mtk_snfi_check_buswidth(u8 width)
++{
++ switch (width) {
++ case 1:
++ case 2:
++ case 4:
++ return 0;
++
++ default:
++ break;
++ }
++
++ return -ENOTSUPP;
++}
++
++static bool mtk_snfi_supports_op(struct spi_mem *mem,
++ const struct spi_mem_op *op)
++{
++ int ret = 0;
++
++ /* For MTK Spi Nand controller, cmd buswidth just support 1 bit*/
++ if (op->cmd.buswidth != 1)
++ ret = -ENOTSUPP;
++
++ if (op->addr.nbytes)
++ ret |= mtk_snfi_check_buswidth(op->addr.buswidth);
++
++ if (op->dummy.nbytes)
++ ret |= mtk_snfi_check_buswidth(op->dummy.buswidth);
++
++ if (op->data.nbytes)
++ ret |= mtk_snfi_check_buswidth(op->data.buswidth);
++
++ if (ret)
++ return false;
++
++ return true;
++}
++
++static const struct spi_controller_mem_ops mtk_snfi_ops = {
++ .supports_op = mtk_snfi_supports_op,
++ .exec_op = mtk_snfi_exec_op,
++};
++
++static const struct mtk_snfi_caps snfi_mt7622 = {
++ .spare_size = spare_size_mt7622,
++ .num_spare_size = 4,
++ .nand_sec_size = 512,
++ .nand_fdm_size = 8,
++ .nand_fdm_ecc_size = 1,
++ .ecc_parity_bits = 13,
++ .pageformat_spare_shift = 4,
++ .bad_mark_swap = 0,
++};
++
++static const struct of_device_id mtk_snfi_id_table[] = {
++ { .compatible = "mediatek,mt7622-snfi", .data = &snfi_mt7622, },
++ { /* sentinel */ }
++};
++
++static int mtk_snfi_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct spi_controller *ctlr;
++ struct mtk_snfi *snfi;
++ struct resource *res;
++ int ret = 0, irq;
++
++ ctlr = spi_alloc_master(&pdev->dev, sizeof(*snfi));
++ if (!ctlr)
++ return -ENOMEM;
++
++ snfi = spi_controller_get_devdata(ctlr);
++ snfi->caps = of_device_get_match_data(dev);
++ snfi->dev = dev;
++
++ snfi->ecc = of_mtk_ecc_get(np);
++ if (IS_ERR_OR_NULL(snfi->ecc))
++ goto err_put_master;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ snfi->regs = devm_ioremap_resource(dev, res);
++ if (IS_ERR(snfi->regs)) {
++ ret = PTR_ERR(snfi->regs);
++ goto release_ecc;
++ }
++
++ /* find the clocks */
++ snfi->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
++ if (IS_ERR(snfi->clk.nfi_clk)) {
++ dev_err(dev, "no nfi clk\n");
++ ret = PTR_ERR(snfi->clk.nfi_clk);
++ goto release_ecc;
++ }
++
++ snfi->clk.spi_clk = devm_clk_get(dev, "spi_clk");
++ if (IS_ERR(snfi->clk.spi_clk)) {
++ dev_err(dev, "no spi clk\n");
++ ret = PTR_ERR(snfi->clk.spi_clk);
++ goto release_ecc;
++ }
++
++ ret = mtk_snfi_enable_clk(dev, &snfi->clk);
++ if (ret)
++ goto release_ecc;
++
++ /* find the irq */
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(dev, "no snfi irq resource\n");
++ ret = -EINVAL;
++ goto clk_disable;
++ }
++
++ ret = devm_request_irq(dev, irq, mtk_snfi_irq, 0, "mtk-snfi", snfi);
++ if (ret) {
++ dev_err(dev, "failed to request snfi irq\n");
++ goto clk_disable;
++ }
++
++ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
++ if (ret) {
++ dev_err(dev, "failed to set dma mask\n");
++ goto clk_disable;
++ }
++
++ ctlr->dev.of_node = np;
++ ctlr->mem_ops = &mtk_snfi_ops;
++
++ platform_set_drvdata(pdev, snfi);
++ ret = mtk_snfi_init(snfi);
++ if (ret) {
++ dev_err(dev, "failed to init snfi\n");
++ goto clk_disable;
++ }
++
++ ret = devm_spi_register_master(dev, ctlr);
++ if (ret)
++ goto clk_disable;
++
++ return 0;
++
++clk_disable:
++ mtk_snfi_disable_clk(&snfi->clk);
++
++release_ecc:
++ mtk_ecc_release(snfi->ecc);
++
++err_put_master:
++ spi_master_put(ctlr);
++
++ dev_err(dev, "MediaTek SPI NAND interface probe failed %d\n", ret);
++ return ret;
++}
++
++static int mtk_snfi_remove(struct platform_device *pdev)
++{
++ struct mtk_snfi *snfi = platform_get_drvdata(pdev);
++
++ mtk_snfi_disable_clk(&snfi->clk);
++
++ return 0;
++}
++
++static int mtk_snfi_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct mtk_snfi *snfi = platform_get_drvdata(pdev);
++
++ mtk_snfi_disable_clk(&snfi->clk);
++
++ return 0;
++}
++
++static int mtk_snfi_resume(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct mtk_snfi *snfi = dev_get_drvdata(dev);
++ int ret;
++
++ ret = mtk_snfi_enable_clk(dev, &snfi->clk);
++ if (ret)
++ return ret;
++
++ ret = mtk_snfi_init(snfi);
++ if (ret)
++ dev_err(dev, "failed to init snfi controller\n");
++
++ return ret;
++}
++
++static struct platform_driver mtk_snfi_driver = {
++ .driver = {
++ .name = "mtk-snfi",
++ .of_match_table = mtk_snfi_id_table,
++ },
++ .probe = mtk_snfi_probe,
++ .remove = mtk_snfi_remove,
++ .suspend = mtk_snfi_suspend,
++ .resume = mtk_snfi_resume,
++};
++
++module_platform_driver(mtk_snfi_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Xiangsheng Hou <xiangsheng.hou@mediatek.com>");
++MODULE_DESCRIPTION("Mediatek SPI Memory Interface Driver");
+Index: linux-4.19.48/drivers/spi/Kconfig
+===================================================================
+--- linux-4.19.48.orig/drivers/spi/Kconfig
++++ linux-4.19.48/drivers/spi/Kconfig
+@@ -389,6 +389,15 @@ config SPI_MT65XX
+ say Y or M here.If you are not sure, say N.
+ SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
+
++config SPI_MTK_SNFI
++ tristate "MediaTek SPI NAND interface"
++ select MTD_SPI_NAND
++ help
++ This selects the SPI NAND FLASH interface(SNFI),
++ which could be found on MediaTek Soc.
++ Say Y or M here.If you are not sure, say N.
++ Note Parallel Nand and SPI NAND is alternative on MediaTek SoCs.
++
+ config SPI_NUC900
+ tristate "Nuvoton NUC900 series SPI"
+ depends on ARCH_W90X900
+Index: linux-4.19.48/drivers/spi/Makefile
+===================================================================
+--- linux-4.19.48.orig/drivers/spi/Makefile
++++ linux-4.19.48/drivers/spi/Makefile
+@@ -57,6 +57,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mp
+ obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
+ obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
+ obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
++obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o
+ obj-$(CONFIG_SPI_MXS) += spi-mxs.o
+ obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
+ obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
diff --git a/target/linux/mediatek/patches-4.19/0900-bt-mtk-serial-fix.patch b/target/linux/mediatek/patches-4.19/0900-bt-mtk-serial-fix.patch
new file mode 100755
index 0000000000..ae1ef16a79
--- /dev/null
+++ b/target/linux/mediatek/patches-4.19/0900-bt-mtk-serial-fix.patch
@@ -0,0 +1,37 @@
+diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
+index ebfb0bd..2b9ba39 100644
+--- a/drivers/tty/serial/8250/8250.h
++++ b/drivers/tty/serial/8250/8250.h
+@@ -80,6 +80,7 @@ struct serial8250_config {
+ #define UART_CAP_MINI (1 << 17) /* Mini UART on BCM283X family lacks:
+ * STOP PARITY EPAR SPAR WLEN5 WLEN6
+ */
++#define UART_CAP_NMOD (1 << 18) /* UART doesn't do termios */
+
+ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
+ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
+diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
+index c39482b..e4a69a0 100644
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -297,7 +297,7 @@
+ .tx_loadsz = 16,
+ .fcr = UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+- .flags = UART_CAP_FIFO,
++ .flags = UART_CAP_FIFO | UART_CAP_NMOD,
+ },
+ [PORT_NPCM] = {
+ .name = "Nuvoton 16550",
+@@ -2663,6 +2663,11 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port,
+ unsigned long flags;
+ unsigned int baud, quot, frac = 0;
+
++ if (up->capabilities & UART_CAP_NMOD) {
++ termios->c_cflag = 0;
++ return;
++ }
++
+ if (up->capabilities & UART_CAP_MINI) {
+ termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR);
+ if ((termios->c_cflag & CSIZE) == CS5 ||