1
1
openwrt/target/linux/qualcommbe/patches-6.18/0323-net-pcs-qcom-ipq9574-Update-IPQ9574-PCS-driver.patch
Alexandru Gagniuc 7ea10c7015 qualcommbe: kernel-6.18: renumber patches
I generate patches form git, so maintaining an old numbering scheme
does not integrate well with my workflow. renumber the pacthes here so
that the commit shows only the changes to the patches.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/21506
Signed-off-by: Robert Marko <robimarko@gmail.com>
2026-05-28 10:15:20 +02:00

283 lines
8.6 KiB
Diff

From b4e07a8a3ec3dc5f676238987556e2aff0b14028 Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Mon, 29 Jan 2024 11:39:36 +0800
Subject: [PATCH] net: pcs: qcom-ipq9574: Update IPQ9574 PCS driver
Keep the PCS driver synced with the latest version posted to the kernel
community and add the XPCS reset support.
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
.../bindings/net/pcs/qcom,ipq9574-pcs.yaml | 7 ++
drivers/net/pcs/pcs-qcom-ipq9574.c | 68 +++++++++++++++----
2 files changed, 63 insertions(+), 12 deletions(-)
--- a/Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
+++ b/Documentation/devicetree/bindings/net/pcs/qcom,ipq9574-pcs.yaml
@@ -98,6 +98,10 @@ properties:
- const: sys
- const: ahb
+ resets:
+ maxItems: 1
+ description: XPCS reset
+
'#clock-cells':
const: 1
description: See include/dt-bindings/net/qcom,ipq9574-pcs.h for constants
@@ -137,6 +141,7 @@ required:
- '#size-cells'
- clocks
- clock-names
+ - resets
- '#clock-cells'
additionalProperties: false
@@ -144,6 +149,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,ipq9574-gcc.h>
+ #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
ethernet-pcs@7a00000 {
compatible = "qcom,ipq9574-pcs";
@@ -154,6 +160,7 @@ examples:
<&gcc GCC_UNIPHY0_AHB_CLK>;
clock-names = "sys",
"ahb";
+ resets = <&gcc GCC_UNIPHY0_XPCS_RESET>;
#clock-cells = <1>;
pcs-mii@0 {
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -13,6 +13,7 @@
#include <linux/phylink.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <dt-bindings/net/qcom,ipq9574-pcs.h>
@@ -31,9 +32,12 @@
#define PCS_MODE_SEL_MASK GENMASK(12, 8)
#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_2500BASEX FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
#define PCS_MODE_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
#define PCS_MODE_SGMII_MODE_MASK GENMASK(6, 4)
+#define PCS_MODE_SGMII_MODE_MAC FIELD_PREP(PCS_MODE_SGMII_MODE_MASK, \
+ 0x2)
#define PCS_MODE_SGMII_MODE_1000BASEX FIELD_PREP(PCS_MODE_SGMII_MODE_MASK, \
0x0)
@@ -52,6 +56,8 @@
#define PCS_MII_STS_SPEED_10 0
#define PCS_MII_STS_SPEED_100 1
#define PCS_MII_STS_SPEED_1000 2
+#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)
@@ -142,6 +148,7 @@ struct ipq_pcs {
struct clk_hw tx_hw;
struct ipq_pcs_mii *qpcs_mii[PCS_MAX_MII_NRS];
+ struct reset_control *xpcs_rstc;
};
#define phylink_pcs_to_qpcs_mii(_pcs) \
@@ -184,6 +191,11 @@ static void ipq_pcs_get_state_sgmii(stru
state->duplex = DUPLEX_FULL;
else
state->duplex = DUPLEX_HALF;
+
+ if (val & PCS_MII_STS_PAUSE_TX_EN)
+ state->pause |= MLO_PAUSE_TX;
+ if (val & PCS_MII_STS_PAUSE_RX_EN)
+ state->pause |= MLO_PAUSE_RX;
}
static void ipq_pcs_get_state_2500basex(struct ipq_pcs *qpcs,
@@ -198,7 +210,6 @@ static void ipq_pcs_get_state_2500basex(
return;
}
-
state->link = !!(val & PCS_MII_LINK_STS);
if (!state->link)
@@ -281,17 +292,27 @@ static int ipq_pcs_config_mode(struct ip
{
unsigned long rate = 125000000;
unsigned int val, mask, misc2 = 0;
+ bool xpcs_mode = false;
int ret;
+ /* Assert XPCS reset */
+ reset_control_assert(qpcs->xpcs_rstc);
+
/* Configure PCS interface mode */
mask = PCS_MODE_SEL_MASK;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
- val = PCS_MODE_SGMII;
+ mask |= PCS_MODE_SGMII_MODE_MASK;
+ val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_MAC;
misc2 = PCS_MISC2_MODE_SGMII;
break;
case PHY_INTERFACE_MODE_QSGMII:
- val = PCS_MODE_QSGMII;
+ mask |= PCS_MODE_SGMII_MODE_MASK;
+ val = PCS_MODE_QSGMII | PCS_MODE_SGMII_MODE_MAC;
+ break;
+ case PHY_INTERFACE_MODE_PSGMII:
+ mask |= PCS_MODE_SGMII_MODE_MASK;
+ val = PCS_MODE_PSGMII | PCS_MODE_SGMII_MODE_MAC;
break;
case PHY_INTERFACE_MODE_1000BASEX:
mask |= PCS_MODE_SGMII_MODE_MASK;
@@ -308,6 +329,7 @@ static int ipq_pcs_config_mode(struct ip
case PHY_INTERFACE_MODE_10GBASER:
val = PCS_MODE_XPCS;
rate = 312500000;
+ xpcs_mode = true;
break;
default:
return -EOPNOTSUPP;
@@ -367,6 +389,10 @@ static int ipq_pcs_config_mode(struct ip
return ret;
}
+ /* Deassert XPCS */
+ if (xpcs_mode)
+ reset_control_deassert(qpcs->xpcs_rstc);
+
return 0;
}
@@ -384,15 +410,13 @@ static int ipq_pcs_config_sgmii(struct i
return ret;
}
- /* Nothing to do here as in-band autoneg mode is enabled
- * by default for each PCS MII port.
- */
+ /* Set AN mode or force mode */
if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
- return 0;
-
- /* Set force speed mode */
- return regmap_set_bits(qpcs->regmap,
- PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
+ return regmap_clear_bits(qpcs->regmap,
+ PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
+ else
+ return regmap_set_bits(qpcs->regmap,
+ PCS_MII_CTRL(index), PCS_MII_FORCE_MODE);
}
static int ipq_pcs_config_2500basex(struct ipq_pcs *qpcs)
@@ -417,6 +441,10 @@ static int ipq_pcs_config_usxgmii(struct
if (ret)
return ret;
+ ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
+ if (ret)
+ return ret;
+
if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
ret = regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL,
XPCS_USXG_MODE_MASK, XPCS_10G_QXGMII_MODE);
@@ -432,6 +460,7 @@ static int ipq_pcs_config_usxgmii(struct
ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET);
if (ret)
return ret;
+ }
}
/* Disable Tx IPG check for 10G_QXGMII */
@@ -559,7 +588,6 @@ static int ipq_pcs_link_up_config_usxgmi
reg = (index == 0) ? XPCS_DIG_CTRL : XPCS_MII1_DIG_CTRL(index);
val = (index == 0) ? XPCS_USXG_ADPT_RESET : XPCS_MII1_USXG_ADPT_RESET;
return regmap_set_bits(qpcs->regmap, reg, val);
-
}
static int ipq_pcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
@@ -568,6 +596,7 @@ static int ipq_pcs_validate(struct phyli
switch (state->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_PSGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_10GBASER:
return 0;
@@ -592,6 +621,7 @@ static unsigned int ipq_pcs_inband_caps(
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_PSGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10G_QXGMII:
@@ -648,6 +678,7 @@ static void ipq_pcs_get_state(struct phy
switch (state->interface) {
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.
@@ -689,6 +720,7 @@ static int ipq_pcs_config(struct phylink
switch (interface) {
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:
@@ -703,6 +735,11 @@ static int ipq_pcs_config(struct phylink
};
}
+static void ipq_pcs_an_restart(struct phylink_pcs *pcs)
+{
+ /* Currently not used */
+}
+
static void ipq_pcs_link_up(struct phylink_pcs *pcs,
unsigned int neg_mode,
phy_interface_t interface,
@@ -716,6 +753,7 @@ static void ipq_pcs_link_up(struct phyli
switch (interface) {
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);
@@ -746,6 +784,7 @@ static const struct phylink_pcs_ops ipq_
.pcs_disable = ipq_pcs_disable,
.pcs_get_state = ipq_pcs_get_state,
.pcs_config = ipq_pcs_config,
+ .pcs_an_restart = ipq_pcs_an_restart,
.pcs_link_up = ipq_pcs_link_up,
};
@@ -990,6 +1029,11 @@ static int ipq9574_pcs_probe(struct plat
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to enable AHB clock\n");
+ qpcs->xpcs_rstc = devm_reset_control_get_optional(dev, NULL);
+ if (IS_ERR_OR_NULL(qpcs->xpcs_rstc))
+ return dev_err_probe(dev, PTR_ERR(qpcs->xpcs_rstc),
+ "Failed to get XPCS reset\n");
+
ret = ipq_pcs_clk_register(qpcs);
if (ret)
return ret;