diff --git a/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch b/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch index 68170b6614b8e2..0185bed089774d 100644 --- a/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch +++ b/target/linux/generic/pending-5.15/737-07-net-ethernet-mtk_eth_soc-add-paths-and-SerDes-modes-.patch @@ -1,23 +1,40 @@ -From 20ac14fedba025b6b336a821ea60660afe2d46cd Mon Sep 17 00:00:00 2001 +From 3d833ad2cfc1ab503d9aae2967b7f10811bb3c9c Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Wed, 1 Mar 2023 11:56:04 +0000 -Subject: [PATCH 7/7] net: ethernet: mtk_eth_soc: add paths and SerDes modes +Subject: [PATCH 7/7] net: ethernet: mtk_eth_soc: add paths and SerDes modes for MT7988 -MT7988 comes with a built-in 2.5G PHY as well as USXGMII or 10Base-KR -compatible SerDes lanes for external PHYs. +MT7988 comes with a built-in 2.5G PHY as well as +USXGMII/10GBase-KR/5GBase-KR compatible SerDes lanes for external PHYs. Add support for configuring the MAC and SerDes parts for the new paths. Signed-off-by: Daniel Golle --- - drivers/net/ethernet/mediatek/Makefile | 2 +- - drivers/net/ethernet/mediatek/mtk_eth_path.c | 154 ++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 291 +++++++- - drivers/net/ethernet/mediatek/mtk_eth_soc.h | 162 ++++- - drivers/net/ethernet/mediatek/mtk_usxgmii.c | 659 +++++++++++++++++++ - 5 files changed, 1236 insertions(+), 32 deletions(-) + drivers/net/ethernet/mediatek/Kconfig | 7 + + drivers/net/ethernet/mediatek/Makefile | 1 + + drivers/net/ethernet/mediatek/mtk_eth_path.c | 154 +++- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 270 +++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 194 ++++- + drivers/net/ethernet/mediatek/mtk_usxgmii.c | 835 +++++++++++++++++++ + 6 files changed, 1428 insertions(+), 33 deletions(-) create mode 100644 drivers/net/ethernet/mediatek/mtk_usxgmii.c +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -24,6 +24,13 @@ config NET_MEDIATEK_SOC + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. + ++config NET_MEDIATEK_SOC_USXGMII ++ bool "Support USXGMII SerDes on MT7988" ++ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST ++ def_bool NET_MEDIATEK_SOC != n ++ help ++ Include support for 10G SerDes which can be found on MT7988. ++ + config NET_MEDIATEK_STAR_EMAC + tristate "MediaTek STAR Ethernet MAC support" + select PHYLIB --- a/drivers/net/ethernet/mediatek/Makefile +++ b/drivers/net/ethernet/mediatek/Makefile @@ -5,6 +5,7 @@ @@ -291,7 +308,20 @@ Signed-off-by: Daniel Golle static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { -@@ -462,7 +479,7 @@ static void mtk_mac_config(struct phylin +@@ -451,6 +468,12 @@ static struct phylink_pcs *mtk_mac_selec + 0 : mac->id; + + return eth->sgmii_pcs[sid]; ++ } else if ((interface == PHY_INTERFACE_MODE_USXGMII || ++ interface == PHY_INTERFACE_MODE_10GKR || ++ interface == PHY_INTERFACE_MODE_5GBASER) && ++ MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3) && ++ mac->id != MTK_GMAC1_ID) { ++ return mtk_usxgmii_select_pcs(eth, mac->id); + } + + return NULL; +@@ -462,7 +485,7 @@ static void mtk_mac_config(struct phylin struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); struct mtk_eth *eth = mac->hw; @@ -300,7 +330,7 @@ Signed-off-by: Daniel Golle u32 i; /* MT76x8 has no hardware settings between for the MAC */ -@@ -506,6 +523,23 @@ static void mtk_mac_config(struct phylin +@@ -506,6 +529,23 @@ static void mtk_mac_config(struct phylin goto init_err; } break; @@ -324,7 +354,7 @@ Signed-off-by: Daniel Golle default: goto err_phy; } -@@ -584,14 +618,92 @@ static void mtk_mac_config(struct phylin +@@ -584,14 +624,78 @@ static void mtk_mac_config(struct phylin SYSCFG0_SGMII_MASK, ~(u32)SYSCFG0_SGMII_MASK); @@ -338,26 +368,14 @@ Signed-off-by: Daniel Golle + } /* Save the syscfg0 value for mac_finish */ mac->syscfg0 = val; -+ } else if (state->interface == PHY_INTERFACE_MODE_USXGMII || -+ state->interface == PHY_INTERFACE_MODE_10GKR || -+ state->interface == PHY_INTERFACE_MODE_5GBASER) { -+ -+ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { -+ err = -EINVAL; -+ goto init_err; -+ } -+ if (phylink_autoneg_inband(mode)) -+ err = mtk_usxgmii_setup_mode_force(eth, mac->id, -+ state); -+ else -+ err = mtk_usxgmii_setup_mode_an(eth, mac->id, -+ SPEED_10000); -+ -+ if (err) -+ goto init_err; - } else if (phylink_autoneg_inband(mode)) { +- } else if (phylink_autoneg_inband(mode)) { ++ } else if (state->interface != PHY_INTERFACE_MODE_USXGMII && ++ state->interface != PHY_INTERFACE_MODE_10GKR && ++ state->interface != PHY_INTERFACE_MODE_5GBASER && ++ phylink_autoneg_inband(mode)) { dev_err(eth->dev, - "In-band mode not supported in non SGMII mode!\n"); +- "In-band mode not supported in non SGMII mode!\n"); ++ "In-band mode not supported in non-SerDes modes!\n"); return; } @@ -417,7 +435,7 @@ Signed-off-by: Daniel Golle return; err_phy: -@@ -632,11 +744,37 @@ static int mtk_mac_finish(struct phylink +@@ -632,11 +736,40 @@ static int mtk_mac_finish(struct phylink return 0; } @@ -430,7 +448,12 @@ Signed-off-by: Daniel Golle + if (mac->id == MTK_GMAC2_ID) + sts = sts >> 16; + -+ state->duplex = 1; ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); ++ if (!state->link) ++ return; ++ ++ state->duplex = DUPLEX_FULL; ++ state->interface = mac->interface; + + switch (FIELD_GET(MTK_USXGMII_PCS_MODE, sts)) { + case 0: @@ -446,8 +469,6 @@ Signed-off-by: Daniel Golle + state->speed = SPEED_1000; + break; + } -+ -+ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, sts); +} + +static void mtk_gdm_pcs_get_state(struct mtk_mac *mac, @@ -458,7 +479,7 @@ Signed-off-by: Daniel Golle u32 pmsr = mtk_r32(mac->hw, MTK_MAC_MSR(mac->id)); state->link = (pmsr & MAC_MSR_LINK); -@@ -664,15 +802,35 @@ static void mtk_mac_pcs_get_state(struct +@@ -664,15 +797,35 @@ static void mtk_mac_pcs_get_state(struct state->pause |= MLO_PAUSE_TX; } @@ -497,7 +518,7 @@ Signed-off-by: Daniel Golle } static void mtk_set_queue_speed(struct mtk_eth *eth, unsigned int idx, -@@ -744,13 +902,11 @@ static void mtk_set_queue_speed(struct m +@@ -744,13 +897,11 @@ static void mtk_set_queue_speed(struct m mtk_w32(eth, val, soc->reg_map->qdma.qtx_sch + ofs); } @@ -515,7 +536,7 @@ Signed-off-by: Daniel Golle u32 mcr; mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); -@@ -784,6 +940,47 @@ static void mtk_mac_link_up(struct phyli +@@ -784,6 +935,47 @@ static void mtk_mac_link_up(struct phyli mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } @@ -563,7 +584,7 @@ Signed-off-by: Daniel Golle static const struct phylink_mac_ops mtk_phylink_ops = { .validate = phylink_generic_validate, .mac_select_pcs = mtk_mac_select_pcs, -@@ -836,10 +1033,21 @@ static int mtk_mdio_init(struct mtk_eth +@@ -836,10 +1028,21 @@ static int mtk_mdio_init(struct mtk_eth } divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); @@ -586,7 +607,7 @@ Signed-off-by: Daniel Golle mtk_w32(eth, val, MTK_PPSC); dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); -@@ -4433,8 +4641,8 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4433,8 +4636,8 @@ static int mtk_add_mac(struct mtk_eth *e const __be32 *_id = of_get_property(np, "reg", NULL); phy_interface_t phy_mode; struct phylink *phylink; @@ -596,7 +617,7 @@ Signed-off-by: Daniel Golle int txqs = 1; if (!_id) { -@@ -4525,6 +4733,32 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4525,6 +4728,32 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.supported_interfaces); } @@ -629,40 +650,20 @@ Signed-off-by: Daniel Golle phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(mac->of_node), phy_mode, &mtk_phylink_ops); -@@ -4714,6 +4948,33 @@ static int mtk_probe(struct platform_dev - return err; - } +@@ -4712,6 +4941,13 @@ static int mtk_probe(struct platform_dev + if (err) + return err; ++ } ++ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_USXGMII)) { -+ eth->xgmii = devm_kzalloc(eth->dev, sizeof(*eth->xgmii), GFP_KERNEL); + err = mtk_usxgmii_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "usxgmii init failed\n"); -+ return err; -+ } + -+ err = mtk_xfi_pextp_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "pextp init failed\n"); -+ return err; -+ } -+ -+ err = mtk_xfi_pll_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "xfi pll init failed\n"); -+ return err; -+ } -+ -+ err = mtk_toprgu_init(eth); -+ if (err) { -+ dev_err(&pdev->dev, "toprgu init failed\n"); ++ if (err) + return err; -+ } -+ } -+ + } + if (eth->soc->required_pctl) { - eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "mediatek,pctl"); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -126,6 +126,11 @@ @@ -743,34 +744,54 @@ Signed-off-by: Daniel Golle /* ethernet subsystem clock register */ -@@ -506,16 +548,69 @@ +@@ -506,16 +548,91 @@ #define ETHSYS_DMA_AG_MAP_QDMA BIT(1) #define ETHSYS_DMA_AG_MAP_PPE BIT(2) +/* USXGMII subsystem config registers */ +/* Register to control speed */ +#define RG_PHY_TOP_SPEED_CTRL1 0x80C -+#define RG_USXGMII_RATE_UPDATE_MODE BIT(31) -+#define RG_MAC_CK_GATED BIT(29) -+#define RG_IF_FORCE_EN BIT(28) -+#define RG_RATE_ADAPT_MODE GENMASK(10, 8) -+#define RG_RATE_ADAPT_MODE_X1 0 -+#define RG_RATE_ADAPT_MODE_X2 1 -+#define RG_RATE_ADAPT_MODE_X4 2 -+#define RG_RATE_ADAPT_MODE_X10 3 -+#define RG_RATE_ADAPT_MODE_X100 4 -+#define RG_RATE_ADAPT_MODE_X5 5 -+#define RG_RATE_ADAPT_MODE_X50 6 -+#define RG_XFI_RX_MODE GENMASK(6, 4) -+#define RG_XFI_RX_MODE_10G 0 -+#define RG_XFI_RX_MODE_5G 1 -+#define RG_XFI_TX_MODE GENMASK(2, 0) -+#define RG_XFI_TX_MODE_10G 0 -+#define RG_XFI_TX_MODE_5G 1 ++#define USXGMII_RATE_UPDATE_MODE BIT(31) ++#define USXGMII_MAC_CK_GATED BIT(29) ++#define USXGMII_IF_FORCE_EN BIT(28) ++#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8) ++#define USXGMII_RATE_ADAPT_MODE_X1 0 ++#define USXGMII_RATE_ADAPT_MODE_X2 1 ++#define USXGMII_RATE_ADAPT_MODE_X4 2 ++#define USXGMII_RATE_ADAPT_MODE_X10 3 ++#define USXGMII_RATE_ADAPT_MODE_X100 4 ++#define USXGMII_RATE_ADAPT_MODE_X5 5 ++#define USXGMII_RATE_ADAPT_MODE_X50 6 ++#define USXGMII_XFI_RX_MODE GENMASK(6, 4) ++#define USXGMII_XFI_RX_MODE_10G 0 ++#define USXGMII_XFI_RX_MODE_5G 1 ++#define USXGMII_XFI_TX_MODE GENMASK(2, 0) ++#define USXGMII_XFI_TX_MODE_10G 0 ++#define USXGMII_XFI_TX_MODE_5G 1 + +/* Register to control PCS AN */ +#define RG_PCS_AN_CTRL0 0x810 -+#define RG_AN_ENABLE BIT(0) ++#define USXGMII_AN_RESTART BIT(31) ++#define USXGMII_AN_SYNC_CNT GENMASK(30, 11) ++#define USXGMII_AN_ENABLE BIT(0) ++ ++#define RG_PCS_AN_CTRL2 0x818 ++#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20) ++#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10) ++#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0) ++ ++/* Register to read PCS AN status */ ++#define RG_PCS_AN_STS0 0x81c ++#define USXGMII_LPA_SPEED_MASK GENMASK(11, 9) ++#define USXGMII_LPA_SPEED_10 0 ++#define USXGMII_LPA_SPEED_100 1 ++#define USXGMII_LPA_SPEED_1000 2 ++#define USXGMII_LPA_SPEED_10000 3 ++#define USXGMII_LPA_SPEED_2500 4 ++#define USXGMII_LPA_SPEED_5000 5 ++#define USXGMII_LPA_DUPLEX BIT(12) ++#define USXGMII_LPA_LINK BIT(15) ++#define USXGMII_LPA_LATCH BIT(31) + +/* Register to control USXGMII XFI PLL digital */ +#define XFI_PLL_DIG_GLB8 0x08 @@ -791,6 +812,8 @@ Signed-off-by: Daniel Golle +#define SWSYSRST_XFI_PLL_GRST BIT(16) +#define SWSYSRST_XFI_PEXPT1_GRST BIT(15) +#define SWSYSRST_XFI_PEXPT0_GRST BIT(14) ++#define SWSYSRST_XFI1_GRST BIT(13) ++#define SWSYSRST_XFI0_GRST BIT(12) +#define SWSYSRST_SGMII1_GRST BIT(2) +#define SWSYSRST_SGMII0_GRST BIT(1) +#define TOPRGU_SWSYSRST_EN 0xFC @@ -813,7 +836,7 @@ Signed-off-by: Daniel Golle /* MT7628/88 specific stuff */ #define MT7628_PDMA_OFFSET 0x0800 #define MT7628_SDM_OFFSET 0x0c00 -@@ -809,13 +904,6 @@ enum mtk_gmac_id { +@@ -809,13 +926,6 @@ enum mtk_gmac_id { MTK_GMAC_ID_MAX }; @@ -827,7 +850,7 @@ Signed-off-by: Daniel Golle enum mtk_tx_buf_type { MTK_TYPE_SKB, MTK_TYPE_XDP_TX, -@@ -902,6 +990,7 @@ enum mkt_eth_capabilities { +@@ -902,6 +1012,7 @@ enum mkt_eth_capabilities { MTK_TRGMII_BIT, MTK_SGMII_BIT, MTK_USXGMII_BIT, @@ -835,7 +858,7 @@ Signed-off-by: Daniel Golle MTK_ESW_BIT, MTK_GEPHY_BIT, MTK_MUX_BIT, -@@ -922,6 +1011,7 @@ enum mkt_eth_capabilities { +@@ -922,6 +1033,7 @@ enum mkt_eth_capabilities { MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT, MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT, @@ -843,7 +866,7 @@ Signed-off-by: Daniel Golle MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT, MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII_BIT, MTK_ETH_MUX_GMAC123_TO_GEPHY_SGMII_BIT, -@@ -933,6 +1023,7 @@ enum mkt_eth_capabilities { +@@ -933,6 +1045,7 @@ enum mkt_eth_capabilities { MTK_ETH_PATH_GMAC1_SGMII_BIT, MTK_ETH_PATH_GMAC2_RGMII_BIT, MTK_ETH_PATH_GMAC2_SGMII_BIT, @@ -851,7 +874,7 @@ Signed-off-by: Daniel Golle MTK_ETH_PATH_GMAC2_GEPHY_BIT, MTK_ETH_PATH_GMAC3_SGMII_BIT, MTK_ETH_PATH_GDM1_ESW_BIT, -@@ -946,6 +1037,7 @@ enum mkt_eth_capabilities { +@@ -946,6 +1059,7 @@ enum mkt_eth_capabilities { #define MTK_TRGMII BIT_ULL(MTK_TRGMII_BIT) #define MTK_SGMII BIT_ULL(MTK_SGMII_BIT) #define MTK_USXGMII BIT_ULL(MTK_USXGMII_BIT) @@ -859,7 +882,7 @@ Signed-off-by: Daniel Golle #define MTK_ESW BIT_ULL(MTK_ESW_BIT) #define MTK_GEPHY BIT_ULL(MTK_GEPHY_BIT) #define MTK_MUX BIT_ULL(MTK_MUX_BIT) -@@ -968,6 +1060,8 @@ enum mkt_eth_capabilities { +@@ -968,6 +1082,8 @@ enum mkt_eth_capabilities { BIT_ULL(MTK_ETH_MUX_GMAC2_GMAC0_TO_GEPHY_BIT) #define MTK_ETH_MUX_U3_GMAC2_TO_QPHY \ BIT_ULL(MTK_ETH_MUX_U3_GMAC2_TO_QPHY_BIT) @@ -868,7 +891,7 @@ Signed-off-by: Daniel Golle #define MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII \ BIT_ULL(MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII_BIT) #define MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII \ -@@ -983,6 +1077,7 @@ enum mkt_eth_capabilities { +@@ -983,6 +1099,7 @@ enum mkt_eth_capabilities { #define MTK_ETH_PATH_GMAC1_SGMII BIT_ULL(MTK_ETH_PATH_GMAC1_SGMII_BIT) #define MTK_ETH_PATH_GMAC2_RGMII BIT_ULL(MTK_ETH_PATH_GMAC2_RGMII_BIT) #define MTK_ETH_PATH_GMAC2_SGMII BIT_ULL(MTK_ETH_PATH_GMAC2_SGMII_BIT) @@ -876,7 +899,7 @@ Signed-off-by: Daniel Golle #define MTK_ETH_PATH_GMAC2_GEPHY BIT_ULL(MTK_ETH_PATH_GMAC2_GEPHY_BIT) #define MTK_ETH_PATH_GMAC3_SGMII BIT_ULL(MTK_ETH_PATH_GMAC3_SGMII_BIT) #define MTK_ETH_PATH_GDM1_ESW BIT_ULL(MTK_ETH_PATH_GDM1_ESW_BIT) -@@ -996,6 +1091,7 @@ enum mkt_eth_capabilities { +@@ -996,6 +1113,7 @@ enum mkt_eth_capabilities { #define MTK_GMAC2_RGMII (MTK_ETH_PATH_GMAC2_RGMII | MTK_RGMII) #define MTK_GMAC2_SGMII (MTK_ETH_PATH_GMAC2_SGMII | MTK_SGMII) #define MTK_GMAC2_GEPHY (MTK_ETH_PATH_GMAC2_GEPHY | MTK_GEPHY) @@ -884,7 +907,7 @@ Signed-off-by: Daniel Golle #define MTK_GMAC3_SGMII (MTK_ETH_PATH_GMAC3_SGMII | MTK_SGMII) #define MTK_GDM1_ESW (MTK_ETH_PATH_GDM1_ESW | MTK_ESW) #define MTK_GMAC1_USXGMII (MTK_ETH_PATH_GMAC1_USXGMII | MTK_USXGMII) -@@ -1019,6 +1115,10 @@ enum mkt_eth_capabilities { +@@ -1019,6 +1137,10 @@ enum mkt_eth_capabilities { (MTK_ETH_MUX_GMAC1_GMAC2_TO_SGMII_RGMII | MTK_MUX | \ MTK_SHARED_SGMII) @@ -895,7 +918,7 @@ Signed-off-by: Daniel Golle /* 0: GMACx -> GEPHY, 1: GMACx -> SGMII where x is 1 or 2 */ #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) -@@ -1077,7 +1177,8 @@ enum mkt_eth_capabilities { +@@ -1077,7 +1199,8 @@ enum mkt_eth_capabilities { MTK_MUX_GMAC123_TO_GEPHY_SGMII | \ MTK_NETSYS_V3 | MTK_RSTCTRL_PPE1 | \ MTK_GMAC1_USXGMII | MTK_GMAC2_USXGMII | \ @@ -905,37 +928,54 @@ Signed-off-by: Daniel Golle struct mtk_tx_dma_desc_info { dma_addr_t addr; -@@ -1183,6 +1284,19 @@ struct mtk_soc_data { +@@ -1183,6 +1306,22 @@ struct mtk_soc_data { #define MTK_DMA_MONITOR_TIMEOUT msecs_to_jiffies(1000) -+/* struct mtk_xgmii - This is the structure holding sgmii/usxgmii regmap and -+ * its characteristics -+ * @regmap: The register map pointing at the range used to setup -+ * SGMII/USXGMII 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_usxgmii_pcs - This structure holds each usxgmii regmap and ++ * associated data ++ * @regmap: The register map pointing at the range used to setup ++ * USXGMII modes ++ * @interface: Currently selected interface mode ++ * @id: The element is used to record the index of PCS ++ * @pcs: Phylink PCS structure + */ -+struct mtk_xgmii { -+ struct regmap **regmap_usxgmii; -+ struct regmap **regmap_pextp; -+ struct regmap *regmap_pll; ++struct mtk_usxgmii_pcs { ++ struct mtk_eth *eth; ++ struct regmap *regmap; ++ phy_interface_t interface; ++ u8 id; ++ struct phylink_pcs pcs; +}; + /* struct mtk_eth - This is the main datasructure for holding the state * of the driver * @dev: The device pointer -@@ -1244,7 +1358,9 @@ struct mtk_eth { +@@ -1203,6 +1342,11 @@ struct mtk_soc_data { + * @infra: The register map pointing at the range used to setup + * SGMII and GePHY path + * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances ++ * @usxgmii_pll: The register map pointing at the range used to control ++ * the USXGMII SerDes PLL ++ * @regmap_pextp: The register map pointing at the range used to setup ++ * PHYA ++ * @usxgmii_pcs: Pointer to array of pointers to struct for USXGMII PCS + * @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 +@@ -1244,7 +1388,11 @@ struct mtk_eth { unsigned long sysclk; struct regmap *ethsys; struct regmap *infra; + struct regmap *toprgu; struct phylink_pcs **sgmii_pcs; -+ struct mtk_xgmii *xgmii; ++ struct regmap *usxgmii_pll; ++ struct regmap **regmap_pextp; ++ struct mtk_usxgmii_pcs **usxgmii_pcs; struct regmap *pctl; bool hwlro; refcount_t dma_refcnt; -@@ -1400,6 +1516,19 @@ static inline u32 mtk_get_ib2_multicast_ +@@ -1400,6 +1548,19 @@ static inline u32 mtk_get_ib2_multicast_ return MTK_FOE_IB2_MULTICAST; } @@ -955,7 +995,7 @@ Signed-off-by: Daniel Golle /* read the hardware status register */ void mtk_stats_update_mac(struct mtk_mac *mac); -@@ -1407,8 +1536,10 @@ void mtk_w32(struct mtk_eth *eth, u32 va +@@ -1407,8 +1568,10 @@ void mtk_w32(struct mtk_eth *eth, u32 va u32 mtk_r32(struct mtk_eth *eth, unsigned reg); int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); @@ -966,50 +1006,35 @@ Signed-off-by: Daniel Golle int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, -@@ -1418,5 +1549,36 @@ int mtk_flow_offload_cmd(struct mtk_eth +@@ -1418,5 +1581,20 @@ int mtk_flow_offload_cmd(struct mtk_eth void mtk_flow_offload_cleanup(struct mtk_eth *eth, struct list_head *list); void mtk_eth_set_dma_device(struct mtk_eth *eth, struct device *dma_dev); +#ifdef CONFIG_NET_MEDIATEK_SOC_USXGMII ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id); +int mtk_usxgmii_init(struct mtk_eth *eth); -+int mtk_xfi_pextp_init(struct mtk_eth *eth); -+int mtk_xfi_pll_init(struct mtk_eth *eth); -+int mtk_toprgu_init(struct mtk_eth *eth); +int mtk_xfi_pll_enable(struct mtk_eth *eth); -+int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, -+ int max_speed); -+int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state); -+void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id); -+void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id); +void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id); +#else -+static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pextp_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pll_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_toprgu_init(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_xfi_pll_enable(struct mtk_eth *eth) { return 0; }; -+static inline int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, -+ int max_speed) { return 0; }; -+static inline int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state) { return 0; }; -+static inline void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) { }; -+static inline void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) { }; -+#endif ++static inline struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int id) { return NULL; } ++static inline int mtk_usxgmii_init(struct mtk_eth *eth) { return 0; } ++static inline int mtk_xfi_pll_enable(struct mtk_eth *eth) { return 0; } ++static inline void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) { } ++static inline void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) { } ++#endif /* NET_MEDIATEK_SOC_USXGMII */ #endif /* MTK_ETH_H */ --- /dev/null +++ b/drivers/net/ethernet/mediatek/mtk_usxgmii.c -@@ -0,0 +1,646 @@ +@@ -0,0 +1,835 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Copyright (c) 2022 MediaTek Inc. + * Author: Henry Yen ++ * Daniel Golle + */ + +#include @@ -1017,43 +1042,20 @@ Signed-off-by: Daniel Golle +#include +#include "mtk_eth_soc.h" + -+int mtk_usxgmii_init(struct mtk_eth *eth) ++static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs) +{ -+ struct device_node *r = eth->dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; -+ struct device *dev = eth->dev; -+ struct device_node *np; -+ int i; -+ -+ xs->regmap_usxgmii = devm_kzalloc(dev, sizeof(*xs->regmap_usxgmii) * -+ eth->soc->num_devs, GFP_KERNEL); -+ if (!xs->regmap_usxgmii) -+ return -ENOMEM; -+ -+ for (i = 0; i < eth->soc->num_devs; i++) { -+ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); -+ if (!np) -+ break; -+ -+ xs->regmap_usxgmii[i] = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_usxgmii[i])) -+ return PTR_ERR(xs->regmap_usxgmii[i]); -+ } -+ -+ return 0; ++ return container_of(pcs, struct mtk_usxgmii_pcs, pcs); +} + -+int mtk_xfi_pextp_init(struct mtk_eth *eth) ++static int mtk_xfi_pextp_init(struct mtk_eth *eth) +{ + struct device *dev = eth->dev; + struct device_node *r = dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; + struct device_node *np; + int i; + -+ xs->regmap_pextp = devm_kzalloc(dev, sizeof(*xs->regmap_pextp) * -+ eth->soc->num_devs, GFP_KERNEL); -+ if (!xs->regmap_pextp) ++ eth->regmap_pextp = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->regmap_pextp), GFP_KERNEL); ++ if (!eth->regmap_pextp) + return -ENOMEM; + + for (i = 0; i < eth->soc->num_devs; i++) { @@ -1061,32 +1063,31 @@ Signed-off-by: Daniel Golle + if (!np) + break; + -+ xs->regmap_pextp[i] = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_pextp[i])) -+ return PTR_ERR(xs->regmap_pextp[i]); ++ eth->regmap_pextp[i] = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->regmap_pextp[i])) ++ return PTR_ERR(eth->regmap_pextp[i]); + } + + return 0; +} + -+int mtk_xfi_pll_init(struct mtk_eth *eth) ++static int mtk_xfi_pll_init(struct mtk_eth *eth) +{ + struct device_node *r = eth->dev->of_node; -+ struct mtk_xgmii *xs = eth->xgmii; + struct device_node *np; + + np = of_parse_phandle(r, "mediatek,xfi_pll", 0); + if (!np) + return -1; + -+ xs->regmap_pll = syscon_node_to_regmap(np); -+ if (IS_ERR(xs->regmap_pll)) -+ return PTR_ERR(xs->regmap_pll); ++ eth->usxgmii_pll = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pll)) ++ return PTR_ERR(eth->usxgmii_pll); + + return 0; +} + -+int mtk_toprgu_init(struct mtk_eth *eth) ++static int mtk_toprgu_init(struct mtk_eth *eth) +{ + struct device_node *r = eth->dev->of_node; + struct device_node *np; @@ -1104,18 +1105,17 @@ Signed-off-by: Daniel Golle + +int mtk_xfi_pll_enable(struct mtk_eth *eth) +{ -+ struct mtk_xgmii *xs = eth->xgmii; + u32 val = 0; + -+ if (!xs->regmap_pll) ++ if (!eth->usxgmii_pll) + return -EINVAL; + + /* Add software workaround for USXGMII PLL TCL issue */ -+ regmap_write(xs->regmap_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); ++ regmap_write(eth->usxgmii_pll, XFI_PLL_ANA_GLB8, RG_XFI_PLL_ANA_SWWA); + -+ regmap_read(xs->regmap_pll, XFI_PLL_DIG_GLB8, &val); ++ regmap_read(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, &val); + val |= RG_XFI_PLL_EN; -+ regmap_write(xs->regmap_pll, XFI_PLL_DIG_GLB8, val); ++ regmap_write(eth->usxgmii_pll, XFI_PLL_DIG_GLB8, val); + + return 0; +} @@ -1141,531 +1141,727 @@ Signed-off-by: Daniel Golle + return xgmii_id; +} + -+void mtk_usxgmii_setup_phya_an_10000(struct mtk_eth *eth, int mac_id) ++static int mtk_xgmii2mac_id(struct mtk_eth *eth, int xgmii_id) +{ -+ struct mtk_xgmii *xs = eth->xgmii; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ int mac_id = xgmii_id; ++ ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V3)) { ++ switch (xgmii_id) { ++ case 0: ++ mac_id = 2; ++ break; ++ case 1: ++ mac_id = 1; ++ break; ++ default: ++ mac_id = -1; ++ } ++ } ++ ++ return mac_id; ++} ++ + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++static void mtk_usxgmii_setup_phya_usxgmii(struct mtk_usxgmii_pcs *mpcs) ++{ ++ struct regmap *pextp; ++ ++ if (!mpcs->eth) + return; + -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, 0x000FFE6D); -+ regmap_write(xs->regmap_usxgmii[id], 0x818, 0x07B1EC7B); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x30000000); -+ ndelay(1020); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x10000000); -+ ndelay(1020); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, 0x00000000); -+ -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00C9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x01423342); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; ++ ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000001); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_setup_phya_force_5000(struct mtk_eth *eth, int mac_id) ++static void mtk_usxgmii_setup_phya_5gbaser(struct mtk_usxgmii_pcs *mpcs) +{ -+ unsigned int val; -+ struct mtk_xgmii *xs = eth->xgmii; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++ if (!mpcs->eth) + return; + -+ /* Setup USXGMII speed */ -+ val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_5G) | -+ FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_5G); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Disable USXGMII AN mode */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val); -+ val &= ~RG_AN_ENABLE; -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val); -+ -+ /* Gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); -+ -+ /* USXGMII force mode setting */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_USXGMII_RATE_UPDATE_MODE; -+ val |= RG_IF_FORCE_EN; -+ val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Un-gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val &= ~RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; + -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAAA5A5AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C018AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777812B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005C9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9DFAFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x273F3F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA8883868); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x14661466); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E001ABF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x080B0D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02050909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C000000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04000000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x50808C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18000000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x00A132A1); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAAA5A5AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C018AA); ++ regmap_write(pextp, 0x50E0, 0x3777812B); ++ regmap_write(pextp, 0x506C, 0x005C9CFF); ++ regmap_write(pextp, 0x5070, 0x9DFAFAFA); ++ regmap_write(pextp, 0x5074, 0x273F3F3F); ++ regmap_write(pextp, 0x5078, 0xA8883868); ++ regmap_write(pextp, 0x507C, 0x14661466); ++ regmap_write(pextp, 0x5080, 0x0E001ABF); ++ regmap_write(pextp, 0x5084, 0x080B0D0D); ++ regmap_write(pextp, 0x5088, 0x02050909); ++ regmap_write(pextp, 0x50E4, 0x0C000000); ++ regmap_write(pextp, 0x50E8, 0x04000000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x50808C8C); ++ regmap_write(pextp, 0x6004, 0x18000000); ++ regmap_write(pextp, 0x00F8, 0x00A132A1); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000003); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000003); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_setup_phya_force_10000(struct mtk_eth *eth, int mac_id) ++static void mtk_usxgmii_setup_phya_10gbaser(struct mtk_usxgmii_pcs *mpcs) +{ -+ struct mtk_xgmii *xs = eth->xgmii; -+ unsigned int val; -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; + -+ if (id >= eth->soc->num_devs || -+ !xs->regmap_usxgmii[id] || !xs->regmap_pextp[id]) ++ if (!mpcs->eth) + return; + -+ /* Setup USXGMII speed */ -+ val = FIELD_PREP(RG_XFI_RX_MODE, RG_XFI_RX_MODE_10G) | -+ FIELD_PREP(RG_XFI_TX_MODE, RG_XFI_TX_MODE_10G); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Disable USXGMII AN mode */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, &val); -+ val &= ~RG_AN_ENABLE; -+ regmap_write(xs->regmap_usxgmii[id], RG_PCS_AN_CTRL0, val); -+ -+ /* Gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); -+ -+ /* USXGMII force mode setting */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val |= RG_USXGMII_RATE_UPDATE_MODE; -+ val |= RG_IF_FORCE_EN; -+ val |= FIELD_PREP(RG_RATE_ADAPT_MODE, RG_RATE_ADAPT_MODE_X1); -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ /* Un-gated USXGMII */ -+ regmap_read(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, &val); -+ val &= ~RG_MAC_CK_GATED; -+ regmap_write(xs->regmap_usxgmii[id], RG_PHY_TOP_SPEED_CTRL1, val); -+ -+ ndelay(1020); ++ pextp = mpcs->eth->regmap_pextp[mpcs->id]; ++ if (!pextp) ++ return; + -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00C9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x01423342); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F20); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00C9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x01423342); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F20); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00022220); -+ regmap_write(xs->regmap_pextp[id], 0x5064, 0x0F020A01); -+ regmap_write(xs->regmap_pextp[id], 0x50B4, 0x06100600); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x49664100); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0x00000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x00000000); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x00000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00040000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000001); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00022220); ++ regmap_write(pextp, 0x5064, 0x0F020A01); ++ regmap_write(pextp, 0x50B4, 0x06100600); ++ regmap_write(pextp, 0x3048, 0x47684100); ++ regmap_write(pextp, 0x3050, 0x00000000); ++ regmap_write(pextp, 0x3054, 0x00000000); ++ regmap_write(pextp, 0x306C, 0x00000F00); ++ if (mpcs->id == 0) ++ regmap_write(pextp, 0xA008, 0x0007B400); ++ ++ regmap_write(pextp, 0xA060, 0x00040000); ++ regmap_write(pextp, 0x90D0, 0x00000001); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C111); ++ /* Switch to Gen3 */ ++ regmap_write(pextp, 0x0070, 0x0202C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0202C101); ++ regmap_write(pextp, 0x0070, 0x0202C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F00); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F00); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_usxgmii_reset(struct mtk_eth *eth, int mac_id) ++void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) +{ + u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; + -+ if (id >= eth->soc->num_devs || !eth->toprgu) ++ if (id >= eth->soc->num_devs) + return; + -+ switch (mac_id) { -+ case MTK_GMAC2_ID: -+ regmap_write(eth->toprgu, 0xFC, 0x0000A004); -+ regmap_write(eth->toprgu, 0x18, 0x88F0A004); -+ regmap_write(eth->toprgu, 0xFC, 0x00000000); -+ regmap_write(eth->toprgu, 0x18, 0x88F00000); -+ regmap_write(eth->toprgu, 0x18, 0x00F00000); -+ break; -+ case MTK_GMAC3_ID: -+ regmap_write(eth->toprgu, 0xFC, 0x00005002); -+ regmap_write(eth->toprgu, 0x18, 0x88F05002); -+ regmap_write(eth->toprgu, 0xFC, 0x00000000); -+ regmap_write(eth->toprgu, 0x18, 0x88F00000); -+ regmap_write(eth->toprgu, 0x18, 0x00F00000); -+ break; -+ } -+ -+ mdelay(10); -+} -+ -+int mtk_usxgmii_setup_mode_an(struct mtk_eth *eth, int mac_id, int max_speed) -+{ -+ if (mac_id < 0 || mac_id >= eth->soc->num_devs) -+ return -EINVAL; -+ -+ if ((max_speed != SPEED_10000) && (max_speed != SPEED_5000)) -+ return -EINVAL; -+ -+ mtk_xfi_pll_enable(eth); -+ mtk_usxgmii_reset(eth, mac_id); -+ mtk_usxgmii_setup_phya_an_10000(eth, mac_id); -+ -+ return 0; -+} -+ -+int mtk_usxgmii_setup_mode_force(struct mtk_eth *eth, int mac_id, -+ const struct phylink_link_state *state) -+{ -+ if (mac_id < 0 || mac_id >= eth->soc->num_devs) -+ return -EINVAL; -+ -+ mtk_xfi_pll_enable(eth); -+ mtk_usxgmii_reset(eth, mac_id); -+ if (state->interface == PHY_INTERFACE_MODE_5GBASER) -+ mtk_usxgmii_setup_phya_force_5000(eth, mac_id); -+ else -+ mtk_usxgmii_setup_phya_force_10000(eth, mac_id); -+ -+ return 0; -+} -+ -+void mtk_sgmii_setup_phya_gen1(struct mtk_eth *eth, int mac_id) -+{ -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); -+ struct mtk_xgmii *xs = eth->xgmii; -+ -+ if (id >= eth->soc->num_devs || !xs->regmap_pextp[id]) ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) + return; + -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020207); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E05050F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00200032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014BA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000EAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080E0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030B09); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0606); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x00FA32FA); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F21); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020207); ++ regmap_write(pextp, 0x2034, 0x0E05050F); ++ regmap_write(pextp, 0x2040, 0x00200032); ++ regmap_write(pextp, 0x50F0, 0x00C014BA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000EAF); ++ regmap_write(pextp, 0x5084, 0x08080E0D); ++ regmap_write(pextp, 0x5088, 0x02030B09); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0606); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x00FA32FA); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00011110); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3064, 0x0000C000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x20200F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00050000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000007); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3064, 0x0000C000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x20200F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000007); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C111); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C101); ++ regmap_write(pextp, 0x0070, 0x0201C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F01); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + +void mtk_sgmii_setup_phya_gen2(struct mtk_eth *eth, int mac_id) +{ -+ struct mtk_xgmii *xs = eth->xgmii; + u32 id = mtk_mac2xgmii_id(eth, mac_id); ++ struct regmap *pextp; ++ ++ if (id >= eth->soc->num_devs) ++ return; + -+ if (id >= eth->soc->num_devs || !xs->regmap_pextp[id]) ++ pextp = eth->regmap_pextp[id]; ++ if (!pextp) + return; + -+ regmap_write(xs->regmap_pextp[id], 0x9024, 0x00D9071C); -+ regmap_write(xs->regmap_pextp[id], 0x2020, 0xAA8585AA); -+ regmap_write(xs->regmap_pextp[id], 0x2030, 0x0C020707); -+ regmap_write(xs->regmap_pextp[id], 0x2034, 0x0E050F0F); -+ regmap_write(xs->regmap_pextp[id], 0x2040, 0x00140032); -+ regmap_write(xs->regmap_pextp[id], 0x50F0, 0x00C014AA); -+ regmap_write(xs->regmap_pextp[id], 0x50E0, 0x3777C12B); -+ regmap_write(xs->regmap_pextp[id], 0x506C, 0x005F9CFF); -+ regmap_write(xs->regmap_pextp[id], 0x5070, 0x9D9DFAFA); -+ regmap_write(xs->regmap_pextp[id], 0x5074, 0x27273F3F); -+ regmap_write(xs->regmap_pextp[id], 0x5078, 0xA7883C68); -+ regmap_write(xs->regmap_pextp[id], 0x507C, 0x11661166); -+ regmap_write(xs->regmap_pextp[id], 0x5080, 0x0E000AAF); -+ regmap_write(xs->regmap_pextp[id], 0x5084, 0x08080D0D); -+ regmap_write(xs->regmap_pextp[id], 0x5088, 0x02030909); -+ regmap_write(xs->regmap_pextp[id], 0x50E4, 0x0C0C0000); -+ regmap_write(xs->regmap_pextp[id], 0x50E8, 0x04040000); -+ regmap_write(xs->regmap_pextp[id], 0x50EC, 0x0F0F0C06); -+ regmap_write(xs->regmap_pextp[id], 0x50A8, 0x506E8C8C); -+ regmap_write(xs->regmap_pextp[id], 0x6004, 0x18190000); -+ regmap_write(xs->regmap_pextp[id], 0x00F8, 0x009C329C); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F21); -+ regmap_write(xs->regmap_pextp[id], 0x0030, 0x00050C00); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x02002800); ++ /* Setup operation mode */ ++ regmap_write(pextp, 0x9024, 0x00D9071C); ++ regmap_write(pextp, 0x2020, 0xAA8585AA); ++ regmap_write(pextp, 0x2030, 0x0C020707); ++ regmap_write(pextp, 0x2034, 0x0E050F0F); ++ regmap_write(pextp, 0x2040, 0x00140032); ++ regmap_write(pextp, 0x50F0, 0x00C014AA); ++ regmap_write(pextp, 0x50E0, 0x3777C12B); ++ regmap_write(pextp, 0x506C, 0x005F9CFF); ++ regmap_write(pextp, 0x5070, 0x9D9DFAFA); ++ regmap_write(pextp, 0x5074, 0x27273F3F); ++ regmap_write(pextp, 0x5078, 0xA7883C68); ++ regmap_write(pextp, 0x507C, 0x11661166); ++ regmap_write(pextp, 0x5080, 0x0E000AAF); ++ regmap_write(pextp, 0x5084, 0x08080D0D); ++ regmap_write(pextp, 0x5088, 0x02030909); ++ regmap_write(pextp, 0x50E4, 0x0C0C0000); ++ regmap_write(pextp, 0x50E8, 0x04040000); ++ regmap_write(pextp, 0x50EC, 0x0F0F0C06); ++ regmap_write(pextp, 0x50A8, 0x506E8C8C); ++ regmap_write(pextp, 0x6004, 0x18190000); ++ regmap_write(pextp, 0x00F8, 0x009C329C); ++ /* Force SGDT_OUT off and select PCS */ ++ regmap_write(pextp, 0x00F4, 0x80201F21); ++ /* Force GLB_CKDET_OUT */ ++ regmap_write(pextp, 0x0030, 0x00050C00); ++ /* Force AEQ on */ ++ regmap_write(pextp, 0x0070, 0x02002800); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000020); -+ regmap_write(xs->regmap_pextp[id], 0x3028, 0x00008A01); -+ regmap_write(xs->regmap_pextp[id], 0x302C, 0x0000A884); -+ regmap_write(xs->regmap_pextp[id], 0x3024, 0x00083002); -+ regmap_write(xs->regmap_pextp[id], 0x3010, 0x00011110); -+ regmap_write(xs->regmap_pextp[id], 0x3048, 0x40704000); -+ regmap_write(xs->regmap_pextp[id], 0x3050, 0xA8000000); -+ regmap_write(xs->regmap_pextp[id], 0x3054, 0x000000AA); -+ regmap_write(xs->regmap_pextp[id], 0x306C, 0x22000F00); -+ regmap_write(xs->regmap_pextp[id], 0xA060, 0x00050000); -+ regmap_write(xs->regmap_pextp[id], 0x90D0, 0x00000005); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200E800); ++ /* Setup DA default value */ ++ regmap_write(pextp, 0x30B0, 0x00000020); ++ regmap_write(pextp, 0x3028, 0x00008A01); ++ regmap_write(pextp, 0x302C, 0x0000A884); ++ regmap_write(pextp, 0x3024, 0x00083002); ++ regmap_write(pextp, 0x3010, 0x00011110); ++ regmap_write(pextp, 0x3048, 0x40704000); ++ regmap_write(pextp, 0x3050, 0xA8000000); ++ regmap_write(pextp, 0x3054, 0x000000AA); ++ regmap_write(pextp, 0x306C, 0x22000F00); ++ regmap_write(pextp, 0xA060, 0x00050000); ++ regmap_write(pextp, 0x90D0, 0x00000005); ++ /* Release reset */ ++ regmap_write(pextp, 0x0070, 0x0200E800); + udelay(150); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C111); ++ /* Switch to P0 */ ++ regmap_write(pextp, 0x0070, 0x0200C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0200C101); ++ regmap_write(pextp, 0x0070, 0x0200C101); + udelay(15); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C111); ++ /* Switch to Gen2 */ ++ regmap_write(pextp, 0x0070, 0x0201C111); + ndelay(1020); -+ regmap_write(xs->regmap_pextp[id], 0x0070, 0x0201C101); ++ regmap_write(pextp, 0x0070, 0x0201C101); + udelay(100); -+ regmap_write(xs->regmap_pextp[id], 0x30B0, 0x00000030); -+ regmap_write(xs->regmap_pextp[id], 0x00F4, 0x80201F01); -+ regmap_write(xs->regmap_pextp[id], 0x3040, 0x30000000); ++ regmap_write(pextp, 0x30B0, 0x00000030); ++ regmap_write(pextp, 0x00F4, 0x80201F01); ++ regmap_write(pextp, 0x3040, 0x30000000); + udelay(400); +} + -+void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) ++static void mtk_usxgmii_reset(struct mtk_eth *eth, int id) +{ -+ u32 id = mtk_mac2xgmii_id(eth, mac_id); + u32 val = 0; + + if (id >= eth->soc->num_devs || !eth->toprgu) + return; + -+ switch (mac_id) { -+ case MTK_GMAC2_ID: ++ switch (id) { ++ case 0: + /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val |= SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST; ++ val |= SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + -+ /* Assert SGMII reset */ ++ /* Assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | -+ SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST; ++ SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + -+ /* De-assert SGMII reset */ ++ /* De-assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); -+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | -+ SWSYSRST_SGMII1_GRST); ++ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | ++ SWSYSRST_XFI0_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; -+ case MTK_GMAC3_ID: -+ /* Enable Software reset */ ++ case 1: ++ /* Enable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val |= SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST; ++ val |= SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + -+ /* Assert SGMII reset */ ++ /* Assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88) | -+ SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST; ++ SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST; + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + udelay(100); + -+ /* De-assert SGMII reset */ ++ /* De-assert USXGMII reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST, &val); + val |= FIELD_PREP(SWSYSRST_UNLOCK_KEY, 0x88); -+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST, val); + + /* Disable software reset */ + regmap_read(eth->toprgu, TOPRGU_SWSYSRST_EN, &val); -+ val &= ~(SWSYSRST_XFI_PEXPT0_GRST | -+ SWSYSRST_SGMII0_GRST); ++ val &= ~(SWSYSRST_XFI_PEXPT1_GRST | ++ SWSYSRST_XFI1_GRST); + regmap_write(eth->toprgu, TOPRGU_SWSYSRST_EN, val); + break; + } + -+ mdelay(1); ++ mdelay(10); ++} ++ ++void mtk_sgmii_reset(struct mtk_eth *eth, int mac_id) ++{ ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); ++ ++ mtk_usxgmii_reset(eth, xgmii_id); ++} ++ ++ ++static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0; ++ bool mode_changed = false; ++ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | ++ USXGMII_AN_ENABLE; ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ } else if (interface == PHY_INTERFACE_MODE_10GKR) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_10G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_10G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else if (interface == PHY_INTERFACE_MODE_5GBASER) { ++ an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF); ++ link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) | ++ FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D); ++ xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_RX_MODE_5G) | ++ FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_TX_MODE_5G); ++ adapt_mode = USXGMII_RATE_UPDATE_MODE; ++ } else ++ return -EINVAL; ++ ++ adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1); ++ ++ if (mpcs->interface != interface) { ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ mtk_xfi_pll_enable(eth); ++ mtk_usxgmii_reset(eth, mpcs->id); ++ ++ /* Setup USXGMII AN ctrl */ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL0, ++ USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE, ++ an_ctrl); ++ ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_CTRL2, ++ USXGMII_LINK_TIMER_IDLE_DETECT | ++ USXGMII_LINK_TIMER_COMP_ACK_DETECT | ++ USXGMII_LINK_TIMER_AN_RESTART, ++ link_timer); ++ ++ /* Gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED); ++ ++ /* Enable interface force mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN); ++ ++ /* Setup USXGMII adapt mode */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE, ++ adapt_mode); ++ ++ /* Setup USXGMII speed */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE, ++ xfi_mode); ++ ++ udelay(1); ++ ++ /* Un-gated MAC CK */ ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_MAC_CK_GATED, 0); ++ ++ udelay(1); ++ ++ /* Disable interface force mode for the AN mode */ ++ if (an_ctrl & USXGMII_AN_ENABLE) ++ regmap_update_bits(mpcs->regmap, RG_PHY_TOP_SPEED_CTRL1, ++ USXGMII_IF_FORCE_EN, 0); ++ ++ /* Setup USXGMIISYS with the determined property */ ++ if (interface == PHY_INTERFACE_MODE_USXGMII) ++ mtk_usxgmii_setup_phya_usxgmii(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_10GKR) ++ mtk_usxgmii_setup_phya_10gbaser(mpcs); ++ else if (interface == PHY_INTERFACE_MODE_5GBASER) ++ mtk_usxgmii_setup_phya_5gbaser(mpcs); ++ ++ return mode_changed; ++} ++ ++static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ struct mtk_eth *eth = mpcs->eth; ++ struct mtk_mac *mac = eth->mac[mtk_xgmii2mac_id(eth, mpcs->id)]; ++ u32 val = 0; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ if (FIELD_GET(USXGMII_AN_ENABLE, val)) { ++ /* Refresh LPA by inverting LPA_LATCH */ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ regmap_update_bits(mpcs->regmap, RG_PCS_AN_STS0, ++ USXGMII_LPA_LATCH, ++ !(val & USXGMII_LPA_LATCH)); ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_STS0, &val); ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(USXGMII_LPA_LINK, val); ++ state->duplex = FIELD_GET(USXGMII_LPA_DUPLEX, val); ++ ++ switch (FIELD_GET(USXGMII_LPA_SPEED_MASK, val)) { ++ case USXGMII_LPA_SPEED_10: ++ state->speed = SPEED_10; ++ break; ++ case USXGMII_LPA_SPEED_100: ++ state->speed = SPEED_100; ++ break; ++ case USXGMII_LPA_SPEED_1000: ++ state->speed = SPEED_1000; ++ break; ++ case USXGMII_LPA_SPEED_2500: ++ state->speed = SPEED_2500; ++ break; ++ case USXGMII_LPA_SPEED_5000: ++ state->speed = SPEED_5000; ++ break; ++ case USXGMII_LPA_SPEED_10000: ++ state->speed = SPEED_10000; ++ break; ++ } ++ } else { ++ val = mtk_r32(mac->hw, MTK_XGMAC_STS(mac->id)); ++ ++ if (mac->id == MTK_GMAC2_ID) ++ val = val >> 16; ++ ++ switch (FIELD_GET(MTK_USXGMII_PCS_MODE, val)) { ++ case 0: ++ state->speed = SPEED_10000; ++ break; ++ case 1: ++ state->speed = SPEED_5000; ++ break; ++ case 2: ++ state->speed = SPEED_2500; ++ break; ++ case 3: ++ state->speed = SPEED_1000; ++ break; ++ } ++ ++ state->interface = mpcs->interface; ++ state->link = FIELD_GET(MTK_USXGMII_PCS_LINK, val); ++ state->duplex = DUPLEX_FULL; ++ } ++ ++ if (state->link == 0) ++ mtk_usxgmii_pcs_config(pcs, MLO_AN_INBAND, ++ state->interface, NULL, false); +} ---- a/drivers/net/ethernet/mediatek/Kconfig -+++ b/drivers/net/ethernet/mediatek/Kconfig -@@ -11,6 +11,14 @@ config NET_MEDIATEK_SOC_WED - depends on ARCH_MEDIATEK || COMPILE_TEST - def_bool NET_MEDIATEK_SOC != n - -+config NET_MEDIATEK_SOC_USXGMII -+ bool "Support USXGMII SerDes on MT7988" -+ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST -+ def_bool NET_MEDIATEK_SOC != n -+ help -+ Include support for 10G USXGMII SerDes unit which can -+ be found on MT7988. + - config NET_MEDIATEK_SOC - tristate "MediaTek SoC Gigabit Ethernet support" - depends on NET_DSA || !NET_DSA ++static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs); ++ unsigned int val = 0; ++ ++ if (!mpcs->regmap) ++ return; ++ ++ regmap_read(mpcs->regmap, RG_PCS_AN_CTRL0, &val); ++ val |= USXGMII_AN_RESTART; ++ regmap_write(mpcs->regmap, RG_PCS_AN_CTRL0, val); ++} ++ ++static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ int speed, int duplex) ++{ ++ /* Reconfiguring USXGMII to ensure the quality of the RX signal ++ * after the line side link up. ++ */ ++ mtk_usxgmii_pcs_config(pcs, mode, ++ interface, NULL, false); ++} ++ ++static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = { ++ .pcs_config = mtk_usxgmii_pcs_config, ++ .pcs_get_state = mtk_usxgmii_pcs_get_state, ++ .pcs_an_restart = mtk_usxgmii_pcs_restart_an, ++ .pcs_link_up = mtk_usxgmii_pcs_link_up, ++}; ++ ++int mtk_usxgmii_init(struct mtk_eth *eth) ++{ ++ struct device_node *r = eth->dev->of_node; ++ struct device *dev = eth->dev; ++ struct device_node *np; ++ int i, ret; ++ ++ eth->usxgmii_pcs = devm_kcalloc(dev, eth->soc->num_devs, sizeof(eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs) ++ return -ENOMEM; ++ ++ for (i = 0; i < eth->soc->num_devs; i++) { ++ np = of_parse_phandle(r, "mediatek,usxgmiisys", i); ++ if (!np) ++ break; ++ ++ eth->usxgmii_pcs[i] = devm_kzalloc(dev, sizeof(*eth->usxgmii_pcs), GFP_KERNEL); ++ if (!eth->usxgmii_pcs[i]) ++ return -ENOMEM; ++ ++ eth->usxgmii_pcs[i]->id = i; ++ eth->usxgmii_pcs[i]->eth = eth; ++ eth->usxgmii_pcs[i]->regmap = syscon_node_to_regmap(np); ++ if (IS_ERR(eth->usxgmii_pcs[i]->regmap)) ++ return PTR_ERR(eth->usxgmii_pcs[i]->regmap); ++ ++ eth->usxgmii_pcs[i]->pcs.ops = &mtk_usxgmii_pcs_ops; ++ eth->usxgmii_pcs[i]->pcs.poll = true; ++ eth->usxgmii_pcs[i]->interface = PHY_INTERFACE_MODE_NA; ++ ++ of_node_put(np); ++ } ++ ++ ret = mtk_xfi_pextp_init(eth); ++ if (ret) ++ return ret; ++ ++ ret = mtk_xfi_pll_init(eth); ++ if (ret) ++ return ret; ++ ++ return mtk_toprgu_init(eth); ++} ++ ++struct phylink_pcs *mtk_usxgmii_select_pcs(struct mtk_eth *eth, int mac_id) ++{ ++ u32 xgmii_id = mtk_mac2xgmii_id(eth, mac_id); ++ ++ if (!eth->usxgmii_pcs[xgmii_id]->regmap) ++ return NULL; ++ ++ return ð->usxgmii_pcs[xgmii_id]->pcs; ++} diff --git a/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-2p5ge.c b/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-2p5ge.c new file mode 100644 index 00000000000000..c12e6b8eb6f8f6 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-2p5ge.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MEDAITEK_2P5GE_PHY_DMB_FW "mediatek/mediatek-2p5ge-phy-dmb.bin" +#define MEDIATEK_2P5GE_PHY_PMB_FW "mediatek/mediatek-2p5ge-phy-pmb.bin" + +#define MD32_EN_CFG 0x18 +#define MD32_EN BIT(0) + +#define BASE100T_STATUS_EXTEND 0x10 +#define BASE1000T_STATUS_EXTEND 0x11 +#define EXTEND_CTRL_AND_STATUS 0x16 + +#define PHY_AUX_CTRL_STATUS 0x1d +#define PHY_AUX_DPX_MASK GENMASK(5, 5) +#define PHY_AUX_SPEED_MASK GENMASK(4, 2) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_LINK_STATUS_MISC 0xa2 +#define MTK_PHY_FDX_ENABLE BIT(5) + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL 0x24 +#define MTK_PHY_LED0_ON_LINK1000 BIT(0) +#define MTK_PHY_LED0_ON_LINK100 BIT(1) +#define MTK_PHY_LED0_ON_LINK10 BIT(2) +#define MTK_PHY_LED0_ON_LINK2500 BIT(7) +#define MTK_PHY_LED0_POLARITY BIT(14) + +#define MTK_PHY_LED1_ON_CTRL 0x26 +#define MTK_PHY_LED1_ON_FDX BIT(4) +#define MTK_PHY_LED1_ON_HDX BIT(5) +#define MTK_PHY_LED1_POLARITY BIT(14) + +enum { + PHY_AUX_SPD_10 = 0, + PHY_AUX_SPD_100, + PHY_AUX_SPD_1000, + PHY_AUX_SPD_2500, +}; + +static int mt798x_2p5ge_phy_config_init(struct phy_device *phydev) +{ + int ret; + int i; + const struct firmware *fw; + struct device *dev = &phydev->mdio.dev; + struct device_node *np; + void __iomem *dmb_addr; + void __iomem *pmb_addr; + void __iomem *mcucsr_base; + u16 reg; + struct pinctrl *pinctrl; + + np = of_find_compatible_node(NULL, NULL, "mediatek,2p5gphy-fw"); + if (!np) + return -ENOENT; + + dmb_addr = of_iomap(np, 0); + if (!dmb_addr) + return -ENOMEM; + pmb_addr = of_iomap(np, 1); + if (!pmb_addr) + return -ENOMEM; + mcucsr_base = of_iomap(np, 2); + if (!mcucsr_base) + return -ENOMEM; + + ret = request_firmware(&fw, MEDAITEK_2P5GE_PHY_DMB_FW, dev); + if (ret) { + dev_err(dev, "failed to load firmware: %s, ret: %d\n", + MEDAITEK_2P5GE_PHY_DMB_FW, ret); + return ret; + } + for (i = 0; i < fw->size - 1; i += 4) + writel(*((uint32_t *)(fw->data + i)), dmb_addr + i); + release_firmware(fw); + + ret = request_firmware(&fw, MEDIATEK_2P5GE_PHY_PMB_FW, dev); + if (ret) { + dev_err(dev, "failed to load firmware: %s, ret: %d\n", + MEDIATEK_2P5GE_PHY_PMB_FW, ret); + return ret; + } + for (i = 0; i < fw->size - 1; i += 4) + writel(*((uint32_t *)(fw->data + i)), pmb_addr + i); + release_firmware(fw); + + reg = readw(mcucsr_base + MD32_EN_CFG); + writew(reg | MD32_EN, mcucsr_base + MD32_EN_CFG); + dev_dbg(dev, "Firmware loading/trigger ok.\n"); + + /* Setup LED */ + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_POLARITY); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_ON_LINK10 | + MTK_PHY_LED0_ON_LINK100 | + MTK_PHY_LED0_ON_LINK1000 | + MTK_PHY_LED0_ON_LINK2500); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED1_ON_FDX | MTK_PHY_LED1_ON_HDX); + + pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "i2p5gbe-led"); + if (IS_ERR(pinctrl)) { + dev_err(&phydev->mdio.dev, "Fail to set LED pins!\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +static int mt798x_2p5ge_phy_config_aneg(struct phy_device *phydev) +{ + bool changed = false; + u32 adv; + int ret; + + if (phydev->autoneg == AUTONEG_DISABLE) { + /* Configure half duplex with genphy_setup_forced, + * because genphy_c45_pma_setup_forced does not support. + */ + return phydev->duplex != DUPLEX_FULL + ? genphy_setup_forced(phydev) + : genphy_c45_pma_setup_forced(phydev); + } + + ret = genphy_c45_an_config_aneg(phydev); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising); + ret = phy_modify_changed(phydev, MII_CTRL1000, + ADVERTISE_1000FULL | ADVERTISE_1000HALF, + adv); + if (ret < 0) + return ret; + if (ret > 0) + changed = true; + + return genphy_c45_check_and_restart_aneg(phydev, changed); +} + +static int mt798x_2p5ge_phy_get_features(struct phy_device *phydev) +{ + int ret; + + ret = genphy_read_abilities(phydev); + if (ret) + return ret; + + /* We don't support HDX at MAC layer on mt798x. + * So mask phy's HDX capabilities, too. + */ + linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, + phydev->supported); + linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); + + return 0; +} + +static int mt798x_2p5ge_phy_read_status(struct phy_device *phydev) +{ + int ret; + + ret = genphy_update_link(phydev); + if (ret) + return ret; + + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + phydev->pause = 0; + phydev->asym_pause = 0; + + if (!phydev->link) + return 0; + + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { + ret = genphy_c45_read_lpa(phydev); + if (ret < 0) + return ret; + + /* Read the link partner's 1G advertisement */ + ret = phy_read(phydev, MII_STAT1000); + if (ret < 0) + return ret; + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret); + } else if (phydev->autoneg == AUTONEG_DISABLE) { + linkmode_zero(phydev->lp_advertising); + } + + ret = phy_read(phydev, PHY_AUX_CTRL_STATUS); + if (ret < 0) + return ret; + + switch (FIELD_GET(PHY_AUX_SPEED_MASK, ret)) { + case PHY_AUX_SPD_10: + phydev->speed = SPEED_10; + break; + case PHY_AUX_SPD_100: + phydev->speed = SPEED_100; + break; + case PHY_AUX_SPD_1000: + phydev->speed = SPEED_1000; + break; + case PHY_AUX_SPD_2500: + phydev->speed = SPEED_2500; + phydev->duplex = DUPLEX_FULL; /* 2.5G must be FDX */ + break; + } + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LINK_STATUS_MISC); + if (ret < 0) + return ret; + + phydev->duplex = (ret & MTK_PHY_FDX_ENABLE) ? DUPLEX_FULL : DUPLEX_HALF; + + return 0; +} + +static struct phy_driver mtk_gephy_driver[] = { + { + PHY_ID_MATCH_EXACT(0x00339c11), + .name = "MediaTek MT798x 2.5GbE PHY", + .config_init = mt798x_2p5ge_phy_config_init, + .config_aneg = mt798x_2p5ge_phy_config_aneg, + .get_features = mt798x_2p5ge_phy_get_features, + .read_status = mt798x_2p5ge_phy_read_status, + }, +}; + +module_phy_driver(mtk_gephy_driver); + +static struct mdio_device_id __maybe_unused mtk_2p5ge_phy_tbl[] = { + { PHY_ID_MATCH_VENDOR(0x00339c00) }, + { } +}; + +MODULE_DESCRIPTION("MediaTek 2.5Gb Ethernet PHY driver"); +MODULE_AUTHOR("SkyLake Huang "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, mtk_2p5ge_phy_tbl); +MODULE_FIRMWARE(MEDAITEK_2P5GE_PHY_DMB_FW); +MODULE_FIRMWARE(MEDIATEK_2P5GE_PHY_PMB_FW); diff --git a/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-ge-soc.c b/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-ge-soc.c new file mode 100644 index 00000000000000..e48502ac3fdd16 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/net/phy/mediatek-ge-soc.c @@ -0,0 +1,1263 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include +#include +#include + +#define MTK_GPHY_ID_MT7981 0x03a29461 +#define MTK_GPHY_ID_MT7988 0x03a29481 + +#define MTK_EXT_PAGE_ACCESS 0x1f +#define MTK_PHY_PAGE_STANDARD 0x0000 +#define MTK_PHY_PAGE_EXTENDED_3 0x0003 + +#define MTK_PHY_LPI_REG_14 0x14 +#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK GENMASK(8, 0) + +#define MTK_PHY_LPI_REG_1c 0x1c +#define MTK_PHY_SMI_DET_ON_THRESH_MASK GENMASK(13, 8) + +#define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 +#define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + +#define ANALOG_INTERNAL_OPERATION_MAX_US 20 +#define TXRESERVE_MIN 0 +#define TXRESERVE_MAX 7 + +#define MTK_PHY_ANARG_RG 0x10 +#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) + +/* Registers on MDIO_MMD_VEND1 */ +#define MTK_PHY_TXVLD_DA_RG 0x12 +#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) +#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 0x16 +#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) +#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 0x17 +#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 0x18 +#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 0x19 +#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 0x20 +#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 0x21 +#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) + +#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 0x22 +#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) +#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) + +#define MTK_PHY_RXADC_CTRL_RG7 0xc6 +#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) + +#define MTK_PHY_RXADC_CTRL_RG9 0xc8 +#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) +#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) +#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) +#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) + +#define MTK_PHY_LDO_OUTPUT_V 0xd7 + +#define MTK_PHY_RG_ANA_CAL_RG0 0xdb +#define MTK_PHY_RG_CAL_CKINV BIT(12) +#define MTK_PHY_RG_ANA_CALEN BIT(8) +#define MTK_PHY_RG_ZCALEN_A BIT(0) + +#define MTK_PHY_RG_ANA_CAL_RG1 0xdc +#define MTK_PHY_RG_ZCALEN_B BIT(12) +#define MTK_PHY_RG_ZCALEN_C BIT(8) +#define MTK_PHY_RG_ZCALEN_D BIT(4) +#define MTK_PHY_RG_TXVOS_CALEN BIT(0) + +#define MTK_PHY_RG_ANA_CAL_RG5 0xe0 +#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) + +#define MTK_PHY_RG_TX_FILTER 0xfe + +#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120 0x120 +#define MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK GENMASK(12, 8) +#define MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK GENMASK(4, 0) + +#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122 0x122 +#define MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK GENMASK(7, 0) + +#define MTK_PHY_RG_TESTMUX_ADC_CTRL 0x144 +#define MTK_PHY_RG_TXEN_DIG_MASK GENMASK(5, 5) + +#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B 0x172 +#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) +#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D 0x173 +#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) +#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) + +#define MTK_PHY_RG_AD_CAL_COMP 0x17a +#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) + +#define MTK_PHY_RG_AD_CAL_CLK 0x17b +#define MTK_PHY_DA_CAL_CLK BIT(0) + +#define MTK_PHY_RG_AD_CALIN 0x17c +#define MTK_PHY_DA_CALIN_FLAG BIT(0) + +#define MTK_PHY_RG_DASN_DAC_IN0_A 0x17d +#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_B 0x17e +#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_C 0x17f +#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN0_D 0x180 +#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_A 0x181 +#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_B 0x182 +#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_C 0x183 +#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DASN_DAC_IN1_D 0x184 +#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) + +#define MTK_PHY_RG_DEV1E_REG19b 0x19b +#define MTK_PHY_BYPASS_DSP_LPI_READY BIT(8) + +#define MTK_PHY_RG_LP_IIR2_K1_L 0x22a +#define MTK_PHY_RG_LP_IIR2_K1_U 0x22b +#define MTK_PHY_RG_LP_IIR2_K2_L 0x22c +#define MTK_PHY_RG_LP_IIR2_K2_U 0x22d +#define MTK_PHY_RG_LP_IIR2_K3_L 0x22e +#define MTK_PHY_RG_LP_IIR2_K3_U 0x22f +#define MTK_PHY_RG_LP_IIR2_K4_L 0x230 +#define MTK_PHY_RG_LP_IIR2_K4_U 0x231 +#define MTK_PHY_RG_LP_IIR2_K5_L 0x232 +#define MTK_PHY_RG_LP_IIR2_K5_U 0x233 + +#define MTK_PHY_RG_DEV1E_REG234 0x234 +#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) +#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) +#define MTK_PHY_TR_LP_IIR_EEE_EN BIT(12) + +#define MTK_PHY_RG_LPF_CNT_VAL 0x235 + +#define MTK_PHY_RG_DEV1E_REG238 0x238 +#define MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK GENMASK(8, 0) +#define MTK_PHY_LPI_SLV_SEND_TX_EN BIT(12) + +#define MTK_PHY_RG_DEV1E_REG239 0x239 +#define MTK_PHY_LPI_SEND_LOC_TIMER_MASK GENMASK(8, 0) +#define MTK_PHY_LPI_TXPCS_LOC_RCV BIT(12) + +#define MTK_PHY_RG_DEV1E_REG27C 0x27c +#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) +#define MTK_PHY_RG_DEV1E_REG27D 0x27d +#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) + +#define MTK_PHY_RG_DEV1E_REG2C7 0x2c7 +#define MTK_PHY_MAX_GAIN_MASK GENMASK(4, 0) +#define MTK_PHY_MIN_GAIN_MASK GENMASK(12, 8) + +#define MTK_PHY_RG_DEV1E_REG2D1 0x2d1 +#define MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK GENMASK(7, 0) +#define MTK_PHY_LPI_SKIP_SD_SLV_TR BIT(8) +#define MTK_PHY_LPI_TR_READY BIT(9) +#define MTK_PHY_LPI_VCO_EEE_STG0_EN BIT(10) + +#define MTK_PHY_RG_DEV1E_REG323 0x323 +#define MTK_PHY_EEE_WAKE_MAS_INT_DC BIT(0) +#define MTK_PHY_EEE_WAKE_SLV_INT_DC BIT(4) + +#define MTK_PHY_RG_DEV1E_REG324 0x324 +#define MTK_PHY_SMI_DETCNT_MAX_MASK GENMASK(5, 0) +#define MTK_PHY_SMI_DET_MAX_EN BIT(8) + +#define MTK_PHY_RG_DEV1E_REG326 0x326 +#define MTK_PHY_LPI_MODE_SD_ON BIT(0) +#define MTK_PHY_RESET_RANDUPD_CNT BIT(1) +#define MTK_PHY_TREC_UPDATE_ENAB_CLR BIT(2) +#define MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF BIT(4) +#define MTK_PHY_TR_READY_SKIP_AFE_WAKEUP BIT(5) + +#define MTK_PHY_LDO_PUMP_EN_PAIRAB 0x502 +#define MTK_PHY_LDO_PUMP_EN_PAIRCD 0x503 + +#define MTK_PHY_DA_TX_R50_PAIR_A 0x53d +#define MTK_PHY_DA_TX_R50_PAIR_B 0x53e +#define MTK_PHY_DA_TX_R50_PAIR_C 0x53f +#define MTK_PHY_DA_TX_R50_PAIR_D 0x540 + +/* Registers on MDIO_MMD_VEND2 */ +#define MTK_PHY_LED0_ON_CTRL 0x24 +#define MTK_PHY_LED0_ON_MASK GENMASK(6, 0) +#define MTK_PHY_LED0_ON_LINK1000 BIT(0) +#define MTK_PHY_LED0_ON_LINK100 BIT(1) +#define MTK_PHY_LED0_ON_LINK10 BIT(2) +#define MTK_PHY_LED0_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED0_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED0_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED0_FORCE_ON BIT(6) +#define MTK_PHY_LED0_POLARITY BIT(14) +#define MTK_PHY_LED0_ENABLE BIT(15) + +#define MTK_PHY_LED0_BLINK_CTRL 0x25 +#define MTK_PHY_LED0_1000TX BIT(0) +#define MTK_PHY_LED0_1000RX BIT(1) +#define MTK_PHY_LED0_100TX BIT(2) +#define MTK_PHY_LED0_100RX BIT(3) +#define MTK_PHY_LED0_10TX BIT(4) +#define MTK_PHY_LED0_10RX BIT(5) +#define MTK_PHY_LED0_COLLISION BIT(6) +#define MTK_PHY_LED0_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED0_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED0_FORCE_BLINK BIT(9) + +#define MTK_PHY_LED1_ON_CTRL 0x26 +#define MTK_PHY_LED1_ON_MASK GENMASK(6, 0) +#define MTK_PHY_LED1_ON_LINK1000 BIT(0) +#define MTK_PHY_LED1_ON_LINK100 BIT(1) +#define MTK_PHY_LED1_ON_LINK10 BIT(2) +#define MTK_PHY_LED1_ON_LINKDOWN BIT(3) +#define MTK_PHY_LED1_ON_FDX BIT(4) /* Full duplex */ +#define MTK_PHY_LED1_ON_HDX BIT(5) /* Half duplex */ +#define MTK_PHY_LED1_FORCE_ON BIT(6) +#define MTK_PHY_LED1_POLARITY BIT(14) +#define MTK_PHY_LED1_ENABLE BIT(15) + +#define MTK_PHY_LED1_BLINK_CTRL 0x27 +#define MTK_PHY_LED1_1000TX BIT(0) +#define MTK_PHY_LED1_1000RX BIT(1) +#define MTK_PHY_LED1_100TX BIT(2) +#define MTK_PHY_LED1_100RX BIT(3) +#define MTK_PHY_LED1_10TX BIT(4) +#define MTK_PHY_LED1_10RX BIT(5) +#define MTK_PHY_LED1_COLLISION BIT(6) +#define MTK_PHY_LED1_RX_CRC_ERR BIT(7) +#define MTK_PHY_LED1_RX_IDLE_ERR BIT(8) +#define MTK_PHY_LED1_FORCE_BLINK BIT(9) + +#define MTK_PHY_RG_BG_RASEL 0x115 +#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) + +/* These macro privides efuse parsing for internal phy. */ +#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) +#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) + +#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) +#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) + +#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) +#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) + +#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) +#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) + +enum { + NO_PAIR, + PAIR_A, + PAIR_B, + PAIR_C, + PAIR_D, +}; + +enum { + GPHY_PORT0, + GPHY_PORT1, + GPHY_PORT2, + GPHY_PORT3, +}; + +enum calibration_mode { + EFUSE_K, + SW_K +}; + +enum CAL_ITEM { + REXT, + TX_OFFSET, + TX_AMP, + TX_R50, + TX_VCM +}; + +enum CAL_MODE { + EFUSE_M, + SW_M +}; + +struct mtk_socphy_shared_priv { + u32 boottrap; +}; + +static int mtk_socphy_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +} + +static int mtk_socphy_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page); +} + +/* One calibration cycle consists of: + * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high + * until AD_CAL_COMP is ready to output calibration result. + * 2.Wait until DA_CAL_CLK is available. + * 3.Fetch AD_CAL_COMP_OUT. + */ +static int cal_cycle(struct phy_device *phydev, int devad, + u32 regnum, u16 mask, u16 cal_val) +{ + int reg_val; + int ret; + + phy_modify_mmd(phydev, devad, regnum, + mask, cal_val); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); + + ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_AD_CAL_CLK, reg_val, + reg_val & MTK_PHY_DA_CAL_CLK, 500, + ANALOG_INTERNAL_OPERATION_MAX_US, false); + if (ret) { + phydev_err(phydev, "Calibration cycle timeout\n"); + return ret; + } + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, + MTK_PHY_DA_CALIN_FLAG); + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> + MTK_PHY_AD_CAL_COMP_OUT_SHIFT; + phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); + + return ret; +} + +static int rext_fill_result(struct phy_device *phydev, u16 *buf) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, + MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, + MTK_PHY_RG_BG_RASEL_MASK, buf[1]); + + return 0; +} + +static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 rext_cal_val[2]; + + rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); + rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); + rext_fill_result(phydev, rext_cal_val); + + return 0; +} + +static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, + MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, + MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, + MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, + MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); + + return 0; +} + +static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 tx_offset_cal_val[4]; + + tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); + tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); + tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); + tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); + + tx_offset_fill_result(phydev, tx_offset_cal_val); + + return 0; +} + +static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) +{ + int i; + int bias[16] = {}; + const int vals_9461[16] = { 7, 1, 4, 7, + 7, 1, 4, 7, + 7, 1, 4, 7, + 7, 1, 4, 7 }; + const int vals_9481[16] = { 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10, + 10, 6, 6, 10 }; + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + /* We add some calibration to efuse values + * due to board level influence. + * GBE: +7, TBT: +1, HBT: +4, TST: +7 + */ + memcpy(bias, (const void *)vals_9461, sizeof(bias)); + break; + case MTK_GPHY_ID_MT7988: + memcpy(bias, (const void *)vals_9481, sizeof(bias)); + break; + } + + /* Prevent overflow */ + for (i = 0; i < 12; i++) { + if (buf[i >> 2] + bias[i] > 63) { + buf[i >> 2] = 63; + bias[i] = 0; + } + } + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, + MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, + MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, + MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, + MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, + MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, + MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, + MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, + MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, + MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, + MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, + MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, + MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, + MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, + MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, + MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); + + return 0; +} + +static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) +{ + u16 tx_amp_cal_val[4]; + + tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); + tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); + tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); + tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); + tx_amp_fill_result(phydev, tx_amp_cal_val); + + return 0; +} + +static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, + u8 txg_calen_x) +{ + int bias = 0; + u16 reg, val; + + if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988) + bias = -2; + + val = clamp_val(bias + tx_r50_cal_val, 0, 63); + + switch (txg_calen_x) { + case PAIR_A: + reg = MTK_PHY_DA_TX_R50_PAIR_A; + break; + case PAIR_B: + reg = MTK_PHY_DA_TX_R50_PAIR_B; + break; + case PAIR_C: + reg = MTK_PHY_DA_TX_R50_PAIR_C; + break; + case PAIR_D: + reg = MTK_PHY_DA_TX_R50_PAIR_D; + break; + default: + return -EINVAL; + } + + phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); + + return 0; +} + +static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, + u8 txg_calen_x) +{ + u16 tx_r50_cal_val; + + switch (txg_calen_x) { + case PAIR_A: + tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); + break; + case PAIR_B: + tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); + break; + case PAIR_C: + tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); + break; + case PAIR_D: + tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); + break; + default: + return -EINVAL; + } + tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); + + return 0; +} + +static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) +{ + u8 lower_idx, upper_idx, txreserve_val; + u8 lower_ret, upper_ret; + int ret; + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ANA_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_CAL_CKINV); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + + switch (rg_txreserve_x) { + case PAIR_A: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_A, + MTK_PHY_DASN_DAC_IN0_A_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_A, + MTK_PHY_DASN_DAC_IN1_A_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ZCALEN_A); + break; + case PAIR_B: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_B, + MTK_PHY_DASN_DAC_IN0_B_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_B, + MTK_PHY_DASN_DAC_IN1_B_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_B); + break; + case PAIR_C: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_C, + MTK_PHY_DASN_DAC_IN0_C_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_C, + MTK_PHY_DASN_DAC_IN1_C_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_C); + break; + case PAIR_D: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN0_D, + MTK_PHY_DASN_DAC_IN0_D_MASK); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DASN_DAC_IN1_D, + MTK_PHY_DASN_DAC_IN1_D_MASK); + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_D); + break; + default: + ret = -EINVAL; + goto restore; + } + + lower_idx = TXRESERVE_MIN; + upper_idx = TXRESERVE_MAX; + + phydev_dbg(phydev, "Start TX-VCM SW cal.\n"); + while ((upper_idx - lower_idx) > 1) { + txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); + ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + txreserve_val << 12 | txreserve_val << 8 | + txreserve_val << 4 | txreserve_val); + if (ret == 1) { + upper_idx = txreserve_val; + upper_ret = ret; + } else if (ret == 0) { + lower_idx = txreserve_val; + lower_ret = ret; + } else { + goto restore; + } + } + + if (lower_idx == TXRESERVE_MIN) { + lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, + MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + lower_idx << 12 | lower_idx << 8 | + lower_idx << 4 | lower_idx); + ret = lower_ret; + } else if (upper_idx == TXRESERVE_MAX) { + upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, + MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + upper_idx << 12 | upper_idx << 8 | + upper_idx << 4 | upper_idx); + ret = upper_ret; + } + if (ret < 0) + goto restore; + + /* We calibrate TX-VCM in different logic. Check upper index and then + * lower index. If this calibration is valid, apply lower index's result. + */ + ret = upper_ret - lower_ret; + if (ret == 1) { + ret = 0; + /* Make sure we use upper_idx in our calibration system */ + cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + upper_idx << 12 | upper_idx << 8 | + upper_idx << 4 | upper_idx); + phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx); + } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && + lower_ret == 1) { + ret = 0; + cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, + MTK_PHY_DA_RX_PSBN_TBT_MASK | + MTK_PHY_DA_RX_PSBN_HBT_MASK | + MTK_PHY_DA_RX_PSBN_GBE_MASK | + MTK_PHY_DA_RX_PSBN_LP_MASK, + lower_idx << 12 | lower_idx << 8 | + lower_idx << 4 | lower_idx); + phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n", + lower_idx); + } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && + lower_ret == 0) { + ret = 0; + phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n", + upper_idx); + } else { + ret = -EINVAL; + } + +restore: + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ANA_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_TXVOS_CALEN); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, + MTK_PHY_RG_ZCALEN_A); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, + MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | + MTK_PHY_RG_ZCALEN_D); + + return ret; +} + +static void mt798x_phy_common_finetune(struct phy_device *phydev) +{ + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* EnabRandUpdTrig = 1 */ + __phy_write(phydev, 0x11, 0x2f00); + __phy_write(phydev, 0x12, 0xe); + __phy_write(phydev, 0x10, 0x8fb0); + + /* NormMseLoThresh = 85 */ + __phy_write(phydev, 0x11, 0x55a0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x83aa); + + /* TrFreeze = 0 */ + __phy_write(phydev, 0x11, 0x0); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9686); + + /* SSTrKp1000Slv = 5 */ + __phy_write(phydev, 0x11, 0xbaef); + __phy_write(phydev, 0x12, 0x2e); + __phy_write(phydev, 0x10, 0x968c); + + /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, + * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 + */ + __phy_write(phydev, 0x11, 0xd10a); + __phy_write(phydev, 0x12, 0x34); + __phy_write(phydev, 0x10, 0x8f82); + + /* VcoSlicerThreshBitsHigh */ + __phy_write(phydev, 0x11, 0x5555); + __phy_write(phydev, 0x12, 0x55); + __phy_write(phydev, 0x10, 0x8ec0); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, + MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, + BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); + + /* rg_tr_lpf_cnt_val = 512 */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); + + /* IIR2 related */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); + + /* FFE peaking */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, + MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, + MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); + + /* Disable LDO pump */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); + /* Adjust LDO output voltage */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); +} + +static void mt7981_phy_finetune(struct phy_device *phydev) +{ + u16 val[8] = { 0x01ce, 0x01c1, + 0x020f, 0x0202, + 0x03d0, 0x03c0, + 0x0013, 0x0005 }; + int i, k; + + /* 100M eye finetune: + * Keep middle level of TX MLT3 shapper as default. + * Only change TX MLT3 overshoot level here. + */ + for (k = 0, i = 1; i < 12; i++) { + if (i % 3 == 0) + continue; + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]); + } + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */ + __phy_write(phydev, 0x11, 0xc71); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + + /* ResetSyncOffset = 6 */ + __phy_write(phydev, 0x11, 0x600); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate = 1 */ + __phy_write(phydev, 0x11, 0x4c2a); + __phy_write(phydev, 0x12, 0x3e); + __phy_write(phydev, 0x10, 0x8fa4); + + /* FfeUpdGainForce = 4 */ + __phy_write(phydev, 0x11, 0x240); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9680); + + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt7988_phy_finetune(struct phy_device *phydev) +{ + u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, + 0x020d, 0x0206, 0x0384, 0x03d0, + 0x03c6, 0x030a, 0x0011, 0x0005 }; + int i; + + /* Set default MLT3 shaper first */ + for (i = 0; i < 12; i++) + phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); + + /* TCT finetune */ + phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); + + /* Disable TX power saving */ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, + MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + + /* SlvDSPreadyTime = 24, MasDSPreadyTime = 12 */ + __phy_write(phydev, 0x11, 0x671); + __phy_write(phydev, 0x12, 0xc); + __phy_write(phydev, 0x10, 0x8fae); + + /* ResetSyncOffset = 5 */ + __phy_write(phydev, 0x11, 0x500); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8fc0); + + /* VgaDecRate is 1 at default on mt7988 */ + + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); + /* TxClkOffset = 2 */ + __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, + FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); +} + +static void mt798x_phy_eee(struct phy_device *phydev) +{ + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120, + MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK | + MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) | + FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + 0xff)); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_TESTMUX_ADC_CTRL, + MTK_PHY_RG_TXEN_DIG_MASK); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY); + + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238, + MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK | + MTK_PHY_LPI_SLV_SEND_TX_EN, + FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239, + MTK_PHY_LPI_SEND_LOC_TIMER_MASK | + MTK_PHY_LPI_TXPCS_LOC_RCV, + FIELD_PREP(MTK_PHY_LPI_SEND_LOC_TIMER_MASK, 0x117)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7, + MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK, + FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) | + FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13)); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1, + MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, + FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK, + 0x33) | + MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY | + MTK_PHY_LPI_VCO_EEE_STG0_EN); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323, + MTK_PHY_EEE_WAKE_MAS_INT_DC | + MTK_PHY_EEE_WAKE_SLV_INT_DC); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324, + MTK_PHY_SMI_DETCNT_MAX_MASK, + FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) | + MTK_PHY_SMI_DET_MAX_EN); + + phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326, + MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT | + MTK_PHY_TREC_UPDATE_ENAB_CLR | + MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF | + MTK_PHY_TR_READY_SKIP_AFE_WAKEUP); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); + /* Regsigdet_sel_1000 = 0 */ + __phy_write(phydev, 0x11, 0xb); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x9690); + + /* REG_EEE_st2TrKf1000 = 3 */ + __phy_write(phydev, 0x11, 0x114f); + __phy_write(phydev, 0x12, 0x2); + __phy_write(phydev, 0x10, 0x969a); + + /* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */ + __phy_write(phydev, 0x11, 0x3028); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x969e); + + /* RegEEE_slv_wake_int_timer_tar = 8 */ + __phy_write(phydev, 0x11, 0x5010); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96a0); + + /* RegEEE_trfreeze_timer2 = 586 */ + __phy_write(phydev, 0x11, 0x24a); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96a8); + + /* RegEEE100Stg1_tar = 16 */ + __phy_write(phydev, 0x11, 0x3210); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96b8); + + /* REGEEE_wake_slv_tr_wait_dfesigdet_en = 1 */ + __phy_write(phydev, 0x11, 0x1463); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x96ca); + + /* DfeTailEnableVgaThresh1000 = 27 */ + __phy_write(phydev, 0x11, 0x36); + __phy_write(phydev, 0x12, 0x0); + __phy_write(phydev, 0x10, 0x8f80); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3); + __phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK, + FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c)); + + __phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK, + FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc)); + phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); + + phy_modify_mmd(phydev, MDIO_MMD_VEND1, + MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122, + MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, + FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff)); +} + +static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, + u8 start_pair, u8 end_pair) +{ + u8 pair_n; + int ret; + + for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { + /* TX_OFFSET & TX_AMP have no SW calibration. */ + switch (cal_item) { + case TX_VCM: + ret = tx_vcm_cal_sw(phydev, pair_n); + break; + default: + return -EINVAL; + } + if (ret) + return ret; + } + return 0; +} + +static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, + u8 start_pair, u8 end_pair, u32 *buf) +{ + u8 pair_n; + int ret; + + for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { + /* TX_VCM has no efuse calibration. */ + switch (cal_item) { + case REXT: + ret = rext_cal_efuse(phydev, buf); + break; + case TX_OFFSET: + ret = tx_offset_cal_efuse(phydev, buf); + break; + case TX_AMP: + ret = tx_amp_cal_efuse(phydev, buf); + break; + case TX_R50: + ret = tx_r50_cal_efuse(phydev, buf, pair_n); + break; + default: + return -EINVAL; + } + if (ret) + return ret; + } + + return 0; +} + +static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, + enum CAL_MODE cal_mode, u8 start_pair, + u8 end_pair, u32 *buf) +{ + int ret; + + switch (cal_mode) { + case EFUSE_M: + ret = cal_efuse(phydev, cal_item, start_pair, + end_pair, buf); + break; + case SW_M: + ret = cal_sw(phydev, cal_item, start_pair, end_pair); + break; + default: + return -EINVAL; + } + + if (ret) { + phydev_err(phydev, "cal %d failed\n", cal_item); + return -EIO; + } + + return 0; +} + +static int mt798x_phy_calibration(struct phy_device *phydev) +{ + int ret = 0; + u32 *buf; + size_t len; + struct nvmem_cell *cell; + + cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return PTR_ERR(cell); + return 0; + } + + buf = (u32 *)nvmem_cell_read(cell, &len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + nvmem_cell_put(cell); + + if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) { + phydev_err(phydev, "invalid efuse data\n"); + ret = -EINVAL; + goto out; + } + + ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf); + if (ret) + goto out; + ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf); + if (ret) + goto out; + +out: + kfree(buf); + return ret; +} + +static int mt798x_phy_config_init(struct phy_device *phydev) +{ + switch (phydev->drv->phy_id) { + case MTK_GPHY_ID_MT7981: + mt7981_phy_finetune(phydev); + break; + case MTK_GPHY_ID_MT7988: + mt7988_phy_finetune(phydev); + break; + } + + mt798x_phy_common_finetune(phydev); + mt798x_phy_eee(phydev); + + return mt798x_phy_calibration(phydev); +} + +static int mt7988_phy_setup_led(struct phy_device *phydev) +{ + struct mtk_socphy_shared_priv *priv = phydev->shared->priv; + int port = phydev->mdio.addr; + u32 reg = priv->boottrap; + struct pinctrl *pinctrl; + + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY | + MTK_PHY_LED0_ON_LINK10 | + MTK_PHY_LED0_ON_LINK100 | + MTK_PHY_LED0_ON_LINK1000); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED1_ENABLE | MTK_PHY_LED1_POLARITY | + MTK_PHY_LED1_ON_LINK10 | + MTK_PHY_LED1_ON_LINK100 | + MTK_PHY_LED1_ON_LINK1000); + + if ((port == GPHY_PORT0 && reg & BIT(8)) || + (port == GPHY_PORT1 && reg & BIT(9)) || + (port == GPHY_PORT2 && reg & BIT(10)) || + (port == GPHY_PORT3 && reg & BIT(11))) { + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, + MTK_PHY_LED0_POLARITY); + phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL, + MTK_PHY_LED1_POLARITY); + } + + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL, + MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX | + MTK_PHY_LED0_100TX | MTK_PHY_LED0_100RX | + MTK_PHY_LED0_10TX | MTK_PHY_LED0_10RX); + phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_BLINK_CTRL, + MTK_PHY_LED1_1000TX | MTK_PHY_LED1_1000RX | + MTK_PHY_LED1_100TX | MTK_PHY_LED1_100RX | + MTK_PHY_LED1_10TX | MTK_PHY_LED1_10RX); + + pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led"); + if (IS_ERR(pinctrl)) { + dev_err(&phydev->mdio.bus->dev, "Failed to setup LED pins\n"); + return PTR_ERR(pinctrl); + } + + return 0; +} + +static int mt7988_phy_probe_shared(struct phy_device *phydev) +{ + struct mtk_socphy_shared_priv *priv = phydev->shared->priv; + void __iomem *boottrap; + struct device_node *np; + u32 reg; + + np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap"); + if (!np) + return -ENOENT; + + boottrap = of_iomap(np, 0); + if (!boottrap) + return -ENOMEM; + + reg = readl(boottrap); + iounmap(boottrap); + + priv->boottrap = reg; + + return 0; +} + +static int mt7981_phy_probe(struct phy_device *phydev) +{ + return mt798x_phy_calibration(phydev); +} + +static int mt7988_phy_probe(struct phy_device *phydev) +{ + int err; + + err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0, + sizeof(struct mtk_socphy_shared_priv)); + if (err) + return err; + + if (phy_package_probe_once(phydev)) { + err = mt7988_phy_probe_shared(phydev); + if (err) + return err; + } + + mt7988_phy_setup_led(phydev); + + return mt798x_phy_calibration(phydev); +} + +static struct phy_driver mtk_socphy_driver[] = { + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981), + .name = "MediaTek MT7981 PHY", + .config_init = mt798x_phy_config_init, + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_handle_interrupt_no_ack, + .probe = mt7981_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_socphy_read_page, + .write_page = mtk_socphy_write_page, + }, + { + PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988), + .name = "MediaTek MT7988 PHY", + .config_init = mt798x_phy_config_init, + .config_intr = genphy_no_config_intr, + .handle_interrupt = genphy_handle_interrupt_no_ack, + .probe = mt7988_phy_probe, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = mtk_socphy_read_page, + .write_page = mtk_socphy_write_page, + }, +}; + +module_phy_driver(mtk_socphy_driver); + +static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = { + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) }, + { PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) }, + { } +}; + +MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver"); +MODULE_AUTHOR("Daniel Golle "); +MODULE_AUTHOR("SkyLake Huang "); +MODULE_LICENSE("GPL"); + +MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl); diff --git a/target/linux/mediatek/filogic/config-5.15 b/target/linux/mediatek/filogic/config-5.15 index 2036c9488422f6..4e15e1e1921667 100644 --- a/target/linux/mediatek/filogic/config-5.15 +++ b/target/linux/mediatek/filogic/config-5.15 @@ -205,8 +205,9 @@ CONFIG_MAXLINEAR_GPHY=y CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y +CONFIG_MEDIATEK_2P5G_PHY=y CONFIG_MEDIATEK_GE_PHY=y -CONFIG_MEDIATEK_GE_PHY_SOC=y +CONFIG_MEDIATEK_GE_SOC_PHY=y CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 diff --git a/target/linux/mediatek/mt7622/config-5.15 b/target/linux/mediatek/mt7622/config-5.15 index dcb85abe167f9a..ce51a74867d471 100644 --- a/target/linux/mediatek/mt7622/config-5.15 +++ b/target/linux/mediatek/mt7622/config-5.15 @@ -230,8 +230,9 @@ CONFIG_MAGIC_SYSRQ=y CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y +# CONFIG_MEDIATEK_2P5G_PHY is not set CONFIG_MEDIATEK_GE_PHY=y -# CONFIG_MEDIATEK_GE_PHY_SOC is not set +# CONFIG_MEDIATEK_GE_SOC_PHY is not set CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 diff --git a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch index 04ca80c213d767..84718d300b5df7 100644 --- a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch +++ b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch @@ -103,7 +103,7 @@ Signed-off-by: David S. Miller ret = mtk_mdio_busy_wait(eth); if (ret < 0) -@@ -1018,6 +1061,7 @@ static int mtk_mdio_init(struct mtk_eth +@@ -1013,6 +1056,7 @@ static int mtk_mdio_init(struct mtk_eth eth->mii_bus->name = "mdio"; eth->mii_bus->read = mtk_mdio_read; eth->mii_bus->write = mtk_mdio_write; diff --git a/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch b/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch index 8712161cb82b8c..f9be0055196390 100644 --- a/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch +++ b/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch @@ -1,23 +1,27 @@ -From a6f143af419bfc3f52d82e88ac033d9833e720af Mon Sep 17 00:00:00 2001 +From 60ed9eb9605656c19ca402b7bd3f47552e901601 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 13 Feb 2023 02:33:14 +0000 Subject: [PATCH] net: phy: add driver for MediaTek SoC built-in GE PHYs Some of MediaTek's Filogic SoCs come with built-in gigabit Ethernet PHYs which require calibration data from the SoC's efuse. -Add support for these PHYs to the mediatek-ge driver if built for -MediaTek's ARM64 SoCs. +Despite the similar design the driver doesn't share any code with the +existing mediatek-ge.c, so add support for these PHYs by introducing a +new driver for only MediaTek's ARM64 SoCs. Signed-off-by: Daniel Golle --- - MAINTAINERS | 8 + - drivers/net/phy/Kconfig | 12 + - drivers/net/phy/mediatek-ge.c | 1351 +++++++++++++++++++++++++++++++++ - 3 files changed, 1371 insertions(+) + MAINTAINERS | 9 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/Makefile | 1 + + drivers/net/phy/mediatek-ge-soc.c | 1263 +++++++++++++++++++++++++++++ + drivers/net/phy/mediatek-ge.c | 3 +- + 5 files changed, 1287 insertions(+), 1 deletion(-) + create mode 100644 drivers/net/phy/mediatek-ge-soc.c --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -11791,6 +11791,14 @@ S: Maintained +@@ -11791,6 +11791,15 @@ S: Maintained F: drivers/net/pcs/pcs-mtk-lynxi.c F: include/linux/pcs/pcs-mtk-lynxi.h @@ -27,6 +31,7 @@ Signed-off-by: Daniel Golle +M: SkyLake Huang +L: netdev@vger.kernel.org +S: Maintained ++F: drivers/net/phy/mediatek-ge-soc.c +F: drivers/net/phy/mediatek-ge.c + MEDIATEK I2C CONTROLLER DRIVER @@ -38,9 +43,9 @@ Signed-off-by: Daniel Golle help Supports the MediaTek Gigabit Ethernet PHYs. -+config MEDIATEK_GE_PHY_SOC -+ bool "MediaTek SoC Ethernet PHYs" -+ depends on (ARM64 && ARCH_MEDIATEK && MEDIATEK_GE_PHY) || COMPILE_TEST ++config MEDIATEK_GE_SOC_PHY ++ tristate "MediaTek SoC Ethernet PHYs" ++ depends on (ARM64 && ARCH_MEDIATEK) || COMPILE_TEST + select NVMEM_MTK_EFUSE + help + Supports MediaTek SoC built-in Gigabit Ethernet PHYs. @@ -53,1391 +58,25 @@ Signed-off-by: Daniel Golle config MICREL_PHY tristate "Micrel PHYs" help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -81,6 +81,7 @@ obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o + obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o + obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o ++obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o + obj-$(CONFIG_MICREL_KS8995MA) += spi_ks8995.o + obj-$(CONFIG_MICREL_PHY) += micrel.o --- a/drivers/net/phy/mediatek-ge.c +++ b/drivers/net/phy/mediatek-ge.c -@@ -2,6 +2,9 @@ - #include - #include - #include -+#include -+#include -+#include - #include - - #define MTK_EXT_PAGE_ACCESS 0x1f -@@ -12,6 +15,275 @@ - #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 - #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 - -+#define ANALOG_INTERNAL_OPERATION_MAX_US (20) -+#define ZCAL_CTRL_MIN (0) -+#define ZCAL_CTRL_MAX (63) -+#define TXRESERVE_MIN (0) -+#define TXRESERVE_MAX (7) -+ -+#define MTK_PHY_ANARG_RG (0x10) -+#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) -+ -+/* Registers on MDIO_MMD_VEND1 */ -+enum { -+ MTK_PHY_MIDDLE_LEVEL_SHAPPER_0TO1 = 0, -+ MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1, -+ MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1, -+ MTK_PHY_MIDDLE_LEVEL_SHAPPER_1TO0, -+ MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0, -+ MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0, -+ MTK_PHY_MIDDLE_LEVEL_SHAPPER_0TON1, /* N means negative */ -+ MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1, -+ MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1, -+ MTK_PHY_MIDDLE_LEVEL_SHAPPER_N1TO0, -+ MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0, -+ MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0, -+ MTK_PHY_TX_MLT3_END, -+}; -+ -+#define MTK_PHY_TXVLD_DA_RG (0x12) -+#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) -+#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 (0x16) -+#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) -+#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 (0x17) -+#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 (0x18) -+#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 (0x19) -+#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 (0x20) -+#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 (0x21) -+#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 (0x22) -+#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) -+#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_TANA_CAL_MODE (0xc1) -+#define MTK_PHY_TANA_CAL_MODE_SHIFT (8) -+ -+#define MTK_PHY_RXADC_CTRL_RG7 (0xc6) -+#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) -+ -+#define MTK_PHY_RXADC_CTRL_RG9 (0xc8) -+#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) -+#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) -+#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) -+#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) -+ -+#define MTK_PHY_LDO_OUTPUT_V (0xd7) -+ -+#define MTK_PHY_RG_ANA_CAL_RG0 (0xdb) -+#define MTK_PHY_RG_CAL_CKINV BIT(12) -+#define MTK_PHY_RG_ANA_CALEN BIT(8) -+#define MTK_PHY_RG_REXT_CALEN BIT(4) -+#define MTK_PHY_RG_ZCALEN_A BIT(0) -+ -+#define MTK_PHY_RG_ANA_CAL_RG1 (0xdc) -+#define MTK_PHY_RG_ZCALEN_B BIT(12) -+#define MTK_PHY_RG_ZCALEN_C BIT(8) -+#define MTK_PHY_RG_ZCALEN_D BIT(4) -+#define MTK_PHY_RG_TXVOS_CALEN BIT(0) -+ -+#define MTK_PHY_RG_ANA_CAL_RG2 (0xdd) -+#define MTK_PHY_RG_TXG_CALEN_A BIT(12) -+#define MTK_PHY_RG_TXG_CALEN_B BIT(8) -+#define MTK_PHY_RG_TXG_CALEN_C BIT(4) -+#define MTK_PHY_RG_TXG_CALEN_D BIT(0) -+ -+#define MTK_PHY_RG_ANA_CAL_RG5 (0xe0) -+#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) -+#define MTK_PHY_RG_ZCAL_CTRL_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_RG_TX_FILTER (0xfe) -+ -+#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B (0x172) -+#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) -+#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) -+ -+#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D (0x173) -+#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) -+#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) -+ -+#define MTK_PHY_RG_AD_CAL_COMP (0x17a) -+#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) -+ -+#define MTK_PHY_RG_AD_CAL_CLK (0x17b) -+#define MTK_PHY_DA_CAL_CLK BIT(0) -+ -+#define MTK_PHY_RG_AD_CALIN (0x17c) -+#define MTK_PHY_DA_CALIN_FLAG BIT(0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN0_A (0x17d) -+#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN0_B (0x17e) -+#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN0_C (0x17f) -+#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN0_D (0x180) -+#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN1_A (0x181) -+#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN1_B (0x182) -+#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN1_C (0x183) -+#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_DASN_DAC_IN1_D (0x180) -+#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) -+ -+#define MTK_PHY_RG_LP_IIR2_K1_L (0x22a) -+#define MTK_PHY_RG_LP_IIR2_K1_U (0x22b) -+#define MTK_PHY_RG_LP_IIR2_K2_L (0x22c) -+#define MTK_PHY_RG_LP_IIR2_K2_U (0x22d) -+#define MTK_PHY_RG_LP_IIR2_K3_L (0x22e) -+#define MTK_PHY_RG_LP_IIR2_K3_U (0x22f) -+#define MTK_PHY_RG_LP_IIR2_K4_L (0x230) -+#define MTK_PHY_RG_LP_IIR2_K4_U (0x231) -+#define MTK_PHY_RG_LP_IIR2_K5_L (0x232) -+#define MTK_PHY_RG_LP_IIR2_K5_U (0x233) -+ -+#define MTK_PHY_RG_DEV1E_REG234 (0x234) -+#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) -+#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) -+ -+#define MTK_PHY_RG_LPF_CNT_VAL (0x235) -+ -+#define MTK_PHY_RG_DEV1E_REG27C (0x27c) -+#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) -+#define MTK_PHY_RG_DEV1E_REG27D (0x27d) -+#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) -+ -+#define MTK_PHY_LDO_PUMP_EN_PAIRAB (0x502) -+#define MTK_PHY_LDO_PUMP_EN_PAIRCD (0x503) -+ -+#define MTK_PHY_DA_TX_R50_PAIR_A (0x53d) -+#define MTK_PHY_DA_TX_R50_PAIR_B (0x53e) -+#define MTK_PHY_DA_TX_R50_PAIR_C (0x53f) -+#define MTK_PHY_DA_TX_R50_PAIR_D (0x540) -+ -+/* Registers on MDIO_MMD_VEND2 */ -+#define MTK_PHY_LED0_ON_CTRL (0x24) -+#define MTK_PHY_LED0_ON_MASK GENMASK(6, 0) -+#define MTK_PHY_LED0_ON_LINK1000 BIT(0) -+#define MTK_PHY_LED0_ON_LINK100 BIT(1) -+#define MTK_PHY_LED0_ON_LINK10 BIT(2) -+#define MTK_PHY_LED0_ON_LINKDOWN BIT(3) -+#define MTK_PHY_LED0_ON_FDX BIT(4) /* Full duplex */ -+#define MTK_PHY_LED0_ON_HDX BIT(5) /* Half duplex */ -+#define MTK_PHY_LED0_FORCE_ON BIT(6) -+#define MTK_PHY_LED0_POLARITY BIT(14) -+#define MTK_PHY_LED0_ENABLE BIT(15) -+ -+#define MTK_PHY_LED0_BLINK_CTRL (0x25) -+#define MTK_PHY_LED0_1000TX BIT(0) -+#define MTK_PHY_LED0_1000RX BIT(1) -+#define MTK_PHY_LED0_100TX BIT(2) -+#define MTK_PHY_LED0_100RX BIT(3) -+#define MTK_PHY_LED0_10TX BIT(4) -+#define MTK_PHY_LED0_10RX BIT(5) -+#define MTK_PHY_LED0_COLLISION BIT(6) -+#define MTK_PHY_LED0_RX_CRC_ERR BIT(7) -+#define MTK_PHY_LED0_RX_IDLE_ERR BIT(8) -+#define MTK_PHY_LED0_FORCE_BLINK BIT(9) -+ -+#define MTK_PHY_ANA_TEST_BUS_CTRL_RG (0x100) -+#define MTK_PHY_ANA_TEST_MODE_MASK GENMASK(15, 8) -+ -+#define MTK_PHY_RG_DASN_TXT_DMY2 (0x110) -+#define MTK_PHY_TST_DMY2_MASK GENMASK(5, 0) -+ -+#define MTK_PHY_RG_BG_RASEL (0x115) -+#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) -+ -+/* These macro privides efuse parsing for internal phy. */ -+#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) -+#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) -+#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) -+#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) -+#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) -+ -+#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) -+#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) -+#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) -+#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) -+#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) -+ -+#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) -+#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) -+#define EFS_DA_TX_R50_A_10M(x) (((x) >> 12) & GENMASK(5, 0)) -+#define EFS_DA_TX_R50_B_10M(x) (((x) >> 18) & GENMASK(5, 0)) -+ -+#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) -+#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) -+ -+enum { -+ NO_PAIR, -+ PAIR_A, -+ PAIR_B, -+ PAIR_C, -+ PAIR_D, -+}; -+ -+enum { -+ GPHY_PORT0, -+ GPHY_PORT1, -+ GPHY_PORT2, -+ GPHY_PORT3, -+}; -+ -+enum calibration_mode { -+ EFUSE_K, -+ SW_K -+}; -+ -+enum CAL_ITEM { -+ REXT, -+ TX_OFFSET, -+ TX_AMP, -+ TX_R50, -+ TX_VCM -+}; -+ -+enum CAL_MODE { -+ SW_EFUSE_M, -+ EFUSE_M, -+ SW_M -+}; -+ -+const u8 mt798x_zcal_to_r50[64] = { -+ 7, 8, 9, 9, 10, 10, 11, 11, -+ 12, 13, 13, 14, 14, 15, 16, 16, -+ 17, 18, 18, 19, 20, 21, 21, 22, -+ 23, 24, 24, 25, 26, 27, 28, 29, -+ 30, 31, 32, 33, 34, 35, 36, 37, -+ 38, 40, 41, 42, 43, 45, 46, 48, -+ 49, 51, 52, 54, 55, 57, 59, 61, -+ 62, 63, 63, 63, 63, 63, 63, 63 -+}; -+ -+const char pair[4] = {'A', 'B', 'C', 'D'}; -+ - static int mtk_gephy_read_page(struct phy_device *phydev) - { - return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); -@@ -102,6 +374,1059 @@ static int mt7531_phy_config_init(struct - return 0; - } - -+#ifdef CONFIG_MEDIATEK_GE_PHY_SOC -+/* One calibration cycle consists of: -+ * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high -+ * until AD_CAL_COMP is ready to output calibration result. -+ * 2.Wait until DA_CAL_CLK is available. -+ * 3.Fetch AD_CAL_COMP_OUT. -+ */ -+static int cal_cycle(struct phy_device *phydev, int devad, -+ u32 regnum, u16 mask, u16 cal_val) -+{ -+ unsigned long timeout; -+ int reg_val; -+ int ret; -+ -+ phy_modify_mmd(phydev, devad, regnum, -+ mask, cal_val); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, -+ MTK_PHY_DA_CALIN_FLAG); -+ -+ timeout = jiffies + usecs_to_jiffies(ANALOG_INTERNAL_OPERATION_MAX_US); -+ do { -+ reg_val = phy_read_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_AD_CAL_CLK); -+ } while (time_before(jiffies, timeout) && !(reg_val & BIT(0))); -+ -+ if (!(reg_val & BIT(0))) { -+ dev_err(&phydev->mdio.dev, "Calibration cycle timeout\n"); -+ return -ETIMEDOUT; -+ } -+ -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, -+ MTK_PHY_DA_CALIN_FLAG); -+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> -+ MTK_PHY_AD_CAL_COMP_OUT_SHIFT; -+ dev_dbg(&phydev->mdio.dev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); -+ -+ return ret; -+} -+ -+static int rext_fill_result(struct phy_device *phydev, u16 *buf) -+{ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, -+ MTK_PHY_RG_BG_RASEL_MASK, buf[1]); -+ -+ return 0; -+} -+ -+static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) -+{ -+ u16 rext_cal_val[2]; -+ -+ rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); -+ rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); -+ rext_fill_result(phydev, rext_cal_val); -+ -+ return 0; -+} -+ -+static int rext_cal_sw(struct phy_device *phydev) -+{ -+ u8 rg_zcal_ctrl_def; -+ u8 zcal_lower, zcal_upper, rg_zcal_ctrl; -+ u8 lower_ret, upper_ret; -+ u16 rext_cal_val[2]; -+ int ret; -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, -+ MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_TXVOS_CALEN); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | -+ MTK_PHY_RG_REXT_CALEN); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, -+ MTK_PHY_TST_DMY2_MASK, 0x1); -+ -+ rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5) & -+ MTK_PHY_RG_ZCAL_CTRL_MASK; -+ zcal_lower = ZCAL_CTRL_MIN; -+ zcal_upper = ZCAL_CTRL_MAX; -+ -+ dev_dbg(&phydev->mdio.dev, "Start REXT SW cal.\n"); -+ while ((zcal_upper - zcal_lower) > 1) { -+ rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower + zcal_upper, 2); -+ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); -+ if (ret == 1) { -+ zcal_upper = rg_zcal_ctrl; -+ upper_ret = ret; -+ } else if (ret == 0) { -+ zcal_lower = rg_zcal_ctrl; -+ lower_ret = ret; -+ } else { -+ goto restore; -+ } -+ } -+ -+ if (zcal_lower == ZCAL_CTRL_MIN) { -+ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); -+ ret = lower_ret; -+ } else if (zcal_upper == ZCAL_CTRL_MAX) { -+ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); -+ ret = upper_ret; -+ } -+ if (ret < 0) -+ goto restore; -+ -+ ret = upper_ret - lower_ret; -+ if (ret == 1) { -+ rext_cal_val[0] = zcal_upper; -+ rext_cal_val[1] = zcal_upper >> 3; -+ rext_fill_result(phydev, rext_cal_val); -+ dev_info(&phydev->mdio.dev, "REXT SW cal result: 0x%x\n", -+ zcal_upper); -+ ret = 0; -+ } else { -+ ret = -EINVAL; -+ } -+ -+restore: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, -+ MTK_PHY_ANA_TEST_BUS_CTRL_RG, -+ MTK_PHY_ANA_TEST_MODE_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | -+ MTK_PHY_RG_REXT_CALEN); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, -+ MTK_PHY_TST_DMY2_MASK); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); -+ -+ return ret; -+} -+ -+static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) -+{ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, -+ MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, -+ MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, -+ MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, -+ MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); -+ -+ return 0; -+} -+ -+static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) -+{ -+ u16 tx_offset_cal_val[4]; -+ -+ tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); -+ tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); -+ tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); -+ tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); -+ -+ tx_offset_fill_result(phydev, tx_offset_cal_val); -+ -+ return 0; -+} -+ -+static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) -+{ -+ int i; -+ int bias[16] = {0}; -+ const int vals_9461[16] = { 7, 1, 4, 7, -+ 7, 1, 4, 7, -+ 7, 1, 4, 7, -+ 7, 1, 4, 7 }; -+ const int vals_9481[16] = { 10, 6, 6, 10, -+ 10, 6, 6, 10, -+ 10, 6, 6, 10, -+ 10, 6, 6, 10 }; -+ -+ switch (phydev->drv->phy_id) { -+ case 0x03a29461: -+ /* We add some calibration to efuse values -+ * due to board level influence. -+ * GBE: +7, TBT: +1, HBT: +4, TST: +7 -+ */ -+ memcpy(bias, (const void *)vals_9461, sizeof(bias)); -+ for (i = 0; i <= 12; i += 4) { -+ if (likely(buf[i >> 2] + bias[i] >= 32)) { -+ bias[i] -= 13; -+ } else { -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, -+ 0x5c, 0x7 << i, bias[i] << i); -+ bias[i + 1] += 13; -+ bias[i + 2] += 13; -+ bias[i + 3] += 13; -+ } -+ } -+ break; -+ case 0x03a29481: -+ memcpy(bias, (const void *)vals_9481, sizeof(bias)); -+ break; -+ default: -+ break; -+ } -+ -+ /* Prevent overflow */ -+ for (i = 0; i < 12; i++) { -+ if (buf[i >> 2] + bias[i] > 63) { -+ buf[i >> 2] = 63; -+ bias[i] = 0; -+ } else if (buf[i >> 2] + bias[i] < 0) { -+ /* Bias caused by board design may change in the future. -+ * So check negative cases, too. -+ */ -+ buf[i >> 2] = 0; -+ bias[i] = 0; -+ } -+ } -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, -+ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, -+ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, -+ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, -+ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, -+ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, -+ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, -+ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, -+ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, -+ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, -+ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, -+ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, -+ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, -+ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, -+ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, -+ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, -+ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); -+ -+ return 0; -+} -+ -+static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) -+{ -+ u16 tx_amp_cal_val[4]; -+ -+ tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); -+ tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); -+ tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); -+ tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); -+ tx_amp_fill_result(phydev, tx_amp_cal_val); -+ -+ return 0; -+} -+ -+static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, -+ u8 txg_calen_x) -+{ -+ int bias = 0; -+ u16 reg, val; -+ -+ switch (phydev->drv->phy_id) { -+ case 0x03a29481: -+ { -+ bias = -2; -+ break; -+ } -+ /* 0x03a29461 enters default case */ -+ default: -+ break; -+ } -+ -+ val = clamp_val(bias + tx_r50_cal_val, 0, 63); -+ -+ switch (txg_calen_x) { -+ case PAIR_A: -+ reg = MTK_PHY_DA_TX_R50_PAIR_A; -+ break; -+ case PAIR_B: -+ reg = MTK_PHY_DA_TX_R50_PAIR_B; -+ break; -+ case PAIR_C: -+ reg = MTK_PHY_DA_TX_R50_PAIR_C; -+ break; -+ case PAIR_D: -+ reg = MTK_PHY_DA_TX_R50_PAIR_D; -+ break; -+ } -+ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); -+ -+ return 0; -+} -+ -+static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, -+ u8 txg_calen_x) -+{ -+ u16 tx_r50_cal_val; -+ -+ switch (txg_calen_x) { -+ case PAIR_A: -+ tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); -+ break; -+ case PAIR_B: -+ tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); -+ break; -+ case PAIR_C: -+ tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); -+ break; -+ case PAIR_D: -+ tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); -+ break; -+ } -+ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); -+ -+ return 0; -+} -+ -+static int tx_r50_cal_sw(struct phy_device *phydev, u8 txg_calen_x) -+{ -+ u8 zcal_lower, zcal_upper, rg_zcal_ctrl; -+ u8 lower_ret, upper_ret; -+ u8 rg_zcal_ctrl_def; -+ u16 tx_r50_cal_val; -+ int ret; -+ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, -+ MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_TXVOS_CALEN); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, -+ BIT(txg_calen_x * 4)); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, -+ MTK_PHY_TST_DMY2_MASK, 0x1); -+ -+ rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5) & -+ MTK_PHY_RG_ZCAL_CTRL_MASK; -+ zcal_lower = ZCAL_CTRL_MIN; -+ zcal_upper = ZCAL_CTRL_MAX; -+ -+ dev_dbg(&phydev->mdio.dev, "Start TX-R50 Pair%c SW cal.\n", -+ pair[txg_calen_x]); -+ while ((zcal_upper - zcal_lower) > 1) { -+ rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower + zcal_upper, 2); -+ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); -+ if (ret == 1) { -+ zcal_upper = rg_zcal_ctrl; -+ upper_ret = ret; -+ } else if (ret == 0) { -+ zcal_lower = rg_zcal_ctrl; -+ lower_ret = ret; -+ } else { -+ goto restore; -+ } -+ } -+ -+ if (zcal_lower == ZCAL_CTRL_MIN) { -+ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); -+ ret = lower_ret; -+ } else if (zcal_upper == ZCAL_CTRL_MAX) { -+ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); -+ ret = upper_ret; -+ } -+ if (ret < 0) -+ goto restore; -+ -+ ret = upper_ret - lower_ret; -+ if (ret == 1) { -+ tx_r50_cal_val = mt798x_zcal_to_r50[zcal_upper]; -+ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); -+ dev_info(&phydev->mdio.dev, -+ "TX-R50 Pair%c SW cal result: 0x%x\n", -+ pair[txg_calen_x], zcal_lower); -+ ret = 0; -+ } else { -+ ret = -EINVAL; -+ } -+ -+restore: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, -+ MTK_PHY_ANA_TEST_MODE_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, -+ BIT(txg_calen_x * 4)); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, -+ MTK_PHY_TST_DMY2_MASK); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, -+ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); -+ -+ return ret; -+} -+ -+static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) -+{ -+ u8 lower_idx, upper_idx, txreserve_val; -+ u8 lower_ret, upper_ret; -+ int ret; -+ -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_ANA_CALEN); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_CAL_CKINV); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_TXVOS_CALEN); -+ -+ switch (rg_txreserve_x) { -+ case PAIR_A: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN0_A, -+ MTK_PHY_DASN_DAC_IN0_A_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN1_A, -+ MTK_PHY_DASN_DAC_IN1_A_MASK); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_ZCALEN_A); -+ break; -+ case PAIR_B: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN0_B, -+ MTK_PHY_DASN_DAC_IN0_B_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN1_B, -+ MTK_PHY_DASN_DAC_IN1_B_MASK); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_ZCALEN_B); -+ break; -+ case PAIR_C: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN0_C, -+ MTK_PHY_DASN_DAC_IN0_C_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN1_C, -+ MTK_PHY_DASN_DAC_IN1_C_MASK); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_ZCALEN_C); -+ break; -+ case PAIR_D: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN0_D, -+ MTK_PHY_DASN_DAC_IN0_D_MASK); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_DASN_DAC_IN1_D, -+ MTK_PHY_DASN_DAC_IN1_D_MASK); -+ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_ZCALEN_D); -+ break; -+ default: -+ ret = -EINVAL; -+ goto restore; -+ } -+ -+ lower_idx = TXRESERVE_MIN; -+ upper_idx = TXRESERVE_MAX; -+ -+ dev_dbg(&phydev->mdio.dev, "Start TX-VCM SW cal.\n"); -+ while ((upper_idx - lower_idx) > 1) { -+ txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); -+ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, -+ MTK_PHY_DA_RX_PSBN_TBT_MASK | -+ MTK_PHY_DA_RX_PSBN_HBT_MASK | -+ MTK_PHY_DA_RX_PSBN_GBE_MASK | -+ MTK_PHY_DA_RX_PSBN_LP_MASK, -+ txreserve_val << 12 | txreserve_val << 8 | -+ txreserve_val << 4 | txreserve_val); -+ if (ret == 1) { -+ upper_idx = txreserve_val; -+ upper_ret = ret; -+ } else if (ret == 0) { -+ lower_idx = txreserve_val; -+ lower_ret = ret; -+ } else { -+ goto restore; -+ } -+ } -+ -+ if (lower_idx == TXRESERVE_MIN) { -+ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RXADC_CTRL_RG9, -+ MTK_PHY_DA_RX_PSBN_TBT_MASK | -+ MTK_PHY_DA_RX_PSBN_HBT_MASK | -+ MTK_PHY_DA_RX_PSBN_GBE_MASK | -+ MTK_PHY_DA_RX_PSBN_LP_MASK, -+ lower_idx << 12 | lower_idx << 8 | -+ lower_idx << 4 | lower_idx); -+ ret = lower_ret; -+ } else if (upper_idx == TXRESERVE_MAX) { -+ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, -+ MTK_PHY_RXADC_CTRL_RG9, -+ MTK_PHY_DA_RX_PSBN_TBT_MASK | -+ MTK_PHY_DA_RX_PSBN_HBT_MASK | -+ MTK_PHY_DA_RX_PSBN_GBE_MASK | -+ MTK_PHY_DA_RX_PSBN_LP_MASK, -+ upper_idx << 12 | upper_idx << 8 | -+ upper_idx << 4 | upper_idx); -+ ret = upper_ret; -+ } -+ if (ret < 0) -+ goto restore; -+ -+ /* We calibrate TX-VCM in different logic. Check upper index and then -+ * lower index. If this calibration is valid, apply lower index's result. -+ */ -+ ret = upper_ret - lower_ret; -+ if (ret == 1) { -+ ret = 0; -+ /* Make sure we use upper_idx in our calibration system */ -+ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, -+ MTK_PHY_DA_RX_PSBN_TBT_MASK | -+ MTK_PHY_DA_RX_PSBN_HBT_MASK | -+ MTK_PHY_DA_RX_PSBN_GBE_MASK | -+ MTK_PHY_DA_RX_PSBN_LP_MASK, -+ upper_idx << 12 | upper_idx << 8 | -+ upper_idx << 4 | upper_idx); -+ dev_info(&phydev->mdio.dev, "TX-VCM SW cal result: 0x%x\n", -+ upper_idx); -+ } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && -+ lower_ret == 1) { -+ ret = 0; -+ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, -+ MTK_PHY_DA_RX_PSBN_TBT_MASK | -+ MTK_PHY_DA_RX_PSBN_HBT_MASK | -+ MTK_PHY_DA_RX_PSBN_GBE_MASK | -+ MTK_PHY_DA_RX_PSBN_LP_MASK, -+ lower_idx << 12 | lower_idx << 8 | -+ lower_idx << 4 | lower_idx); -+ dev_warn(&phydev->mdio.dev, -+ "TX-VCM SW cal result at low margin 0x%x\n", -+ lower_idx); -+ } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && -+ lower_ret == 0) { -+ ret = 0; -+ dev_warn(&phydev->mdio.dev, -+ "TX-VCM SW cal result at high margin 0x%x\n", -+ upper_idx); -+ } else { -+ ret = -EINVAL; -+ } -+ -+restore: -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_ANA_CALEN); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_TXVOS_CALEN); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, -+ MTK_PHY_RG_ZCALEN_A); -+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, -+ MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | -+ MTK_PHY_RG_ZCALEN_D); -+ -+ return ret; -+} -+ -+static inline void mt7981_phy_finetune(struct phy_device *phydev) -+{ -+ u32 i; -+ -+ /* 100M eye finetune: -+ * Keep middle level of TX MLT3 shapper as default. -+ * Only change TX MLT3 overshoot level here. -+ */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1, -+ 0x1ce); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1, -+ 0x1c1); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0, -+ 0x20f); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0, -+ 0x202); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1, -+ 0x3d0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1, -+ 0x3c0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0, -+ 0x13); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0, -+ 0x5); -+ -+ /* TX-AMP finetune: -+ * 100M +4, 1000M +6 to default value. -+ * If efuse values aren't valid, TX-AMP uses the below values. -+ */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, 0x9824); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, -+ 0x9026); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, -+ 0x2624); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, -+ 0x2426); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, -+ 0x2624); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, -+ 0x2426); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, -+ 0x2624); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, -+ 0x2426); -+ -+ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); -+ /* EnabRandUpdTrig = 1 */ -+ __phy_write(phydev, 0x11, 0x2f00); -+ __phy_write(phydev, 0x12, 0xe); -+ __phy_write(phydev, 0x10, 0x8fb0); -+ -+ /* SlvDSPreadyTime = 0xc */ -+ __phy_write(phydev, 0x11, 0x671); -+ __phy_write(phydev, 0x12, 0xc); -+ __phy_write(phydev, 0x10, 0x8fae); -+ -+ /* NormMseLoThresh = 85 */ -+ __phy_write(phydev, 0x11, 0x55a0); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x83aa); -+ -+ /* InhibitDisableDfeTail1000 = 1 */ -+ __phy_write(phydev, 0x11, 0x2b); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x8f80); -+ -+ /* SSTr related */ -+ __phy_write(phydev, 0x11, 0xbaef); -+ __phy_write(phydev, 0x12, 0x2e); -+ __phy_write(phydev, 0x10, 0x968c); -+ -+ /* VcoSlicerThreshBitsHigh */ -+ __phy_write(phydev, 0x11, 0x5555); -+ __phy_write(phydev, 0x12, 0x55); -+ __phy_write(phydev, 0x10, 0x8ec0); -+ -+ /* ResetSyncOffset = 6 */ -+ __phy_write(phydev, 0x11, 0x600); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x8fc0); -+ -+ /* VgaDecRate = 1 */ -+ __phy_write(phydev, 0x11, 0x4c2a); -+ __phy_write(phydev, 0x12, 0x3e); -+ __phy_write(phydev, 0x10, 0x8fa4); -+ -+ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); -+ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, -+ MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, -+ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); -+ -+ /* rg_tr_lpf_cnt_val = 512 */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); -+ -+ /* IIR2 related */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); -+ -+ /* FFE peaking */ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, -+ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, -+ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); -+ -+ /* TX shape */ -+ /* 10/100/1000 TX shaper is enabled by default */ -+ for (i = 0x202; i < 0x230; i += 2) { -+ if (i == 0x20c || i == 0x218 || i == 0x224) -+ continue; -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, i, 0x2219); -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, i + 1, 0x23); -+ } -+} -+ -+static inline void mt7988_phy_finetune(struct phy_device *phydev) -+{ -+ u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, -+ 0x020d, 0x0206, 0x0384, 0x03d0, -+ 0x03c6, 0x030a, 0x0011, 0x0005 }; -+ int i; -+ -+ for (i = 0; i < MTK_PHY_TX_MLT3_END; i++) -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); -+ -+ /* TCT finetune */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); -+ -+ /* Disable TX power saving */ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, -+ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); -+ -+ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); -+ /* EnabRandUpdTrig = 1 */ -+ __phy_write(phydev, 0x11, 0x2f00); -+ __phy_write(phydev, 0x12, 0xe); -+ __phy_write(phydev, 0x10, 0x8fb0); -+ -+ /* SlvDSPreadyTime = 0xc */ -+ __phy_write(phydev, 0x11, 0x671); -+ __phy_write(phydev, 0x12, 0xc); -+ __phy_write(phydev, 0x10, 0x8fae); -+ -+ /* NormMseLoThresh = 85 */ -+ __phy_write(phydev, 0x11, 0x55a0); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x83aa); -+ -+ /* InhibitDisableDfeTail1000 = 1 */ -+ __phy_write(phydev, 0x11, 0x2b); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x8f80); -+ -+ /* SSTr related */ -+ __phy_write(phydev, 0x11, 0xbaef); -+ __phy_write(phydev, 0x12, 0x2e); -+ __phy_write(phydev, 0x10, 0x968c); -+ -+ /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, -+ * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 -+ */ -+ __phy_write(phydev, 0x11, 0xd10a); -+ __phy_write(phydev, 0x12, 0x34); -+ __phy_write(phydev, 0x10, 0x8f82); -+ -+ /* VcoSlicerThreshBitsHigh */ -+ __phy_write(phydev, 0x11, 0x5555); -+ __phy_write(phydev, 0x12, 0x55); -+ __phy_write(phydev, 0x10, 0x8ec0); -+ -+ /* ResetSyncOffset = 5 */ -+ __phy_write(phydev, 0x11, 0x500); -+ __phy_write(phydev, 0x12, 0x0); -+ __phy_write(phydev, 0x10, 0x8fc0); -+ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); -+ -+ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); -+ /* TxClkOffset = 2 */ -+ __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, -+ FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); -+ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); -+ -+ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, -+ MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, -+ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); -+ -+ /* rg_tr_lpf_cnt_val = 512 */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); -+ -+ /* IIR2 related */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); -+ -+ /* FFE peaking */ -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, -+ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); -+ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, -+ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); -+ -+ /* TX shape */ -+ /* 10/100/1000 TX shaper is enabled by default */ -+ for (i = 0x202; i < 0x230; i += 2) { -+ if (i == 0x20c || i == 0x218 || i == 0x224) -+ continue; -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, i, 0x2219); -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, i + 1, 0x23); -+ } -+ -+ /* Disable LDO pump */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); -+ -+ /* Adjust LDO output voltage */ -+ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); -+} -+ -+static inline int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, -+ u8 start_pair, u8 end_pair) -+{ -+ u8 pair_n; -+ int ret; -+ -+ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { -+ /* TX_OFFSET & TX_AMP have no SW calibration. */ -+ switch (cal_item) { -+ case REXT: -+ ret = rext_cal_sw(phydev); -+ break; -+ case TX_R50: -+ ret = tx_r50_cal_sw(phydev, pair_n); -+ break; -+ case TX_VCM: -+ ret = tx_vcm_cal_sw(phydev, pair_n); -+ break; -+ default: -+ return -EINVAL; -+ } -+ if (ret) -+ return ret; -+ } -+ return 0; -+} -+ -+static inline int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, -+ u8 start_pair, u8 end_pair, u32 *buf) -+{ -+ u8 pair_n; -+ int ret; -+ -+ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { -+ /* TX_VCM has no efuse calibration. */ -+ switch (cal_item) { -+ case REXT: -+ ret = rext_cal_efuse(phydev, buf); -+ break; -+ case TX_OFFSET: -+ ret = tx_offset_cal_efuse(phydev, buf); -+ break; -+ case TX_AMP: -+ ret = tx_amp_cal_efuse(phydev, buf); -+ break; -+ case TX_R50: -+ ret = tx_r50_cal_efuse(phydev, buf, pair_n); -+ break; -+ default: -+ return -EINVAL; -+ } -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, -+ bool efs_valid, enum CAL_MODE cal_mode, u8 start_pair, -+ u8 end_pair, u32 *buf) -+{ -+ char cal_prop[5][20] = { "mediatek,rext", "mediatek,tx_offset", -+ "mediatek,tx_amp", "mediatek,tx_r50", -+ "mediatek,tx_vcm" }; -+ const char *dts_cal_mode; -+ u8 final_cal_mode = 0; -+ bool is_cal = true; -+ int ret, cal_ret; -+ -+ ret = of_property_read_string(phydev->mdio.dev.of_node, -+ cal_prop[cal_item], &dts_cal_mode); -+ -+ switch (cal_mode) { -+ case SW_EFUSE_M: -+ if ((efs_valid && ret) || -+ (efs_valid && !ret && strcmp("efuse", dts_cal_mode) == 0)) { -+ cal_ret = cal_efuse(phydev, cal_item, start_pair, -+ end_pair, buf); -+ final_cal_mode = EFUSE_K; -+ } else if ((!efs_valid && ret) || -+ (!ret && strcmp("sw", dts_cal_mode) == 0)) { -+ cal_ret = cal_sw(phydev, cal_item, start_pair, end_pair); -+ final_cal_mode = SW_K; -+ } else { -+ is_cal = false; -+ } -+ break; -+ case EFUSE_M: -+ if ((efs_valid && ret) || -+ (efs_valid && !ret && strcmp("efuse", dts_cal_mode) == 0)) { -+ cal_ret = cal_efuse(phydev, cal_item, start_pair, -+ end_pair, buf); -+ final_cal_mode = EFUSE_K; -+ } else { -+ is_cal = false; -+ } -+ break; -+ case SW_M: -+ if (ret || (!ret && strcmp("sw", dts_cal_mode) == 0)) { -+ cal_ret = cal_sw(phydev, cal_item, start_pair, end_pair); -+ final_cal_mode = SW_K; -+ } else { -+ is_cal = false; -+ } -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if (cal_ret) { -+ dev_err(&phydev->mdio.dev, "[%s]cal failed\n", cal_prop[cal_item]); -+ return -EIO; -+ } -+ -+ if (!is_cal) { -+ dev_dbg(&phydev->mdio.dev, "[%s]K mode: %s(not supported)\n", -+ cal_prop[cal_item], dts_cal_mode); -+ return -EIO; -+ } -+ -+ dev_dbg(&phydev->mdio.dev, "[%s]K mode: %s(dts: %s), efs-valid: %s\n", -+ cal_prop[cal_item], -+ final_cal_mode ? "SW" : "EFUSE", -+ ret ? "not set" : dts_cal_mode, -+ efs_valid ? "yes" : "no"); -+ return 0; -+} -+ -+static int mt798x_phy_calibration(struct phy_device *phydev) -+{ -+ int ret = 0; -+ u32 *buf; -+ bool efs_valid = true; -+ size_t len; -+ struct nvmem_cell *cell; -+ -+ if (phydev->interface != PHY_INTERFACE_MODE_GMII) -+ return -EINVAL; -+ -+ cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); -+ if (IS_ERR(cell)) { -+ if (PTR_ERR(cell) == -EPROBE_DEFER) -+ return PTR_ERR(cell); -+ return 0; -+ } -+ -+ buf = (u32 *)nvmem_cell_read(cell, &len); -+ if (IS_ERR(buf)) -+ return PTR_ERR(buf); -+ nvmem_cell_put(cell); -+ -+ if (!buf[0] || !buf[1] || !buf[2] || !buf[3]) -+ efs_valid = false; -+ -+ if (len < 4 * sizeof(u32)) { -+ dev_err(&phydev->mdio.dev, "invalid calibration data\n"); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ ret = start_cal(phydev, REXT, efs_valid, SW_EFUSE_M, -+ NO_PAIR, NO_PAIR, buf); -+ if (ret) -+ goto out; -+ ret = start_cal(phydev, TX_OFFSET, efs_valid, EFUSE_M, -+ NO_PAIR, NO_PAIR, buf); -+ if (ret) -+ goto out; -+ ret = start_cal(phydev, TX_AMP, efs_valid, EFUSE_M, -+ NO_PAIR, NO_PAIR, buf); -+ if (ret) -+ goto out; -+ ret = start_cal(phydev, TX_R50, efs_valid, EFUSE_M, -+ PAIR_A, PAIR_D, buf); -+ if (ret) -+ goto out; -+ ret = start_cal(phydev, TX_VCM, efs_valid, SW_M, -+ PAIR_A, PAIR_A, buf); -+ if (ret) -+ goto out; -+ -+out: -+ kfree(buf); -+ return ret; -+} -+ -+static int mt7981_phy_probe(struct phy_device *phydev) -+{ -+ mt7981_phy_finetune(phydev); -+ -+ return mt798x_phy_calibration(phydev); -+} -+ -+static int mt7988_phy_probe(struct phy_device *phydev) -+{ -+ struct device_node *np; -+ void __iomem *boottrap; -+ u32 reg; -+ int port; -+ -+ /* Setup LED polarity according to boottrap's polarity */ -+ np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap"); -+ if (!np) -+ return -ENOENT; -+ boottrap = of_iomap(np, 0); -+ if (!boottrap) -+ return -ENOMEM; -+ reg = readl(boottrap); -+ port = phydev->mdio.addr; -+ if ((port == GPHY_PORT0 && reg & BIT(8)) || -+ (port == GPHY_PORT1 && reg & BIT(9)) || -+ (port == GPHY_PORT2 && reg & BIT(10)) || -+ (port == GPHY_PORT3 && reg & BIT(11))) { -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, -+ MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_ON_LINK10 | -+ MTK_PHY_LED0_ON_LINK100 | -+ MTK_PHY_LED0_ON_LINK1000); -+ } else { -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, -+ MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY | -+ MTK_PHY_LED0_ON_LINK10 | -+ MTK_PHY_LED0_ON_LINK100 | -+ MTK_PHY_LED0_ON_LINK1000); -+ } -+ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL, -+ MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX | -+ MTK_PHY_LED0_100TX | MTK_PHY_LED0_100RX | -+ MTK_PHY_LED0_10TX | MTK_PHY_LED0_10RX); -+ -+ mt7988_phy_finetune(phydev); -+ -+ return mt798x_phy_calibration(phydev); -+} -+#endif -+ - static struct phy_driver mtk_gephy_driver[] = { - { - PHY_ID_MATCH_EXACT(0x03a29412), -@@ -131,6 +1456,30 @@ static struct phy_driver mtk_gephy_drive - .read_page = mtk_gephy_read_page, - .write_page = mtk_gephy_write_page, - }, -+#ifdef CONFIG_MEDIATEK_GE_PHY_SOC -+ { -+ PHY_ID_MATCH_EXACT(0x03a29461), -+ .name = "MediaTek MT7981 PHY", -+ .probe = mt7981_phy_probe, -+ .config_intr = genphy_no_config_intr, -+ .handle_interrupt = genphy_handle_interrupt_no_ack, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .read_page = mtk_gephy_read_page, -+ .write_page = mtk_gephy_write_page, -+ }, -+ { -+ PHY_ID_MATCH_EXACT(0x03a29481), -+ .name = "MediaTek MT7988 PHY", -+ .probe = mt7988_phy_probe, -+ .config_intr = genphy_no_config_intr, -+ .handle_interrupt = genphy_handle_interrupt_no_ack, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .read_page = mtk_gephy_read_page, -+ .write_page = mtk_gephy_write_page, -+ }, -+#endif - }; - +@@ -136,7 +136,8 @@ static struct phy_driver mtk_gephy_drive module_phy_driver(mtk_gephy_driver); -@@ -141,6 +1490,8 @@ static struct mdio_device_id __maybe_unu - }; - MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); -+MODULE_AUTHOR("Daniel Golle "); -+MODULE_AUTHOR("SkyLake Huang "); - MODULE_AUTHOR("DENG, Qingfang "); - MODULE_LICENSE("GPL"); + static struct mdio_device_id __maybe_unused mtk_gephy_tbl[] = { +- { PHY_ID_MATCH_VENDOR(0x03a29400) }, ++ { PHY_ID_MATCH_EXACT(0x03a29441) }, ++ { PHY_ID_MATCH_EXACT(0x03a29412) }, + { } + }; diff --git a/target/linux/mediatek/patches-5.15/732-net-phy-add-driver-for-MediaTek-2.5G-PHY.patch b/target/linux/mediatek/patches-5.15/732-net-phy-add-driver-for-MediaTek-2.5G-PHY.patch new file mode 100644 index 00000000000000..a2a3bab6322d26 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/732-net-phy-add-driver-for-MediaTek-2.5G-PHY.patch @@ -0,0 +1,39 @@ +From 128dc09b0af36772062142ce9e85b19c84ac789a Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 28 Feb 2023 17:53:37 +0000 +Subject: [PATCH] net: phy: add driver for MediaTek 2.5G PHY + +Signed-off-by: Daniel Golle +--- + drivers/net/phy/Kconfig | 7 ++ + drivers/net/phy/Makefile | 1 + + drivers/net/phy/mediatek-2p5ge.c | 220 +++++++++++++++++++++++++++++++ + 3 files changed, 226 insertions(+) + create mode 100644 drivers/net/phy/mediatek-2p5ge.c + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -304,6 +304,13 @@ config MEDIATEK_GE_SOC_PHY + present in the SoCs efuse and will dynamically calibrate VCM + (common-mode voltage) during startup. + ++config MEDIATEK_2P5G_PHY ++ tristate "MediaTek 2.5G Ethernet PHY" ++ depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST ++ default NET_MEDIATEK_SOC ++ help ++ Supports the MediaTek 2.5G Ethernet PHY. ++ + config MICREL_PHY + tristate "Micrel PHYs" + help +--- a/drivers/net/phy/Makefile ++++ b/drivers/net/phy/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_MARVELL_10G_PHY) += marvell + obj-$(CONFIG_MARVELL_PHY) += marvell.o + obj-$(CONFIG_MARVELL_88X2222_PHY) += marvell-88x2222.o + obj-$(CONFIG_MAXLINEAR_GPHY) += mxl-gpy.o ++obj-$(CONFIG_MEDIATEK_2P5G_PHY) += mediatek-2p5ge.o + obj-$(CONFIG_MEDIATEK_GE_PHY) += mediatek-ge.o + obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mediatek-ge-soc.o + obj-$(CONFIG_MESON_GXL_PHY) += meson-gxl.o