1
1
openwrt/target/linux/qualcommbe/patches-6.18/0312-net-phy-qca808x-Add-QCA8084-SerDes-speed-config.patch
Alexandru Gagniuc 809ca978d1 qualcommbe: kernel-6.18: update patches
Generate new patches for 6.18 from my ipq95xx development branch.

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

252 lines
7.5 KiB
Diff

From c2d664e748ce7b96dbd07f79091527aaf1725165 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@quicinc.com>
Date: Mon, 23 Sep 2024 21:24:56 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes speed config
When the link of PHY is changed, the XPCS channel needs to be
configured to adapt the current link status.
Change-Id: I50d8973691dff133fc6bec1e9a1043bb646811fc
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
Alex G: Use phy_package_get_*() accessors
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
drivers/net/phy/qcom/qca8084_serdes.c | 159 ++++++++++++++++++++++++++
drivers/net/phy/qcom/qca8084_serdes.h | 3 +
drivers/net/phy/qcom/qca808x.c | 15 ++-
3 files changed, 175 insertions(+), 2 deletions(-)
--- a/drivers/net/phy/qcom/qca8084_serdes.c
+++ b/drivers/net/phy/qcom/qca8084_serdes.c
@@ -4,6 +4,7 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/dev_printk.h>
#include <linux/mdio.h>
#include <linux/of.h>
@@ -55,6 +56,13 @@
#define BYPASS_TUNNING_IPG 0x189
#define BYPASS_TUNNING_IPG_MASK GENMASK(11, 0)
+#define QP_USXG_RESET 0x18c
+#define QP_USXG_SGMII_FUNC_RESET BIT(4)
+#define QP_USXG_P3_FUNC_RESET BIT(3)
+#define QP_USXG_P2_FUNC_RESET BIT(2)
+#define QP_USXG_P1_FUNC_RESET BIT(1)
+#define QP_USXG_P0_FUNC_RESET BIT(0)
+
/* MDIO_MMD_PCS register */
#define PCS_CONTROL2 0x7
#define PCS_TYPE_MASK GENMASK(3, 0)
@@ -107,6 +115,9 @@
#define TX_CONFIG BIT(3)
#define AUTO_NEGOTIATION_CMPLT_INTR BIT(0)
+#define PCS_ERR_SEL 0x8002
+#define PCS_AN_COMPLETE BIT(0)
+
#define XAUI_CONTROL 0x8004
#define TX_IPG_CHECK_DISABLE BIT(0)
@@ -621,3 +632,151 @@ int qca8084_qxgmii_set_mode(struct mdio_
return qca8084_xpcs_set_mode(xpcs_mdiodev);
}
+
+static int qca8084_pcs_ipg_tune_reset(struct mdio_device *mdio_dev,
+ int reset_function)
+{
+ int ret;
+
+ ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
+ reset_function, 0);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 1100);
+
+ return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_RESET,
+ reset_function, reset_function);
+}
+
+static int qca8084_xpcs_an_restart(struct mdio_device *xpcs_mdiodev,
+ int channel)
+{
+ int ret, mmd;
+
+ mmd = qca8084_xpcs_ch_mmd[channel];
+
+ /* Restart auto-negotiation */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
+ AUTO_NEGOTIATION_RESTART,
+ AUTO_NEGOTIATION_RESTART);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 1100);
+
+ /* Clear pcs auto-negotiation complete interrupt */
+ return mdiodev_c45_modify(xpcs_mdiodev, mmd, PCS_ERR_SEL,
+ PCS_AN_COMPLETE, 0);
+}
+
+void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev,
+ int channel, int speed)
+{
+ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
+ struct qca8084_xpcs_channel_priv *xpcs_ch;
+ int mmd, i, ret, xpcs_rate;
+ unsigned long rate;
+
+ for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
+ xpcs_ch = &(xpcs_data->xpcs_ch[channel]);
+ if (channel == xpcs_ch->ch_id)
+ break;
+ }
+
+ if (i == QCA8084_CHANNEL_MAX) {
+ dev_err(&xpcs_mdiodev->dev, "Invalid channel %d\n", channel);
+ return;
+ }
+
+ mmd = qca8084_xpcs_ch_mmd[channel];
+
+ ret = qca8084_xpcs_an_restart(xpcs_mdiodev, channel);
+ if (ret)
+ return;
+
+ switch (speed) {
+ case SPEED_2500:
+ rate = 312500000;
+ xpcs_rate = PCS_SPEED_2500;
+ break;
+ case SPEED_1000:
+ rate = 125000000;
+ xpcs_rate = PCS_SPEED_1000;
+ break;
+ case SPEED_100:
+ rate = 25000000;
+ xpcs_rate = PCS_SPEED_100;
+ break;
+ case SPEED_10:
+ default:
+ rate = 2500000;
+ xpcs_rate = PCS_SPEED_10;
+ break;
+ }
+
+ clk_set_rate(xpcs_ch->clks[XPCS_RX_CLK], rate);
+ clk_set_rate(xpcs_ch->clks[XPCS_TX_CLK], rate);
+
+ /* XGMII takes the different clock rate 78.125Mhz from XPCS clock
+ * when linked at 2500M.
+ */
+ if (speed == SPEED_2500)
+ rate = 78125000;
+
+ clk_set_rate(xpcs_ch->clks[XPCS_XGMII_RX_CLK], rate);
+ clk_set_rate(xpcs_ch->clks[XPCS_XGMII_TX_CLK], rate);
+
+ ret = mdiodev_c45_modify(xpcs_mdiodev, mmd, MII_CONTROL,
+ PCS_SPEED_2500 | PCS_SPEED_1000 |
+ PCS_SPEED_100 | PCS_SPEED_10,
+ xpcs_rate);
+ if (ret)
+ return;
+
+ /* Disable clocks if link down with unknown speed. The channel clocks
+ * are disabled by default, __clk_is_enabled() is used to avoid
+ * disabling the clocks that is already in the disabled status.
+ */
+ if (speed == SPEED_UNKNOWN) {
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_RX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_RX_CLK]);
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_TX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_TX_CLK]);
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_RX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_PORT_TX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_RX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
+ if (__clk_is_enabled(xpcs_ch->clks[XPCS_XGMII_TX_CLK]))
+ clk_disable_unprepare(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
+ } else {
+ clk_prepare_enable(xpcs_ch->clks[XPCS_RX_CLK]);
+ clk_prepare_enable(xpcs_ch->clks[XPCS_TX_CLK]);
+ clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_RX_CLK]);
+ clk_prepare_enable(xpcs_ch->clks[XPCS_PORT_TX_CLK]);
+ clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_RX_CLK]);
+ clk_prepare_enable(xpcs_ch->clks[XPCS_XGMII_TX_CLK]);
+ }
+
+ msleep(100);
+
+ ret = reset_control_reset(xpcs_ch->rstcs);
+ if (ret)
+ return;
+
+ /* Reset IPG tune of PCS device. */
+ ret = qca8084_pcs_ipg_tune_reset(pcs_mdiodev, BIT(channel));
+ if (ret)
+ return;
+
+ if (channel == 0)
+ mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
+ FIFO_RESET_CH0, FIFO_RESET_CH0);
+ else
+ mdiodev_c45_modify(xpcs_mdiodev, mmd, DIG_CTRL1,
+ FIFO_RESET_CH1_CH2_CH3,
+ FIFO_RESET_CH1_CH2_CH3);
+}
--- a/drivers/net/phy/qcom/qca8084_serdes.h
+++ b/drivers/net/phy/qcom/qca8084_serdes.h
@@ -17,4 +17,7 @@ void qca8084_package_xpcs_and_pcs_remove
struct mdio_device *pcs_mdiodev);
int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
struct mdio_device *pcs_mdiodev);
+void qca8084_qxgmii_set_speed(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev,
+ int channel, int speed);
#endif /* _QCA8084_SERDES_H_ */
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
@@ -997,6 +997,7 @@ static int qca8084_config_init(struct ph
static void qca8084_link_change_notify(struct phy_device *phydev)
{
+ struct qca808x_shared_priv *shared_priv;
int ret;
/* Assert the FIFO between PHY and MAC. */
@@ -1028,14 +1029,24 @@ static void qca8084_link_change_notify(s
}
}
- /* Enable IPG level 10 to 11 tuning for link speed 1000M in the
+ /* Enable IPG level 10 to 11 tuning for link speed 1000M and
+ * configure the related XPCS channel with the phydev in the
* 10G_QXGMII mode.
*/
- if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ shared_priv = phy_package_get_priv(phydev);
+ struct qca808x_priv *priv = phydev->priv;
+
phy_modify_mmd(phydev, MDIO_MMD_AN, QCA8084_MMD7_IPG_OP,
QCA8084_IPG_10_TO_11_EN,
phydev->speed == SPEED_1000 ?
QCA8084_IPG_10_TO_11_EN : 0);
+
+ qca8084_qxgmii_set_speed(shared_priv->mdiodev[1],
+ shared_priv->mdiodev[0],
+ priv->channel_id,
+ phydev->speed);
+ }
}
/* QCA8084 is a four-port PHY, which integrates the clock controller,