[openwrt/openwrt] qualcommbe: v6.12: enable 2.5G and 10G phylink modes in pcs-qcom-ipq9574

LEDE Commits lede-commits at lists.infradead.org
Sat May 31 03:26:06 PDT 2025


robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/22dc34e8c191400d25b43c67f18c9c5c3e6d149e

commit 22dc34e8c191400d25b43c67f18c9c5c3e6d149e
Author: Alexandru Gagniuc <mr.nuke.me at gmail.com>
AuthorDate: Sun May 25 17:42:20 2025 -0500

    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 <mr.nuke.me at gmail.com>
    Link: https://github.com/openwrt/openwrt/pull/18796
    Signed-off-by: Robert Marko <robimarko at gmail.com>
---
 ...-10GBASER-interface-mode-support-to-IPQ-U.patch | 127 ++++++++++
 ...-2500BASEX-interface-mode-support-to-IPQ-.patch | 192 +++++++++++++++
 ...-1000BASEX-interface-mode-support-to-IPQ-.patch |  91 +++++++
 ...-10G_QXGMII-interface-mode-support-to-IPQ.patch | 267 +++++++++++++++++++++
 4 files changed, 677 insertions(+)

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 0000000000..529f9f7005
--- /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 <quic_leiwei at quicinc.com>
+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 <quic_leiwei at quicinc.com>
+Alex G: Use regmap to read/write registers
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+---
+ 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 0000000000..3bf5f1f798
--- /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 <quic_leiwei at quicinc.com>
+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 <quic_leiwei at quicinc.com>
+Alex G: use regmap to read/write registers
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+---
+ 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 0000000000..9153cabe24
--- /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 <quic_leiwei at quicinc.com>
+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 <quic_leiwei at quicinc.com>
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+---
+ 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 0000000000..6e75cdfb0b
--- /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 <quic_leiwei at quicinc.com>
+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 <quic_leiwei at quicinc.com>
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me at gmail.com>
+---
+ 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;




More information about the lede-commits mailing list