qualcommbe: update ipq9574 PCS driver

Update the ipq9574 PCS driver the version provided by Qualcomm via
github. The updated driver simplifies link up handling by removing
unnecessary clock rate changes.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/20993
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Alexandru Gagniuc 2025-11-06 21:52:11 -06:00 committed by Robert Marko
parent cc6b9ff17b
commit 95bd7a76a1
13 changed files with 562 additions and 840 deletions

View File

@ -1,4 +1,4 @@
From 240ae5e0ca2ed858e25d7da6d5291d9c1f2c660a Mon Sep 17 00:00:00 2001
From 10b609ddbf4d369c80098efa39451ef3973759b5 Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Fri, 7 Feb 2025 23:53:14 +0800
Subject: [PATCH] net: pcs: qcom-ipq9574: Add PCS instantiation and phylink
@ -14,10 +14,12 @@ MAC.
c.) PCS phylink operations for SGMII/QSGMII interface modes.
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Alex G: remove phylink_pcs .neg_mode boolean
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 469 +++++++++++++++++++++++++++
drivers/net/pcs/pcs-qcom-ipq9574.c | 468 +++++++++++++++++++++++++++
include/linux/pcs/pcs-qcom-ipq9574.h | 15 +
2 files changed, 484 insertions(+)
2 files changed, 483 insertions(+)
create mode 100644 include/linux/pcs/pcs-qcom-ipq9574.h
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
@ -88,7 +90,7 @@ Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
/* PCS private data */
struct ipq_pcs {
struct device *dev;
@@ -31,8 +77,359 @@ struct ipq_pcs {
@@ -31,8 +77,358 @@ struct ipq_pcs {
struct clk_hw rx_hw;
/* TX clock supplied to NSSCC */
struct clk_hw tx_hw;
@ -310,7 +312,7 @@ Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+ clk_disable_unprepare(qpcs_mii->tx_clk);
+}
+
+static void ipq_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
+static void ipq_pcs_get_state(struct phylink_pcs *pcs,
+ struct phylink_link_state *state)
+{
+ struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);
@ -422,7 +424,6 @@ Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+ qpcs_mii->qpcs = qpcs;
+ qpcs_mii->index = index;
+ qpcs_mii->pcs.ops = &ipq_pcs_phylink_ops;
+ qpcs_mii->pcs.neg_mode = true;
+ qpcs_mii->pcs.poll = true;
+
+ qpcs_mii->rx_clk = devm_get_clk_from_child(dev, mii_np, "rx");
@ -448,7 +449,7 @@ Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
{
switch (qpcs->interface) {
@@ -219,6 +616,10 @@ static int ipq9574_pcs_probe(struct plat
@@ -219,6 +615,10 @@ static int ipq9574_pcs_probe(struct plat
if (ret)
return ret;
@ -459,7 +460,7 @@ Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
platform_set_drvdata(pdev, qpcs);
return 0;
@@ -230,6 +631,74 @@ static const struct of_device_id ipq9574
@@ -230,6 +630,74 @@ static const struct of_device_id ipq9574
};
MODULE_DEVICE_TABLE(of, ipq9574_pcs_of_mtable);

View File

@ -1,29 +0,0 @@
From ffe2a80fb76ccdc1781f817f6bbc9a8aa919816e Mon Sep 17 00:00:00 2001
From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Date: Mon, 12 May 2025 09:11:05 -0500
Subject: [PATCH] net: pcs: qcom-ipq9574: remove "neg_mode" argument from
ipq_pcs_get_state
Since commit c6739623c91bb ("net: phylink: pass neg_mode into
.pcs_get_state() method"), the "neg_mode" parameter is part of the
argument list of .pcs_get_state(). This is available starting with
v6.14. However, we want to use the backported IPQ9574 driver on v6.12.
Remove this parameter from ipq_pcs_get_state(), as it is not part of
.pcs_get_state() in v6.12.
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -457,7 +457,7 @@ static void ipq_pcs_disable(struct phyli
clk_disable_unprepare(qpcs_mii->tx_clk);
}
-static void ipq_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
+static void ipq_pcs_get_state(struct phylink_pcs *pcs,
struct phylink_link_state *state)
{
struct ipq_pcs_mii *qpcs_mii = phylink_pcs_to_qpcs_mii(pcs);

View File

@ -1,82 +0,0 @@
From 5b2f02ccca7b9496f0a8da6ade063b82810c75e7 Mon Sep 17 00:00:00 2001
From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Date: Mon, 12 May 2025 09:27:17 -0500
Subject: [PATCH] net: pcs: qcom-ipq9574: delay mii clock probing until
ipq_pcs_get()
NSSCC generates the SYS and AHB clocks for the PCS block The PCS then
feeds the uniphy clocks back to the NSSCC, which are in turn, used to
feed the PCS MII clocks. This works fine in hardware:
GCC -> NSSCC -> PCS -> NSSCC -> PCS(MII)
However, when the PCS MII clocks are probed within the .probe() of
the PCS block, it creates a circular dependency. The MII clocks depend
on the uniphy clocks, which depend on the PCS block being probed.
Since we are in the process of probing the PCS block, this results in
both blocks returning with -EPROBE_DEFER:
platform 39b00000.clock-controller: deferred probe pending: platform: supplier 7a00000.ethernet-pcs not ready
mdio_bus 90000.mdio-1:18: deferred probe pending: mdio_bus: supplier 7a20000.ethernet-pcs not ready
mdio_bus 90000.mdio-1:00: deferred probe pending: mdio_bus: supplier 90000.mdio-1:18 not ready
mdio_bus 90000.mdio-1:01: deferred probe pending: mdio_bus: supplier 90000.mdio-1:18 not ready
mdio_bus 90000.mdio-1:02: deferred probe pending: mdio_bus: supplier 90000.mdio-1:18 not ready
mdio_bus 90000.mdio-1:03: deferred probe pending: mdio_bus: supplier 90000.mdio-1:18 not ready
platform 7a00000.ethernet-pcs: deferred probe pending: ipq9574_pcs: Failed to get MII 0 RX clock
platform 7a20000.ethernet-pcs: deferred probe pending: ipq9574_pcs: Failed to get MII 0 RX clock
platform 3a000000.qcom-ppe: deferred probe pending: platform: supplier 39b00000.clock-controller not ready
To break this dependency, let the PCS block probe, and only probe the
PCS MII clocks from ipq_pcs_get().
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 30 ++++++++++++++++--------------
1 file changed, 16 insertions(+), 14 deletions(-)
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -580,20 +580,6 @@ static int ipq_pcs_create_miis(struct ip
qpcs_mii->pcs.neg_mode = true;
qpcs_mii->pcs.poll = true;
- qpcs_mii->rx_clk = devm_get_clk_from_child(dev, mii_np, "rx");
- if (IS_ERR(qpcs_mii->rx_clk)) {
- of_node_put(mii_np);
- return dev_err_probe(dev, PTR_ERR(qpcs_mii->rx_clk),
- "Failed to get MII %d RX clock\n", index);
- }
-
- qpcs_mii->tx_clk = devm_get_clk_from_child(dev, mii_np, "tx");
- if (IS_ERR(qpcs_mii->tx_clk)) {
- of_node_put(mii_np);
- return dev_err_probe(dev, PTR_ERR(qpcs_mii->tx_clk),
- "Failed to get MII %d TX clock\n", index);
- }
-
qpcs->qpcs_mii[index] = qpcs_mii;
}
@@ -848,6 +834,22 @@ struct phylink_pcs *ipq_pcs_get(struct d
return ERR_PTR(-ENOENT);
}
+ qpcs_mii->rx_clk = devm_get_clk_from_child(&pdev->dev, np, "rx");
+ if (IS_ERR(qpcs_mii->rx_clk)) {
+ put_device(&pdev->dev);
+ return dev_err_ptr_probe(&pdev->dev, PTR_ERR(qpcs_mii->rx_clk),
+ "Failed to get MII %d RX clock\n",
+ index);
+ }
+
+ qpcs_mii->tx_clk = devm_get_clk_from_child(&pdev->dev, np, "tx");
+ if (IS_ERR(qpcs_mii->tx_clk)) {
+ put_device(&pdev->dev);
+ return dev_err_ptr_probe(&pdev->dev, PTR_ERR(qpcs_mii->tx_clk),
+ "Failed to get MII %d TX clock\n",
+ index);
+ }
+
return &qpcs_mii->pcs;
}
EXPORT_SYMBOL(ipq_pcs_get);

View File

@ -1,362 +0,0 @@
From 7de372abe7a4b5b380fdbeedd268445f234990c8 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: add changes not submitted upstream
Was ("net: pcs: Add driver for Qualcomm IPQ UNIPHY PCS").
The UNIPHY hardware block in Qualcomm's IPQ SoC based boards enables
PCS and XPCS functions, and helps in interfacing the Ethernet MAC in
IPQ SoC to external PHYs.
This patch adds the PCS driver support for the UNIPHY hardware used in
IPQ SoC based boards. Support for SGMII/QSGMII/PSGMII and USXGMII
interface modes are added in the driver.
Change-Id: Id2c8f993f121098f7b02186b53770b75bb539a93
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Alex G: Rebase original patch on top of 20250207 uniphy submission
Remove mutex that is not required according to
https://lore.kernel.org/lkml/Z3ZwURgIErzpzpEr@shell.armlinux.org.uk/
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 180 +++++++++++++++++++++++-
include/linux/pcs/pcs-qcom-ipq-uniphy.h | 13 ++
2 files changed, 192 insertions(+), 1 deletion(-)
create mode 100644 include/linux/pcs/pcs-qcom-ipq-uniphy.h
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -9,10 +9,12 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pcs/pcs-qcom-ipq9574.h>
+#include <linux/pcs/pcs-qcom-ipq-uniphy.h>
#include <linux/phy.h>
#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>
@@ -26,6 +28,7 @@
#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_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10)
#define PCS_MII_CTRL(x) (0x480 + 0x18 * (x))
@@ -43,6 +46,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_PLL_RESET 0x780
#define PCS_ANA_SW_RESET BIT(6)
@@ -95,12 +100,35 @@ struct ipq_pcs_mii {
struct clk *tx_clk;
};
+/* UNIPHY PCS reset ID */
+enum {
+ PCS_SYS_RESET,
+ PCS_AHB_RESET,
+ XPCS_RESET,
+ PCS_RESET_MAX
+};
+
+/* UNIPHY PCS reset name */
+static const char *const pcs_reset_name[PCS_RESET_MAX] = {
+ "sys",
+ "ahb",
+ "xpcs",
+};
+
+/* UNIPHY PCS channel clock ID */
+enum {
+ PCS_CH_RX_CLK,
+ PCS_CH_TX_CLK,
+ PCS_CH_CLK_MAX
+};
+
/* PCS private data */
struct ipq_pcs {
struct device *dev;
void __iomem *base;
struct regmap *regmap;
phy_interface_t interface;
+ struct reset_control *reset[PCS_RESET_MAX];
/* RX clock supplied to NSSCC */
struct clk_hw rx_hw;
@@ -150,6 +178,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_usxgmii(struct ipq_pcs *qpcs,
@@ -203,6 +236,9 @@ static int ipq_pcs_config_mode(struct ip
unsigned int val;
int ret;
+ /* Assert XPCS reset */
+ reset_control_assert(qpcs->reset[XPCS_RESET]);
+
/* Configure PCS interface mode */
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
@@ -211,11 +247,16 @@ static int ipq_pcs_config_mode(struct ip
case PHY_INTERFACE_MODE_QSGMII:
val = PCS_MODE_QSGMII;
break;
+ case PHY_INTERFACE_MODE_PSGMII:
+ val = PCS_MODE_PSGMII;
+ break;
case PHY_INTERFACE_MODE_USXGMII:
val = PCS_MODE_XPCS;
rate = 312500000;
break;
default:
+ dev_err(qpcs->dev,
+ "interface %s not supported\n", phy_modes(interface));
return -EOPNOTSUPP;
}
@@ -300,6 +341,9 @@ static int ipq_pcs_config_usxgmii(struct
if (ret)
return ret;
+ /* Deassert XPCS and configure XPCS USXGMII */
+ reset_control_deassert(qpcs->reset[XPCS_RESET]);
+
ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
if (ret)
return ret;
@@ -311,6 +355,91 @@ static int ipq_pcs_config_usxgmii(struct
return regmap_set_bits(qpcs->regmap, XPCS_MII_CTRL, XPCS_MII_AN_EN);
}
+static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed)
+{
+ unsigned long rate = 0;
+
+ switch (speed) {
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ break;
+ case SPEED_10:
+ rate = 2500000;
+ break;
+ default:
+ break;
+ }
+
+ return rate;
+}
+
+static unsigned long ipq_unipcs_clock_rate_get_xgmii(int speed)
+{
+ unsigned long rate = 0;
+
+ switch (speed) {
+ case SPEED_10000:
+ rate = 312500000;
+ break;
+ case SPEED_5000:
+ rate = 156250000;
+ break;
+ case SPEED_2500:
+ rate = 78125000;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ break;
+ case SPEED_100:
+ rate = 12500000;
+ break;
+ case SPEED_10:
+ rate = 1250000;
+ break;
+ default:
+ break;
+ }
+
+ return rate;
+}
+
+static void
+ipq_unipcs_link_up_clock_rate_set(struct ipq_pcs_mii *qunipcs_ch,
+ phy_interface_t interface,
+ int speed)
+{
+ struct ipq_pcs *qpcs = qunipcs_ch->qpcs;
+ unsigned long rate = 0;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_PSGMII:
+ rate = ipq_unipcs_clock_rate_get_gmii(speed);
+ break;
+ case PHY_INTERFACE_MODE_USXGMII:
+ rate = ipq_unipcs_clock_rate_get_xgmii(speed);
+ break;
+ default:
+ dev_err(qpcs->dev,
+ "interface %s not supported\n", phy_modes(interface));
+ return;
+ }
+
+ if (rate == 0) {
+ dev_err(qpcs->dev, "Invalid PCS clock rate\n");
+ return;
+ }
+
+ clk_set_rate(qunipcs_ch->rx_clk, rate);
+ clk_set_rate(qunipcs_ch->tx_clk, rate);
+
+ fsleep(10000);
+}
+
static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
int index,
unsigned int neg_mode,
@@ -467,6 +596,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:
ipq_pcs_get_state_sgmii(qpcs, index, state);
break;
case PHY_INTERFACE_MODE_USXGMII:
@@ -497,10 +627,13 @@ static int ipq_pcs_config(struct phylink
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_PSGMII:
return ipq_pcs_config_sgmii(qpcs, index, neg_mode, interface);
case PHY_INTERFACE_MODE_USXGMII:
return ipq_pcs_config_usxgmii(qpcs);
default:
+ dev_err(qpcs->dev,
+ "interface %s not supported\n", phy_modes(interface));
return -EOPNOTSUPP;
};
}
@@ -515,9 +648,14 @@ static void ipq_pcs_link_up(struct phyli
int index = qpcs_mii->index;
int ret;
+ /* Configure PCS channel interface clock rate */
+ ipq_unipcs_link_up_clock_rate_set(qpcs_mii, interface, speed);
+
+ /* Configure PCS speed and reset PCS adapter */
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_PSGMII:
ret = ipq_pcs_link_up_config_sgmii(qpcs, index,
neg_mode, speed);
break;
@@ -525,6 +663,8 @@ static void ipq_pcs_link_up(struct phyli
ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
break;
default:
+ dev_err(qpcs->dev,
+ "interface %s not supported\n", phy_modes(interface));
return;
}
@@ -735,12 +875,38 @@ static const struct regmap_config ipq_pc
.fast_io = true,
};
+/**
+ * ipq_unipcs_create() - Create Qualcomm IPQ UNIPHY PCS
+ * @np: Device tree node to the PCS
+ *
+ * Description: Create a phylink PCS instance for a PCS node @np.
+ *
+ * Return: A pointer to the phylink PCS instance or an error-pointer value.
+ */
+struct phylink_pcs *ipq_unipcs_create(struct device_node *np)
+{
+ return ipq_pcs_get(np);
+}
+EXPORT_SYMBOL(ipq_unipcs_create);
+
+/**
+ * ipq_unipcs_destroy() - Destroy Qualcomm IPQ UNIPHY PCS
+ * @pcs: PCS instance
+ *
+ * Description: Destroy a phylink PCS instance.
+ */
+void ipq_unipcs_destroy(struct phylink_pcs *pcs)
+{
+ ipq_pcs_put(pcs);
+}
+EXPORT_SYMBOL(ipq_unipcs_destroy);
+
static int ipq9574_pcs_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ipq_pcs *qpcs;
struct clk *clk;
- int ret;
+ int i, ret;
qpcs = devm_kzalloc(dev, sizeof(*qpcs), GFP_KERNEL);
if (!qpcs)
@@ -762,11 +928,23 @@ static int ipq9574_pcs_probe(struct plat
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to enable SYS clock\n");
+ clk_set_rate(clk, 24000000);
clk = devm_clk_get_enabled(dev, "ahb");
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to enable AHB clock\n");
+ clk_set_rate(clk, 100000000);
+
+ for (i = 0; i < PCS_RESET_MAX; i++) {
+ qpcs->reset[i] =
+ devm_reset_control_get_optional_exclusive(dev,
+ pcs_reset_name[i]);
+
+ if (IS_ERR(qpcs->reset[i]))
+ dev_err(dev, "Failed to get the reset ID %s\n",
+ pcs_reset_name[i]);
+ }
ret = ipq_pcs_clk_register(qpcs);
if (ret)
--- /dev/null
+++ b/include/linux/pcs/pcs-qcom-ipq-uniphy.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ */
+
+#ifndef __LINUX_PCS_QCOM_IPQ_UNIPHY_H
+#define __LINUX_PCS_QCOM_IPQ_UNIPHY_H
+
+struct phylink_pcs *ipq_unipcs_create(struct device_node *np);
+void ipq_unipcs_destroy(struct phylink_pcs *pcs);
+
+#endif /* __LINUX_PCS_QCOM_IPQ_UNIPHY_H */

View File

@ -1,4 +1,4 @@
From 8c02b6438167e1b73b908040c4ec3d4877c16f83 Mon Sep 17 00:00:00 2001
From d6f184181b076cbb54f152994f5bc73ce524a67e Mon Sep 17 00:00:00 2001
From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Date: Sun, 11 May 2025 18:21:00 -0500
Subject: [PATCH] arm64: dts: qcom: ipq9574: add PCS uniphy nodes
@ -11,8 +11,8 @@ feed back to the NSSCC node.
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
arch/arm64/boot/dts/qcom/ipq9574.dtsi | 116 ++++++++++++++++++++++++--
1 file changed, 110 insertions(+), 6 deletions(-)
arch/arm64/boot/dts/qcom/ipq9574.dtsi | 100 ++++++++++++++++++++++++--
1 file changed, 94 insertions(+), 6 deletions(-)
--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
@ -43,7 +43,7 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
<&gcc GCC_NSSCC_CLK>;
clock-names = "xo",
"nss_1200",
@@ -1269,6 +1270,109 @@
@@ -1269,6 +1270,93 @@
#reset-cells = <1>;
#interconnect-cells = <1>;
};
@ -57,13 +57,7 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ <&gcc GCC_UNIPHY0_AHB_CLK>;
+ clock-names = "sys",
+ "ahb";
+ resets = <&gcc GCC_UNIPHY0_SYS_RESET>,
+ <&gcc GCC_UNIPHY0_AHB_RESET>,
+ <&gcc GCC_UNIPHY0_XPCS_RESET>;
+ reset-names = "sys",
+ "ahb",
+ "xpcs";
+
+ resets = <&gcc GCC_UNIPHY0_XPCS_RESET>;
+ #clock-cells = <1>;
+
+ pcs0_ch0: pcs-mii@0 {
@ -108,12 +102,7 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ <&gcc GCC_UNIPHY1_AHB_CLK>;
+ clock-names = "sys",
+ "ahb";
+ resets = <&gcc GCC_UNIPHY1_SYS_RESET>,
+ <&gcc GCC_UNIPHY1_AHB_RESET>,
+ <&gcc GCC_UNIPHY1_XPCS_RESET>;
+ reset-names = "sys",
+ "ahb",
+ "xpcs";
+ resets = <&gcc GCC_UNIPHY1_XPCS_RESET>;
+ #clock-cells = <1>;
+
+ pcs1_ch0: pcs-mii@0 {
@ -134,12 +123,7 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ <&gcc GCC_UNIPHY2_AHB_CLK>;
+ clock-names = "sys",
+ "ahb";
+ resets = <&gcc GCC_UNIPHY2_SYS_RESET>,
+ <&gcc GCC_UNIPHY2_AHB_RESET>,
+ <&gcc GCC_UNIPHY2_XPCS_RESET>;
+ reset-names = "sys",
+ "ahb",
+ "xpcs";
+ resets = <&gcc GCC_UNIPHY2_XPCS_RESET>;
+ #clock-cells = <1>;
+
+ pcs2_ch0: pcs-mii@0 {

View File

@ -1,4 +1,4 @@
From e770b36f0353fd11c4628360fe412acb7f02f346 Mon Sep 17 00:00:00 2001
From 432c2a2da1e0f4a8e2c0fea191361832a7f90f36 Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Wed, 6 Mar 2024 17:40:52 +0800
Subject: [PATCH] net: pcs: Add 10GBASER interface mode support to IPQ UNIPHY
@ -9,40 +9,41 @@ Subject: [PATCH] net: pcs: Add 10GBASER interface mode support to IPQ UNIPHY
Change-Id: Ifc3c3bb23811807a9b34e88771aab2c830c2327c
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Alex G: Use regmap to read/write registers
Remove xpcs_reset deassert logic (to be implemented later)
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 57 ++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
drivers/net/pcs/pcs-qcom-ipq9574.c | 47 ++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -60,6 +60,9 @@
@@ -55,6 +55,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_KR_STS 0x30020
+#define XPCS_KR_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
@@ -196,6 +199,28 @@ static void ipq_pcs_get_state_usxgmii(st
state->duplex = DUPLEX_FULL;
}
+static void ipq_unipcs_get_state_10gbaser(struct ipq_pcs *qpcs,
+static void ipq_pcs_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);
+ ret = regmap_read(qpcs->regmap, XPCS_KR_STS, &val);
+ if (ret) {
+ state->link = 0;
+ return;
+ }
+
+ state->link = !!(val & XPCS_10GBASER_LINK_STS);
+ state->link = !!(val & XPCS_KR_LINK_STS);
+
+ if (!state->link)
+ return;
@ -55,49 +56,31 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
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;
@@ -212,6 +237,7 @@ static int ipq_pcs_config_mode(struct ip
val = PCS_MODE_QSGMII;
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
@@ -311,6 +337,15 @@ 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)
+static int ipq_pcs_config_10gbaser(struct ipq_pcs *qpcs)
+{
+ 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;
+ }
+
+ /* Configure 10GBASER mode if required */
+ if (qpcs->interface == PHY_INTERFACE_MODE_10GBASER)
+ return 0;
+
+ return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_10GBASER);
+}
+
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:
@@ -528,6 +574,7 @@ static int ipq_pcs_validate(struct phyli
static int ipq_pcs_link_up_config_sgmii(struct ipq_pcs *qpcs,
int index,
unsigned int neg_mode,
@@ -399,6 +434,7 @@ static int ipq_pcs_validate(struct phyli
switch (state->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
@ -105,44 +88,45 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
return 0;
case PHY_INTERFACE_MODE_USXGMII:
/* USXGMII only supports full duplex mode */
@@ -546,6 +593,7 @@ static unsigned int ipq_pcs_inband_caps(
case PHY_INTERFACE_MODE_SGMII:
@@ -418,6 +454,8 @@ static unsigned int ipq_pcs_inband_caps(
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10GBASER:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_10GBASER:
+ return LINK_INBAND_DISABLE;
default:
return 0;
@@ -602,6 +650,9 @@ static void ipq_pcs_get_state(struct phy
}
@@ -472,6 +510,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);
+ ipq_pcs_get_state_10gbaser(qpcs, state);
+ break;
default:
break;
}
@@ -631,6 +682,8 @@ static int ipq_pcs_config(struct phylink
@@ -500,6 +541,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);
+ return ipq_pcs_config_10gbaser(qpcs);
default:
dev_err(qpcs->dev,
"interface %s not supported\n", phy_modes(interface));
@@ -662,6 +715,9 @@ static void ipq_pcs_link_up(struct phyli
return -EOPNOTSUPP;
};
@@ -524,6 +567,9 @@ 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:
+ /* Nothing to do here */
+ break;
+ return;
default:
dev_err(qpcs->dev,
"interface %s not supported\n", phy_modes(interface));
@@ -730,6 +786,7 @@ static unsigned long ipq_pcs_clk_rate_ge
return;
}
@@ -603,6 +649,7 @@ static unsigned long ipq_pcs_clk_rate_ge
{
switch (qpcs->interface) {
case PHY_INTERFACE_MODE_USXGMII:

View File

@ -1,4 +1,4 @@
From a2e687df29e457621616d5d769688e6c972f9ac6 Mon Sep 17 00:00:00 2001
From 0d3a93e3a5544daec59d8f10ac5ccab39849536e Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Tue, 2 Apr 2024 18:28:42 +0800
Subject: [PATCH] net: pcs: Add 2500BASEX interface mode support to IPQ UNIPHY
@ -12,33 +12,33 @@ connects with a 2.5G SFP module.
Change-Id: I3fe61113c1b3685debc20659736a9488216a029d
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Alex G: use regmap to read/write registers
's/ipq_unipcs/ipq_pcs/' in function names as suggested by Luo Jie
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 95 ++++++++++++++++++++++++++++++
1 file changed, 95 insertions(+)
drivers/net/pcs/pcs-qcom-ipq9574.c | 67 ++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -29,6 +29,7 @@
@@ -26,6 +26,7 @@
#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_SGMII_PLUS FIELD_PREP(PCS_MODE_SEL_MASK, 0x8)
+#define PCS_MODE_2500BASEX 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;
@@ -155,6 +156,29 @@ static void ipq_pcs_get_state_sgmii(stru
state->duplex = DUPLEX_HALF;
}
+static void ipq_unipcs_get_state_2500basex(struct ipq_pcs *qpcs,
+ int index,
+static void ipq_pcs_get_state_2500basex(struct ipq_pcs *qpcs,
+ struct phylink_link_state *state)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(qpcs->regmap, PCS_MII_STS(index), &val);
+ ret = regmap_read(qpcs->regmap, PCS_MII_STS(0), &val);
+ if (ret) {
+ state->link = 0;
+ return;
@ -58,99 +58,57 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
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
@@ -236,6 +260,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;
+ val = PCS_MODE_2500BASEX;
+ 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
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10GBASER:
val = PCS_MODE_XPCS;
@@ -314,6 +342,15 @@ 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)
+static int ipq_pcs_config_2500basex(struct ipq_pcs *qpcs)
+{
+ int ret;
+
+ if (qpcs->interface != interface) {
+ ret = ipq_pcs_config_mode(qpcs, interface);
+ if (ret)
+ return ret;
+
+ qpcs->interface = interface;
+ }
+
+ /* Configure PCS for 2500BASEX mode if required */
+ if (qpcs->interface == PHY_INTERFACE_MODE_2500BASEX)
+ return 0;
+
+ return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_2500BASEX);
+}
+
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(
@@ -388,6 +425,22 @@ 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)
+static int ipq_pcs_link_up_config_2500basex(struct ipq_pcs *qpcs, 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.
+ /* 2500BASEX does not support autoneg and does 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);
+ PCS_MII_CTRL(0), PCS_MII_ADPT_RESET);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(qpcs->regmap,
+ PCS_MII_CTRL(index), PCS_MII_ADPT_RESET);
+ PCS_MII_CTRL(0), PCS_MII_ADPT_RESET);
+}
+
static int ipq_pcs_link_up_config_usxgmii(struct ipq_pcs *qpcs, int speed)
{
unsigned int val;
@@ -576,6 +658,10 @@ static int ipq_pcs_validate(struct phyli
@@ -436,6 +489,10 @@ static int ipq_pcs_validate(struct phyli
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_10GBASER:
return 0;
@ -161,36 +119,44 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
case PHY_INTERFACE_MODE_USXGMII:
/* USXGMII only supports full duplex mode */
phylink_clear(supported, 100baseT_Half);
@@ -647,6 +733,9 @@ static void ipq_pcs_get_state(struct phy
case PHY_INTERFACE_MODE_PSGMII:
@@ -454,6 +511,7 @@ static unsigned int ipq_pcs_inband_caps(
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_USXGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_10GBASER:
return LINK_INBAND_DISABLE;
default:
@@ -507,6 +565,9 @@ static void ipq_pcs_get_state(struct phy
case PHY_INTERFACE_MODE_QSGMII:
ipq_pcs_get_state_sgmii(qpcs, index, state);
break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ ipq_unipcs_get_state_2500basex(qpcs, index, state);
+ ipq_pcs_get_state_2500basex(qpcs, state);
+ break;
case PHY_INTERFACE_MODE_USXGMII:
ipq_pcs_get_state_usxgmii(qpcs, state);
break;
@@ -680,6 +769,8 @@ static int ipq_pcs_config(struct phylink
@@ -539,6 +600,8 @@ static int ipq_pcs_config(struct phylink
case PHY_INTERFACE_MODE_SGMII:
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);
+ return ipq_pcs_config_2500basex(qpcs);
case PHY_INTERFACE_MODE_USXGMII:
return ipq_pcs_config_usxgmii(qpcs);
case PHY_INTERFACE_MODE_10GBASER:
@@ -712,6 +803,9 @@ static void ipq_pcs_link_up(struct phyli
@@ -564,6 +627,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);
+ ret = ipq_pcs_link_up_config_2500basex(qpcs, speed);
+ break;
case PHY_INTERFACE_MODE_USXGMII:
ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
break;
@@ -785,6 +879,7 @@ static int ipq_pcs_create_miis(struct ip
@@ -648,6 +714,7 @@ static int ipq_pcs_create_miis(struct ip
static unsigned long ipq_pcs_clk_rate_get(struct ipq_pcs *qpcs)
{
switch (qpcs->interface) {

View File

@ -1,4 +1,4 @@
From 07f9bb8eb006e9664d651089a1f422d045e093e3 Mon Sep 17 00:00:00 2001
From d82953614a4f09dd7479e1d3904351ff85d1d088 Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Tue, 9 Apr 2024 01:07:22 +0800
Subject: [PATCH] net: pcs: Add 1000BASEX interface mode support to IPQ UNIPHY
@ -10,70 +10,75 @@ Change-Id: Ied7298de3c1ecba74e6457a07fdd6b3ceab79728
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
drivers/net/pcs/pcs-qcom-ipq9574.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 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)
@@ -28,6 +28,9 @@
#define PCS_MODE_QSGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x1)
#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_CTRL_MASK GENMASK(6, 4)
+#define PCS_MODE_SGMII_CTRL_1000BASEX FIELD_PREP(PCS_MODE_SGMII_CTRL_MASK, \
+#define PCS_MODE_SGMII_MODE_MASK GENMASK(6, 4)
+#define PCS_MODE_SGMII_MODE_1000BASEX FIELD_PREP(PCS_MODE_SGMII_MODE_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
@@ -249,10 +252,11 @@ 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;
+ unsigned int val, mask;
int ret;
/* Assert XPCS reset */
@@ -297,6 +300,10 @@ static int ipq_pcs_config_mode(struct ip
/* Configure PCS interface mode */
+ mask = PCS_MODE_SEL_MASK;
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
val = PCS_MODE_SGMII;
@@ -260,6 +264,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;
+ mask |= PCS_MODE_SGMII_MODE_MASK;
+ val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_1000BASEX;
+ break;
case PHY_INTERFACE_MODE_2500BASEX:
val = PCS_MODE_SGMII_PLUS;
val = PCS_MODE_2500BASEX;
rate = 312500000;
@@ -316,7 +323,7 @@ static int ipq_pcs_config_mode(struct ip
@@ -273,8 +281,7 @@ static int ipq_pcs_config_mode(struct ip
return -EOPNOTSUPP;
}
ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL,
- ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL,
- PCS_MODE_SEL_MASK, val);
+ mask, val);
+ ret = regmap_update_bits(qpcs->regmap, PCS_MODE_CTRL, mask, val);
if (ret)
return ret;
@@ -523,6 +530,7 @@ ipq_unipcs_link_up_clock_rate_set(struct
@@ -487,6 +494,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:
rate = ipq_unipcs_clock_rate_get_gmii(speed);
break;
case PHY_INTERFACE_MODE_2500BASEX:
@@ -657,6 +665,7 @@ static int ipq_pcs_validate(struct phyli
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_1000BASEX:
return 0;
case PHY_INTERFACE_MODE_2500BASEX:
/* In-band autoneg is not supported for 2500BASEX */
@@ -731,6 +740,10 @@ static void ipq_pcs_get_state(struct phy
@@ -509,6 +517,7 @@ static unsigned int ipq_pcs_inband_caps(
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_USXGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
case PHY_INTERFACE_MODE_2500BASEX:
@@ -563,6 +572,10 @@ 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.
@ -81,18 +86,18 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
ipq_pcs_get_state_sgmii(qpcs, index, state);
break;
case PHY_INTERFACE_MODE_2500BASEX:
@@ -768,6 +781,7 @@ static int ipq_pcs_config(struct phylink
@@ -599,6 +612,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:
return ipq_unipcs_config_2500basex(qpcs, interface);
@@ -800,6 +814,7 @@ static void ipq_pcs_link_up(struct phyli
return ipq_pcs_config_2500basex(qpcs);
@@ -624,6 +638,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);

View File

@ -1,4 +1,4 @@
From 77462c0d74e51a24408062b93c3fcc0256909d33 Mon Sep 17 00:00:00 2001
From fc26c6f6c69149ce87c88d6878ae929b2a138063 Mon Sep 17 00:00:00 2001
From: Lei Wei <quic_leiwei@quicinc.com>
Date: Mon, 15 Apr 2024 11:06:02 +0800
Subject: [PATCH] net: pcs: Add 10G_QXGMII interface mode support to IPQ UNIPHY
@ -11,14 +11,14 @@ Change-Id: If3dc92a07ac3e51f7c9473fb05fa0668617916fb
Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 112 +++++++++++++++++++++++------
1 file changed, 91 insertions(+), 21 deletions(-)
drivers/net/pcs/pcs-qcom-ipq9574.c | 109 +++++++++++++++++++++++------
1 file changed, 87 insertions(+), 22 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)
@@ -48,6 +48,9 @@
#define PCS_MII_STS_SPEED_100 1
#define PCS_MII_STS_SPEED_1000 2
+#define PCS_QP_USXG_OPTION 0x584
+#define PCS_QP_USXG_GMII_SRC_XPCS BIT(0)
@ -26,8 +26,8 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
#define PCS_PLL_RESET 0x780
#define PCS_ANA_SW_RESET BIT(6)
@@ -68,10 +71,22 @@
#define XPCS_10GBASER_LINK_STS BIT(12)
@@ -63,10 +66,23 @@
#define XPCS_KR_LINK_STS BIT(12)
#define XPCS_DIG_CTRL 0x38000
+#define XPCS_SOFT_RESET BIT(15)
@ -41,73 +41,80 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+#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)
+/* DIG control for MII1 - MII3 */
+#define XPCS_MII1_DIG_CTRL(x) (0x1a8000 + 0x10000 * ((x) - 1))
+#define XPCS_MII1_USXG_ADPT_RESET BIT(5)
+
#define XPCS_MII_CTRL 0x1f0000
+#define XPCS_CHANNEL_MII_CTRL(x) (0x1a0000 + 0x10000 * ((x) - 1))
+#define XPCS_MII1_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 @@
@@ -78,9 +94,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_MII1_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_MII1_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 @@
@@ -90,6 +108,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_MII1_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
@@ -182,13 +204,14 @@ static void ipq_pcs_get_state_2500basex(
state->pause |= MLO_PAUSE_TXRX_MASK;
}
static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
+ int index,
-static void ipq_pcs_get_state_usxgmii(struct ipq_pcs *qpcs,
+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);
- unsigned int val;
+ unsigned int reg, val;
int ret;
- ret = regmap_read(qpcs->regmap, XPCS_MII_AN_INTR_STS, &val);
+ reg = (index == 0) ? XPCS_MII_AN_INTR_STS : XPCS_MII1_AN_INTR_STS(index);
+ 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;
@@ -273,6 +296,7 @@ static int ipq_pcs_config_mode(struct ip
rate = 312500000;
break;
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ val = PCS_MODE_XPCS;
+ rate = 312500000;
case PHY_INTERFACE_MODE_10GBASER:
val = PCS_MODE_XPCS;
rate = 312500000;
@@ -285,6 +309,13 @@ static int ipq_pcs_config_mode(struct ip
if (ret)
return ret;
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ 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;
+ }
+
/* PCS PLL reset */
ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
if (ret)
@@ -358,27 +389,51 @@ static int ipq_pcs_config_2500basex(stru
return ipq_pcs_config_mode(qpcs, PHY_INTERFACE_MODE_2500BASEX);
}
-static int ipq_pcs_config_usxgmii(struct ipq_pcs *qpcs)
@ -115,8 +122,8 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ int index,
+ phy_interface_t interface)
{
- int ret;
+ int ret, reg;
+ unsigned int reg;
int ret;
/* Configure the XPCS for USXGMII mode if required */
- if (qpcs->interface == PHY_INTERFACE_MODE_USXGMII)
@ -129,87 +136,66 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ 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);
- ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_USXG_EN);
- if (ret)
- return ret;
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL,
+ ret = regmap_update_bits(qpcs->regmap, XPCS_KR_CTRL,
+ XPCS_USXG_MODE_MASK, XPCS_10G_QXGMII_MODE);
+ if (ret)
+ return ret;
+
+ /* Set Alignment Marker Interval */
+ regmap_update_bits(qpcs->regmap, XPCS_DIG_STS,
+ /* Set Alignment Marker Interval value as 0x6018 */
+ ret = regmap_update_bits(qpcs->regmap, XPCS_DIG_STS,
+ XPCS_DIG_STS_AM_COUNT, 0x6018);
+ if (ret)
+ return ret;
+
+ regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET);
+ ret = regmap_set_bits(qpcs->regmap, XPCS_DIG_CTRL, XPCS_SOFT_RESET);
+ if (ret)
+ return ret;
+ }
+
+ 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);
+ reg = (index == 0) ? XPCS_XAUI_MODE_CTRL : XPCS_MII1_XAUI_MODE_CTRL(index);
+ ret = regmap_set_bits(qpcs->regmap, reg, XPCS_TX_IPG_CHECK_DIS);
+ if (ret)
+ return ret;
+ }
+
+ /* Enable autoneg */
+ reg = (index == 0) ? XPCS_MII_AN_CTRL : XPCS_CHANNEL_MII_AN_CTRL(index);
- ret = regmap_set_bits(qpcs->regmap, XPCS_MII_AN_CTRL, XPCS_MII_AN_8BIT);
+ reg = (index == 0) ? XPCS_MII_AN_CTRL : XPCS_MII1_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);
+ reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_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_config_10gbaser(struct ipq_pcs *qpcs)
@@ -448,9 +503,10 @@ static int ipq_pcs_link_up_config_2500ba
PCS_MII_CTRL(0), 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)
+ int index, int speed)
{
unsigned int val;
- int ret;
+ int ret, reg;
- unsigned int val;
+ unsigned int reg, val;
int ret;
switch (speed) {
case SPEED_10000:
@@ -648,14 +708,19 @@ static int ipq_pcs_link_up_config_usxgmi
@@ -478,14 +534,17 @@ 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);
+ reg = (index == 0) ? XPCS_MII_CTRL : XPCS_MII1_CTRL(index);
+ ret = regmap_update_bits(qpcs->regmap, reg,
XPCS_SPEED_MASK, val | XPCS_DUPLEX_FULL);
if (ret)
@ -217,25 +203,32 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
/* 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);
- XPCS_DIG_CTRL, XPCS_USXG_ADPT_RESET);
+ 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,
@@ -671,6 +736,7 @@ static int ipq_pcs_validate(struct phyli
/* In-band autoneg is not supported for 2500BASEX */
@@ -502,6 +561,7 @@ static int ipq_pcs_validate(struct phyli
phylink_clear(supported, Autoneg);
return 0;
+ case PHY_INTERFACE_MODE_10G_QXGMII:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
/* USXGMII only supports full duplex mode */
phylink_clear(supported, 100baseT_Half);
@@ -750,7 +816,8 @@ static void ipq_pcs_get_state(struct phy
ipq_unipcs_get_state_2500basex(qpcs, index, state);
phylink_clear(supported, 10baseT_Half);
@@ -519,6 +579,7 @@ static unsigned int ipq_pcs_inband_caps(
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_10GBASER:
@@ -582,7 +643,8 @@ static void ipq_pcs_get_state(struct phy
ipq_pcs_get_state_2500basex(qpcs, state);
break;
case PHY_INTERFACE_MODE_USXGMII:
- ipq_pcs_get_state_usxgmii(qpcs, state);
@ -243,20 +236,19 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ ipq_pcs_get_state_usxgmii(qpcs, index, state);
break;
case PHY_INTERFACE_MODE_10GBASER:
ipq_unipcs_get_state_10gbaser(qpcs, state);
@@ -786,7 +853,9 @@ static int ipq_pcs_config(struct phylink
ipq_pcs_get_state_10gbaser(qpcs, state);
@@ -617,7 +679,8 @@ static int ipq_pcs_config(struct phylink
case PHY_INTERFACE_MODE_2500BASEX:
return ipq_unipcs_config_2500basex(qpcs, interface);
return ipq_pcs_config_2500basex(qpcs);
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);
+ return ipq_pcs_config_usxgmii(qpcs, index, interface);
case PHY_INTERFACE_MODE_10GBASER:
return ipq_unipcs_config_10gbaser(qpcs, interface);
return ipq_pcs_config_10gbaser(qpcs);
default:
@@ -822,7 +891,8 @@ static void ipq_pcs_link_up(struct phyli
ret = ipq_unipcs_link_up_config_2500basex(qpcs, index, speed);
@@ -646,7 +709,8 @@ static void ipq_pcs_link_up(struct phyli
ret = ipq_pcs_link_up_config_2500basex(qpcs, speed);
break;
case PHY_INTERFACE_MODE_USXGMII:
- ret = ipq_pcs_link_up_config_usxgmii(qpcs, speed);
@ -265,3 +257,11 @@ Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
break;
case PHY_INTERFACE_MODE_10GBASER:
/* Nothing to do here */
@@ -731,6 +795,7 @@ static unsigned long ipq_pcs_clk_rate_ge
switch (qpcs->interface) {
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_10G_QXGMII:
case PHY_INTERFACE_MODE_10GBASER:
return 312500000;
default:

View File

@ -1,8 +1,7 @@
From 930203b9bb94dc4ea9342f1ce176851918758ed7 Mon Sep 17 00:00:00 2001
From 87da3bbd25eb0a17e2c698120528e76c26b326d0 Mon Sep 17 00:00:00 2001
From: Mantas Pucka <mantas@8devices.com>
Date: Mon, 2 Jun 2025 17:18:13 +0300
Subject: [PATCH] net: pcs: ipq-uniphy: control MISC2 register for 2.5G
support
Subject: [PATCH] net: pcs: ipq-uniphy: control MISC2 register for 2.5G support
When 2500base-x mode is enabled MISC2 regsister needs to have different
value than for other 1G modes.
@ -14,7 +13,7 @@ Signed-off-by: Mantas Pucka <mantas@8devices.com>
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -24,6 +24,11 @@
@@ -22,6 +22,11 @@
#define PCS_CALIBRATION 0x1e0
#define PCS_CALIBRATION_DONE BIT(7)
@ -26,16 +25,16 @@ Signed-off-by: Mantas Pucka <mantas@8devices.com>
#define PCS_MODE_CTRL 0x46c
#define PCS_MODE_SEL_MASK GENMASK(12, 8)
#define PCS_MODE_SGMII FIELD_PREP(PCS_MODE_SEL_MASK, 0x4)
@@ -311,7 +316,7 @@ static int ipq_pcs_config_mode(struct ip
@@ -275,7 +280,7 @@ static int ipq_pcs_config_mode(struct ip
phy_interface_t interface)
{
unsigned long rate = 125000000;
- unsigned int val, mask = PCS_MODE_SEL_MASK;
+ unsigned int val, misc2 = 0, mask = PCS_MODE_SEL_MASK;
- unsigned int val, mask;
+ unsigned int val, mask, misc2 = 0;
int ret;
/* Assert XPCS reset */
@@ -321,6 +326,7 @@ static int ipq_pcs_config_mode(struct ip
/* Configure PCS interface mode */
@@ -283,6 +288,7 @@ static int ipq_pcs_config_mode(struct ip
switch (interface) {
case PHY_INTERFACE_MODE_SGMII:
val = PCS_MODE_SGMII;
@ -43,30 +42,29 @@ Signed-off-by: Mantas Pucka <mantas@8devices.com>
break;
case PHY_INTERFACE_MODE_QSGMII:
val = PCS_MODE_QSGMII;
@@ -328,10 +334,12 @@ static int ipq_pcs_config_mode(struct ip
@@ -290,9 +296,11 @@ static int ipq_pcs_config_mode(struct ip
case PHY_INTERFACE_MODE_1000BASEX:
mask |= PCS_MODE_SGMII_CTRL_MASK;
val = PCS_MODE_SGMII | PCS_MODE_SGMII_CTRL_1000BASEX;
mask |= PCS_MODE_SGMII_MODE_MASK;
val = PCS_MODE_SGMII | PCS_MODE_SGMII_MODE_1000BASEX;
+ misc2 = PCS_MISC2_MODE_SGMII;
break;
case PHY_INTERFACE_MODE_2500BASEX:
val = PCS_MODE_SGMII_PLUS;
rate = 312500000;
val = PCS_MODE_2500BASEX;
+ misc2 = PCS_MISC2_MODE_SGMII_PLUS;
rate = 312500000;
break;
case PHY_INTERFACE_MODE_PSGMII:
val = PCS_MODE_PSGMII;
@@ -360,6 +368,13 @@ static int ipq_pcs_config_mode(struct ip
case PHY_INTERFACE_MODE_USXGMII:
@@ -315,6 +323,13 @@ static int ipq_pcs_config_mode(struct ip
if (ret)
return ret;
}
+
+ if (misc2) {
+ ret = regmap_update_bits(qpcs->regmap, PCS_MISC2,
+ PCS_MISC2_MODE_MASK, misc2);
+ if (ret)
+ return ret;
+ }
+
/* PCS PLL reset */
ret = regmap_clear_bits(qpcs->regmap, PCS_PLL_RESET, PCS_ANA_SW_RESET);
if (ret)

View File

@ -1,25 +0,0 @@
From ccdfd293f9e948f0f62ac4e9924d72539a4e81ee Mon Sep 17 00:00:00 2001
From: Mantas Pucka <mantas@8devices.com>
Date: Mon, 2 Jun 2025 17:19:45 +0300
Subject: [PATCH] net: pcs: ipq-uniphy: keep autoneg enabled in SGMII mode
For PHYs that don't use in-band-status (e.g. 2.5G PHY swiching between
SGMII and 2500base-x), SGMII autoneg still must be enabled. Only mode
that should use forced speed is 1000base-x
Signed-off-by: Mantas Pucka <mantas@8devices.com>
---
drivers/net/pcs/pcs-qcom-ipq9574.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -431,7 +431,7 @@ static int ipq_pcs_config_sgmii(struct i
/* Nothing to do here as in-band autoneg mode is enabled
* by default for each PCS MII port.
*/
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ if (interface != PHY_INTERFACE_MODE_1000BASEX)
return 0;
/* Set force speed mode */

View File

@ -1,4 +1,4 @@
From 0cff1d9bb695bdc0ad7bad234b92eddf849ce88f Mon Sep 17 00:00:00 2001
From bedf56b46ae53c4abb21eebb3e1d5a7483926dda Mon Sep 17 00:00:00 2001
From: Mantas Pucka <mantas@8devices.com>
Date: Mon, 2 Jun 2025 17:20:58 +0300
Subject: [PATCH] net: pcs: ipq-uniphy: fix USXGMII link-up failure
@ -13,7 +13,7 @@ Signed-off-by: Mantas Pucka <mantas@8devices.com>
--- a/drivers/net/pcs/pcs-qcom-ipq9574.c
+++ b/drivers/net/pcs/pcs-qcom-ipq9574.c
@@ -380,7 +380,7 @@ static int ipq_pcs_config_mode(struct ip
@@ -336,7 +336,7 @@ static int ipq_pcs_config_mode(struct ip
if (ret)
return ret;

View File

@ -0,0 +1,282 @@
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;