From: Alexandru Gagniuc Date: Sun, 25 May 2025 22:42:20 +0000 (-0500) Subject: qualcommbe: v6.12: enable 2.5G and 10G phylink modes in pcs-qcom-ipq9574 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F18796%2Fhead;p=thirdparty%2Fopenwrt.git qualcommbe: v6.12: enable 2.5G and 10G phylink modes in pcs-qcom-ipq9574 The PCS driver in the 6.12 patchset is the v5 submission (see link below). It solves a number of issues and crashes with teh pcs driver from the 6.6 patchset. However, this new driver is missing support for "10gbase-r", "10g-qxgmii", and 1000/2500base-x modes. Port these modes to the 6.12 patchset. "2500base-x" in particular seems to be needed to establish a 2.5G link on phy-mode="usxgmii"; Link: https://lore.kernel.org/lkml/20250207-ipq_pcs_6-14_rc1-v5-0-be2ebec32921@quicinc.com/ Signed-off-by: Alexandru Gagniuc Link: https://github.com/openwrt/openwrt/pull/18796 Signed-off-by: Robert Marko --- diff --git a/target/linux/qualcommbe/patches-6.12/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch b/target/linux/qualcommbe/patches-6.12/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch new file mode 100644 index 00000000000..529f9f7005a --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0361-net-pcs-Add-10GBASER-interface-mode-support-to-IPQ-U.patch @@ -0,0 +1,127 @@ +From e770b36f0353fd11c4628360fe412acb7f02f346 Mon Sep 17 00:00:00 2001 +From: Lei Wei +Date: Wed, 6 Mar 2024 17:40:52 +0800 +Subject: [PATCH] net: pcs: Add 10GBASER interface mode support to IPQ UNIPHY + PCS driver + +10GBASER mode is used when PCS connects with a 10G SFP module. + +Change-Id: Ifc3c3bb23811807a9b34e88771aab2c830c2327c +Signed-off-by: Lei Wei +Alex G: Use regmap to read/write registers +Signed-off-by: Alexandru Gagniuc +--- + drivers/net/pcs/pcs-qcom-ipq9574.c | 53 ++++++++++++++++++++++++++++++ + 1 file changed, 53 insertions(+) + +--- a/drivers/net/pcs/pcs-qcom-ipq9574.c ++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c +@@ -60,6 +60,9 @@ + FIELD_PREP(GENMASK(9, 2), \ + FIELD_GET(XPCS_INDIRECT_ADDR_L, reg))) + ++#define XPCS_10GBASER_STS 0x30020 ++#define XPCS_10GBASER_LINK_STS BIT(12) ++ + #define XPCS_DIG_CTRL 0x38000 + #define XPCS_USXG_ADPT_RESET BIT(10) + #define XPCS_USXG_EN BIT(9) +@@ -229,6 +232,28 @@ static void ipq_pcs_get_state_usxgmii(st + state->duplex = DUPLEX_FULL; + } + ++static void ipq_unipcs_get_state_10gbaser(struct ipq_pcs *qpcs, ++ struct phylink_link_state *state) ++{ ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(qpcs->regmap, XPCS_10GBASER_STS, &val); ++ if (ret) { ++ state->link = 0; ++ return; ++ } ++ ++ state->link = !!(val & XPCS_10GBASER_LINK_STS); ++ ++ if (!state->link) ++ return; ++ ++ state->speed = SPEED_10000; ++ state->duplex = DUPLEX_FULL; ++ state->pause |= MLO_PAUSE_TXRX_MASK; ++} ++ + static int ipq_pcs_config_mode(struct ipq_pcs *qpcs, + phy_interface_t interface) + { +@@ -251,6 +276,7 @@ static int ipq_pcs_config_mode(struct ip + val = PCS_MODE_PSGMII; + break; + case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: + val = PCS_MODE_XPCS; + rate = 312500000; + break; +@@ -355,6 +381,25 @@ static int ipq_pcs_config_usxgmii(struct + return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN); + } + ++static int ipq_unipcs_config_10gbaser(struct ipq_pcs *qpcs, ++ phy_interface_t interface) ++{ ++ int ret; ++ ++ if (qpcs->interface != interface) { ++ ret = ipq_pcs_config_mode(qpcs, interface); ++ if (ret) ++ return ret; ++ ++ /* Deassert XPCS */ ++ reset_control_deassert(qpcs->reset[XPCS_RESET]); ++ ++ qpcs->interface = interface; ++ } ++ ++ return 0; ++} ++ + static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed) + { + unsigned long rate = 0; +@@ -421,6 +466,7 @@ ipq_unipcs_link_up_clock_rate_set(struct + rate = ipq_unipcs_clock_rate_get_gmii(speed); + break; + case PHY_INTERFACE_MODE_USXGMII: ++ case PHY_INTERFACE_MODE_10GBASER: + rate = ipq_unipcs_clock_rate_get_xgmii(speed); + break; + default: +@@ -602,6 +648,9 @@ static void ipq_pcs_get_state(struct phy + case PHY_INTERFACE_MODE_USXGMII: + ipq_pcs_get_state_usxgmii(qpcs, state); + break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ ipq_unipcs_get_state_10gbaser(qpcs, state); ++ break; + default: + break; + } +@@ -631,6 +680,8 @@ static int ipq_pcs_config(struct phylink + return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface); + case PHY_INTERFACE_MODE_USXGMII: + return ipq_pcs_config_usxgmii(qpcs); ++ case PHY_INTERFACE_MODE_10GBASER: ++ return ipq_unipcs_config_10gbaser(qpcs, interface); + default: + dev_err(qpcs->dev, + "interface %s not supported\n", phy_modes(interface)); +@@ -662,6 +713,8 @@ static void ipq_pcs_link_up(struct phyli + case PHY_INTERFACE_MODE_USXGMII: + ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed); + break; ++ case PHY_INTERFACE_MODE_10GBASER: ++ break; + default: + dev_err(qpcs->dev, + "interface %s not supported\n", phy_modes(interface)); diff --git a/target/linux/qualcommbe/patches-6.12/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch b/target/linux/qualcommbe/patches-6.12/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch new file mode 100644 index 00000000000..3bf5f1f7989 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0362-net-pcs-Add-2500BASEX-interface-mode-support-to-IPQ-.patch @@ -0,0 +1,192 @@ +From a2e687df29e457621616d5d769688e6c972f9ac6 Mon Sep 17 00:00:00 2001 +From: Lei Wei +Date: Tue, 2 Apr 2024 18:28:42 +0800 +Subject: [PATCH] net: pcs: Add 2500BASEX interface mode support to IPQ UNIPHY + PCS driver + +2500BASEX mode is used when PCS connects with QCA8386 switch in a fixed +2500M link. It is also used when PCS connectes with QCA8081 PHY which +works at 2500M link speed. In addition, it can be also used when PCS +connects with a 2.5G SFP module. + +Change-Id: I3fe61113c1b3685debc20659736a9488216a029d +Signed-off-by: Lei Wei +Alex G: use regmap to read/write registers +Signed-off-by: Alexandru Gagniuc +--- + drivers/net/pcs/pcs-qcom-ipq9574.c | 94 ++++++++++++++++++++++++++++++ + 1 file changed, 94 insertions(+) + +--- a/drivers/net/pcs/pcs-qcom-ipq9574.c ++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c +@@ -29,6 +29,7 @@ + #define PCS_MODE_SGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x4) + #define PCS_MODE_QSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x1) + #define PCS_MODE_PSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x2) ++#define PCS_MODE_SGMII_PLUS FIELD_PREP(PCS_MODE_SEL_MASK, 0x8) + #define PCS_MODE_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10) + + #define PCS_MII_CTRL(x) (0x480 + 0x18 * (x)) +@@ -188,6 +189,30 @@ static void ipq_pcs_get_state_sgmii(stru + state->pause |= MLO_PAUSE_RX; + } + ++static void ipq_unipcs_get_state_2500basex(struct ipq_pcs *qpcs, ++ int index, ++ struct phylink_link_state *state) ++{ ++ unsigned int val; ++ int ret; ++ ++ ret = regmap_read(qpcs->regmap, PCS_MII_STS(index), &val); ++ if (ret) { ++ state->link = 0; ++ return; ++ } ++ ++ ++ state->link = !!(val & PCS_MII_LINK_STS); ++ ++ if (!state->link) ++ return; ++ ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; ++ state->pause |= MLO_PAUSE_TXRX_MASK; ++} ++ + static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs, + struct phylink_link_state *state) + { +@@ -272,6 +297,10 @@ static int ipq_pcs_config_mode(struct ip + case PHY_INTERFACE_MODE_QSGMII: + val = PCS_MODE_QSGMII; + break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ val = PCS_MODE_SGMII_PLUS; ++ rate = 312500000; ++ break; + case PHY_INTERFACE_MODE_PSGMII: + val = PCS_MODE_PSGMII; + break; +@@ -355,6 +384,22 @@ static int ipq_pcs_config_sgmii(struct i + PCS_MII_CTRL(index), PCS_MII_FORCE_MODE); + } + ++static int ipq_unipcs_config_2500basex(struct ipq_pcs *qpcs, ++ phy_interface_t interface) ++{ ++ int ret; ++ ++ if (qpcs->interface != interface) { ++ ret = ipq_pcs_config_mode(qpcs, interface); ++ if (ret) ++ return ret; ++ ++ qpcs->interface = interface; ++ } ++ ++ return 0; ++} ++ + static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs) + { + int ret; +@@ -421,6 +466,21 @@ static unsigned long ipq_unipcs_clock_ra + return rate; + } + ++static unsigned long ipq_unipcs_clock_rate_get_gmiiplus(int speed) ++{ ++ unsigned long rate = 0; ++ ++ switch (speed) { ++ case SPEED_2500: ++ rate = 312500000; ++ break; ++ default: ++ break; ++ } ++ ++ return rate; ++} ++ + static unsigned long ipq_unipcs_clock_rate_get_xgmii(int speed) + { + unsigned long rate = 0; +@@ -465,6 +525,9 @@ ipq_unipcs_link_up_clock_rate_set(struct + case PHY_INTERFACE_MODE_PSGMII: + rate = ipq_unipcs_clock_rate_get_gmii(speed); + break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ rate = ipq_unipcs_clock_rate_get_gmiiplus(speed); ++ break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GBASER: + rate = ipq_unipcs_clock_rate_get_xgmii(speed); +@@ -528,6 +591,25 @@ static int ipq_pcs_link_up_config_sgmii( + PCS_MII_CTRL(index), PCS_MII_ADPT_RESET); + } + ++static int ipq_unipcs_link_up_config_2500basex(struct ipq_pcs *qpcs, ++ int index, ++ int speed) ++{ ++ unsigned int val; ++ int ret; ++ ++ /* 2500BASEX do not support autoneg and do not need to ++ * configure PCS speed, only reset PCS adapter here. ++ */ ++ ret = regmap_clear_bits(qpcs->regmap, ++ PCS_MII_CTRL(index), PCS_MII_ADPT_RESET); ++ if (ret) ++ return ret; ++ ++ return regmap_set_bits(qpcs->regmap, ++ PCS_MII_CTRL(index), PCS_MII_ADPT_RESET); ++} ++ + static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed) + { + unsigned int val; +@@ -575,6 +657,10 @@ static int ipq_pcs_validate(struct phyli + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + return 0; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ /* In-band autoneg is not supported for 2500BASEX */ ++ phylink_clear(supported, Autoneg); ++ return 0; + case PHY_INTERFACE_MODE_USXGMII: + /* USXGMII only supports full duplex mode */ + phylink_clear(supported, 100baseT_Half); +@@ -645,6 +731,9 @@ static void ipq_pcs_get_state(struct phy + case PHY_INTERFACE_MODE_PSGMII: + ipq_pcs_get_state_sgmii(qpcs, index, state); + break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ ipq_unipcs_get_state_2500basex(qpcs, index, state); ++ break; + case PHY_INTERFACE_MODE_USXGMII: + ipq_pcs_get_state_usxgmii(qpcs, state); + break; +@@ -678,6 +767,8 @@ static int ipq_pcs_config(struct phylink + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: + return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface); ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return ipq_unipcs_config_2500basex(qpcs, interface); + case PHY_INTERFACE_MODE_USXGMII: + return ipq_pcs_config_usxgmii(qpcs); + case PHY_INTERFACE_MODE_10GBASER: +@@ -710,6 +801,9 @@ static void ipq_pcs_link_up(struct phyli + ret = ipq_pcs_link_up_config_sgmii(qpcs, index, + neg_mode, speed); + break; ++ case PHY_INTERFACE_MODE_2500BASEX: ++ ret = ipq_unipcs_link_up_config_2500basex(qpcs, index, speed); ++ break; + case PHY_INTERFACE_MODE_USXGMII: + ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed); + break; diff --git a/target/linux/qualcommbe/patches-6.12/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch b/target/linux/qualcommbe/patches-6.12/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch new file mode 100644 index 00000000000..9153cabe248 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0363-net-pcs-Add-1000BASEX-interface-mode-support-to-IPQ-.patch @@ -0,0 +1,91 @@ +From 07f9bb8eb006e9664d651089a1f422d045e093e3 Mon Sep 17 00:00:00 2001 +From: Lei Wei +Date: Tue, 9 Apr 2024 01:07:22 +0800 +Subject: [PATCH] net: pcs: Add 1000BASEX interface mode support to IPQ UNIPHY + PCS driver + +1000BASEX is used when PCS connects with a 1G SFP module. + +Change-Id: Ied7298de3c1ecba74e6457a07fdd6b3ceab79728 +Signed-off-by: Lei Wei +Signed-off-by: Alexandru Gagniuc +--- + drivers/net/pcs/pcs-qcom-ipq9574.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +--- a/drivers/net/pcs/pcs-qcom-ipq9574.c ++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c +@@ -31,6 +31,9 @@ + #define PCS_MODE_PSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x2) + #define PCS_MODE_SGMII_PLUS FIELD_PREP(PCS_MODE_SEL_MASK, 0x8) + #define PCS_MODE_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10) ++#define PCS_MODE_SGMII_CTRL_MASK GENMASK(6, 4) ++#define PCS_MODE_SGMII_CTRL_1000BASEX FIELD_PREP(PCS_MODE_SGMII_CTRL_MASK, \ ++ 0x0) + + #define PCS_MII_CTRL(x) (0x480 + 0x18 * (x)) + #define PCS_MII_ADPT_RESET BIT(11) +@@ -283,7 +286,7 @@ static int ipq_pcs_config_mode(struct ip + phy_interface_t interface) + { + unsigned long rate = 125000000; +- unsigned int val; ++ unsigned int val, mask = PCS_MODE_SEL_MASK; + int ret; + + /* Assert XPCS reset */ +@@ -297,6 +300,10 @@ static int ipq_pcs_config_mode(struct ip + case PHY_INTERFACE_MODE_QSGMII: + val = PCS_MODE_QSGMII; + break; ++ case PHY_INTERFACE_MODE_1000BASEX: ++ mask |= PCS_MODE_SGMII_CTRL_MASK; ++ val = PCS_MODE_SGMII | PCS_MODE_SGMII_CTRL_1000BASEX; ++ break; + case PHY_INTERFACE_MODE_2500BASEX: + val = PCS_MODE_SGMII_PLUS; + rate = 312500000; +@@ -316,7 +323,7 @@ static int ipq_pcs_config_mode(struct ip + } + + ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL, +- PCS_MODE_SEL_MASK, val); ++ mask, val); + if (ret) + return ret; + +@@ -523,6 +530,7 @@ ipq_unipcs_link_up_clock_rate_set(struct + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: + rate = ipq_unipcs_clock_rate_get_gmii(speed); + break; + case PHY_INTERFACE_MODE_2500BASEX: +@@ -729,6 +737,10 @@ static void ipq_pcs_get_state(struct phy + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: ++ /* SGMII and 1000BASEX in-band autoneg word format are decoded ++ * by PCS hardware and both placed to the same status register. ++ */ + ipq_pcs_get_state_sgmii(qpcs, index, state); + break; + case PHY_INTERFACE_MODE_2500BASEX: +@@ -766,6 +778,7 @@ static int ipq_pcs_config(struct phylink + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: + return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface); + case PHY_INTERFACE_MODE_2500BASEX: + return ipq_unipcs_config_2500basex(qpcs, interface); +@@ -798,6 +811,7 @@ static void ipq_pcs_link_up(struct phyli + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_QSGMII: + case PHY_INTERFACE_MODE_PSGMII: ++ case PHY_INTERFACE_MODE_1000BASEX: + ret = ipq_pcs_link_up_config_sgmii(qpcs, index, + neg_mode, speed); + break; diff --git a/target/linux/qualcommbe/patches-6.12/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch b/target/linux/qualcommbe/patches-6.12/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch new file mode 100644 index 00000000000..6e75cdfb0bd --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0364-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch @@ -0,0 +1,267 @@ +From 77462c0d74e51a24408062b93c3fcc0256909d33 Mon Sep 17 00:00:00 2001 +From: Lei Wei +Date: Mon, 15 Apr 2024 11:06:02 +0800 +Subject: [PATCH] net: pcs: Add 10G_QXGMII interface mode support to IPQ UNIPHY + PCS driver + +10G_QXGMII is used when PCS connectes with QCA8084 four ports +2.5G PHYs. + +Change-Id: If3dc92a07ac3e51f7c9473fb05fa0668617916fb +Signed-off-by: Lei Wei +Signed-off-by: Alexandru Gagniuc +--- + drivers/net/pcs/pcs-qcom-ipq9574.c | 112 +++++++++++++++++++++++------ + 1 file changed, 91 insertions(+), 21 deletions(-) + +--- a/drivers/net/pcs/pcs-qcom-ipq9574.c ++++ b/drivers/net/pcs/pcs-qcom-ipq9574.c +@@ -53,6 +53,9 @@ + #define PCS_MII_STS_PAUSE_TX_EN BIT(1) + #define PCS_MII_STS_PAUSE_RX_EN BIT(0) + ++#define PCS_QP_USXG_OPTION 0x584 ++#define PCS_QP_USXG_GMII_SRC_XPCS BIT(0) ++ + #define PCS_PLL_RESET 0x780 + #define PCS_ANA_SW_RESET BIT(6) + +@@ -68,10 +71,22 @@ + #define XPCS_10GBASER_LINK_STS BIT(12) + + #define XPCS_DIG_CTRL 0x38000 ++#define XPCS_SOFT_RESET BIT(15) + #define XPCS_USXG_ADPT_RESET BIT(10) + #define XPCS_USXG_EN BIT(9) + ++#define XPCS_KR_CTRL 0x38007 ++#define XPCS_USXG_MODE_MASK GENMASK(12, 10) ++#define XPCS_10G_QXGMII_MODE FIELD_PREP(XPCS_USXG_MODE_MASK, 0x5) ++ ++#define XPCS_DIG_STS 0x3800a ++#define XPCS_DIG_STS_AM_COUNT GENMASK(14, 0) ++ ++#define XPCS_CHANNEL_DIG_CTRL(x) (0x1a8000 + 0x10000 * ((x) - 1)) ++#define XPCS_CHANNEL_USXG_ADPT_RESET BIT(5) ++ + #define XPCS_MII_CTRL 0x1f0000 ++#define XPCS_CHANNEL_MII_CTRL(x) (0x1a0000 + 0x10000 * ((x) - 1)) + #define XPCS_MII_AN_EN BIT(12) + #define XPCS_DUPLEX_FULL BIT(8) + #define XPCS_SPEED_MASK (BIT(13) | BIT(6) | BIT(5)) +@@ -83,9 +98,11 @@ + #define XPCS_SPEED_10 0 + + #define XPCS_MII_AN_CTRL 0x1f8001 ++#define XPCS_CHANNEL_MII_AN_CTRL(x) (0x1a8001 + 0x10000 * ((x) - 1)) + #define XPCS_MII_AN_8BIT BIT(8) + + #define XPCS_MII_AN_INTR_STS 0x1f8002 ++#define XPCS_CHANNEL_MII_AN_INTR_STS(x) (0x1a8002 + 0x10000 * ((x) - 1)) + #define XPCS_USXG_AN_LINK_STS BIT(14) + #define XPCS_USXG_AN_SPEED_MASK GENMASK(12, 10) + #define XPCS_USXG_AN_SPEED_10 0 +@@ -95,6 +112,10 @@ + #define XPCS_USXG_AN_SPEED_5000 5 + #define XPCS_USXG_AN_SPEED_10000 3 + ++#define XPCS_XAUI_MODE_CTRL 0x1f8004 ++#define XPCS_CHANNEL_XAUI_MODE_CTRL(x) (0x1a8004 + 0x10000 * ((x) - 1)) ++#define XPCS_TX_IPG_CHECK_DIS BIT(0) ++ + /* Per PCS MII private data */ + struct ipq_pcs_mii { + struct ipq_pcs *qpcs; +@@ -217,12 +238,16 @@ static void ipq_unipcs_get_state_2500bas + } + + static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs, ++ int index, + struct phylink_link_state *state) + { + unsigned int val; +- int ret; ++ int ret, reg; ++ ++ reg = (index == 0) ? XPCS_MII_AN_INTR_STS : ++ XPCS_CHANNEL_MII_AN_INTR_STS(index); + +- ret = regmap_read(qpcs->regmap, XPCS_MII_AN_INTR_STS, &val); ++ ret = regmap_read(qpcs->regmap, reg, &val); + if (ret) { + state->link = 0; + return; +@@ -316,6 +341,14 @@ static int ipq_pcs_config_mode(struct ip + val = PCS_MODE_XPCS; + rate = 312500000; + break; ++ case PHY_INTERFACE_MODE_10G_QXGMII: ++ val = PCS_MODE_XPCS; ++ rate = 312500000; ++ ret = regmap_set_bits(qpcs->regmap, PCS_QP_USXG_OPTION, ++ PCS_QP_USXG_GMII_SRC_XPCS); ++ if (ret) ++ return ret; ++ break; + default: + dev_err(qpcs->dev, + "interface %s not supported\n", phy_modes(interface)); +@@ -407,30 +440,55 @@ static int ipq_unipcs_config_2500basex(s + return 0; + } + +-static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs) ++static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs, ++ int index, ++ phy_interface_t interface) + { +- int ret; ++ int ret, reg; + + /* Configure the XPCS for USXGMII mode if required */ +- if (qpcs->interface == PHY_INTERFACE_MODE_USXGMII) +- return 0; +- +- ret = ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_USXGMII); +- if (ret) +- return ret; ++ if (qpcs->interface != interface) { ++ ret = ipq_pcs_config_mode(qpcs, interface); ++ if (ret) ++ return ret; ++ } + +- /* Deassert XPCS and configure XPCS USXGMII */ ++ /* Deassert XPCS and configure XPCS USXGMII or 10G_QXGMII */ + reset_control_deassert(qpcs->reset[XPCS_RESET]); + + ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN); + if (ret) + return ret; + +- ret = regmap_set_bits(qpcs->regmap, XPCS_MII_AN_CTRL, XPCS_MII_AN_8BIT); ++ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) { ++ regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL, ++ XPCS_USXG_MODE_MASK, XPCS_10G_QXGMII_MODE); ++ ++ /* Set Alignment Marker Interval */ ++ regmap_update_bits(qpcs->regmap, XPCS_DIG_STS, ++ XPCS_DIG_STS_AM_COUNT, 0x6018); ++ ++ regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET); ++ } ++ ++ qpcs->interface = interface; ++ ++ /* Disable Tx IPG check for 10G_QXGMII */ ++ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) { ++ reg = (index == 0) ? XPCS_XAUI_MODE_CTRL : ++ XPCS_CHANNEL_XAUI_MODE_CTRL(index); ++ ++ regmap_set_bits(qpcs->regmap, reg, XPCS_TX_IPG_CHECK_DIS); ++ } ++ ++ /* Enable autoneg */ ++ reg = (index == 0) ? XPCS_MII_AN_CTRL : XPCS_CHANNEL_MII_AN_CTRL(index); ++ ret = regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_8BIT); + if (ret) + return ret; + +- return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN); ++ reg = (index == 0) ? XPCS_MII_CTRL : XPCS_CHANNEL_MII_CTRL(index); ++ return regmap_set_bits(qpcs->regmap, reg, XPCS_MII_AN_EN); + } + + static int ipq_unipcs_config_10gbaser(struct ipq_pcs *qpcs, +@@ -538,6 +596,7 @@ ipq_unipcs_link_up_clock_rate_set(struct + break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GBASER: ++ case PHY_INTERFACE_MODE_10G_QXGMII: + rate = ipq_unipcs_clock_rate_get_xgmii(speed); + break; + default: +@@ -603,7 +662,6 @@ static int ipq_unipcs_link_up_config_250 + int index, + int speed) + { +- unsigned int val; + int ret; + + /* 2500BASEX do not support autoneg and do not need to +@@ -618,10 +676,12 @@ static int ipq_unipcs_link_up_config_250 + PCS_MII_CTRL(index), PCS_MII_ADPT_RESET); + } + +-static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed) ++static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, ++ int channel, ++ int speed) + { + unsigned int val; +- int ret; ++ int ret, reg; + + switch (speed) { + case SPEED_10000: +@@ -648,14 +708,19 @@ static int ipq_pcs_link_up_config_usxgmi + } + + /* Configure XPCS speed */ +- ret = regmap_update_bits(qpcs->regmap, XPCS_MII_CTRL, ++ reg = (channel == 0) ? XPCS_MII_CTRL : XPCS_CHANNEL_MII_CTRL(channel); ++ ret = regmap_update_bits(qpcs->regmap, reg, + XPCS_SPEED_MASK, val | XPCS_DUPLEX_FULL); + if (ret) + return ret; + + /* XPCS adapter reset */ +- return regmap_set_bits(qpcs->regmap, ++ if (channel == 0) ++ return regmap_set_bits(qpcs->regmap, + XPCS_DIG_CTRL, XPCS_USXG_ADPT_RESET); ++ else ++ return regmap_set_bits(qpcs->regmap, XPCS_CHANNEL_DIG_CTRL(channel), ++ XPCS_CHANNEL_USXG_ADPT_RESET); + } + + static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported, +@@ -669,6 +734,7 @@ static int ipq_pcs_validate(struct phyli + /* In-band autoneg is not supported for 2500BASEX */ + phylink_clear(supported, Autoneg); + return 0; ++ case PHY_INTERFACE_MODE_10G_QXGMII: + case PHY_INTERFACE_MODE_USXGMII: + /* USXGMII only supports full duplex mode */ + phylink_clear(supported, 100baseT_Half); +@@ -747,7 +813,8 @@ static void ipq_pcs_get_state(struct phy + ipq_unipcs_get_state_2500basex(qpcs, index, state); + break; + case PHY_INTERFACE_MODE_USXGMII: +- ipq_pcs_get_state_usxgmii(qpcs, state); ++ case PHY_INTERFACE_MODE_10G_QXGMII: ++ ipq_pcs_get_state_usxgmii(qpcs, index, state); + break; + case PHY_INTERFACE_MODE_10GBASER: + ipq_unipcs_get_state_10gbaser(qpcs, state); +@@ -783,7 +850,9 @@ static int ipq_pcs_config(struct phylink + case PHY_INTERFACE_MODE_2500BASEX: + return ipq_unipcs_config_2500basex(qpcs, interface); + case PHY_INTERFACE_MODE_USXGMII: +- return ipq_pcs_config_usxgmii(qpcs); ++ case PHY_INTERFACE_MODE_10G_QXGMII: ++ return ipq_pcs_config_usxgmii(qpcs, index, ++ interface); + case PHY_INTERFACE_MODE_10GBASER: + return ipq_unipcs_config_10gbaser(qpcs, interface); + default: +@@ -819,7 +888,8 @@ static void ipq_pcs_link_up(struct phyli + ret = ipq_unipcs_link_up_config_2500basex(qpcs, index, speed); + break; + case PHY_INTERFACE_MODE_USXGMII: +- ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed); ++ case PHY_INTERFACE_MODE_10G_QXGMII: ++ ret = ipq_pcs_link_up_config_usxgmii(qpcs, index, speed); + break; + case PHY_INTERFACE_MODE_10GBASER: + break;