1
1
openwrt/target/linux/qualcommbe/patches-6.18/0311-net-phy-qca808x-Add-QCA8084-SerDes-init-function.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

447 lines
12 KiB
Diff

From d137b725f8f4a7d49a809dcd73c5b836495ec44d Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@quicinc.com>
Date: Mon, 23 Sep 2024 20:59:40 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 SerDes init function
When QCA8084 works on 10G-QXGMII, the XPCS and PCS need to be
configured in the PHY package init function.
Change-Id: Iac48c44f0e80adf055fa9c2095e99a04ba24c4bb
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
---
drivers/net/phy/qcom/qca8084_serdes.c | 374 ++++++++++++++++++++++++++
drivers/net/phy/qcom/qca8084_serdes.h | 2 +
drivers/net/phy/qcom/qca808x.c | 11 +
3 files changed, 387 insertions(+)
--- a/drivers/net/phy/qcom/qca8084_serdes.c
+++ b/drivers/net/phy/qcom/qca8084_serdes.c
@@ -24,6 +24,92 @@
*/
#define QCA8084_CHANNEL_MAX 4
+/* MII registers */
+#define PLL_POWER_ON_AND_RESET 0x0
+#define PCS_ANA_SW_RESET BIT(6)
+
+#define PLL_CONTROL 6
+#define PLL_CONTROL_CMLDIV2_IBSEL_MASK GENMASK(5, 4)
+
+/* MMD_PMAPMD registers */
+#define CDR_CONTRL 0x20
+#define SSC_FIX_MODE BIT(3)
+
+#define CALIBRATION4 0x78
+#define CALIBRATION_DONE BIT(7)
+
+#define MODE_CONTROL 0x11b
+#define MODE_CONTROL_SEL_MASK GENMASK(12, 8)
+#define MODE_CONTROL_XPCS 0x10
+#define MODE_CONTROL_SGMII_PLUS 0x8
+#define MODE_CONTROL_SGMII 0x4
+#define MODE_CONTROL_SGMII_SEL_MASK GENMASK(6, 4)
+#define MODE_CONTROL_SGMII_PHY 1
+#define MODE_CONTROL_SGMII_MAC 2
+
+#define QP_USXG_OPTION1 0x180
+#define QP_USXG_OPTION1_DATAPASS BIT(0)
+#define QP_USXG_OPTION1_DATAPASS_SGMII 0
+#define QP_USXG_OPTION1_DATAPASS_USXGMII 1
+
+#define BYPASS_TUNNING_IPG 0x189
+#define BYPASS_TUNNING_IPG_MASK GENMASK(11, 0)
+
+/* MDIO_MMD_PCS register */
+#define PCS_CONTROL2 0x7
+#define PCS_TYPE_MASK GENMASK(3, 0)
+#define PCS_TYPE_BASER 0
+
+#define PCS_EEE_CONTROL 0x14
+#define EEE_CAPABILITY BIT(6)
+
+#define PCS_STATUS1 0x20
+#define PCS_BASER_UP BIT(12)
+
+#define DIG_CTRL1 0x8000
+#define DIG_CTRL1_USXGMII_EN BIT(9)
+#define DIG_CTRL1_XPCS_RESET BIT(15)
+#define FIFO_RESET_CH0 BIT(10)
+#define FIFO_RESET_CH1_CH2_CH3 BIT(5)
+
+#define EEE_MODE_CONTROL 0x8006
+#define EEE_LCT_RES GENMASK(11, 8)
+#define EEE_SIGN BIT(6)
+#define EEE_LRX_EN BIT(1)
+#define EEE_LTX_EN BIT(0)
+
+#define PCS_TPC 0x8007
+#define PCS_QXGMII_MODE_MASK GENMASK(12, 10)
+#define PCS_QXGMII_EN 0x5
+
+#define EEE_RX_TIMER 0x8009
+#define EEE_RX_TIMER_100US_RES GENMASK(7, 0)
+#define EEE_RX_TIMER_RWR_RES GENMASK(12, 8)
+
+#define AM_LINK_TIMER 0x800a
+#define AM_LINK_TIMER_VAL 0x6018
+
+#define EEE_MODE_CONTROL1 0x800b
+#define TRANS_LPI_MODE BIT(0)
+#define TRANS_RX_LPI_MODE BIT(8)
+
+/* QXGMII channel MMD register */
+#define MII_CONTROL 0x0
+#define AUTO_NEGOTIATION_EN BIT(12)
+#define AUTO_NEGOTIATION_RESTART BIT(9)
+#define PCS_SPEED_2500 BIT(5)
+#define PCS_SPEED_1000 BIT(6)
+#define PCS_SPEED_100 BIT(13)
+#define PCS_SPEED_10 0
+
+#define DIG_CONTROL2 0x8001
+#define MII_BIT_CONTROL BIT(8)
+#define TX_CONFIG BIT(3)
+#define AUTO_NEGOTIATION_CMPLT_INTR BIT(0)
+
+#define XAUI_CONTROL 0x8004
+#define TX_IPG_CHECK_DISABLE BIT(0)
+
enum pcs_clk_id {
PCS_CLK,
PCS_RX_ROOT_CLK,
@@ -76,6 +162,8 @@ static const char *const pcs_clock_names
[PCS_TX_ROOT_CLK] = "pcs_tx_root",
};
+static const int qca8084_xpcs_ch_mmd[QCA8084_CHANNEL_MAX] = { 31, 26, 27, 28 };
+
struct mdio_device *qca8084_package_pcs_probe(struct device_node *pcs_np)
{
struct qca8084_pcs_data *pcs_data;
@@ -247,3 +335,289 @@ void qca8084_package_xpcs_and_pcs_remove
mdio_device_put(xpcs_mdiodev);
mdio_device_put(pcs_mdiodev);
}
+
+static int qca8084_pcs_set_interface_mode(struct mdio_device *mdio_dev,
+ phy_interface_t ifmode)
+{
+ int ret, hw_ifmode, data;
+
+ switch (ifmode) {
+ case PHY_INTERFACE_MODE_SGMII:
+ hw_ifmode = MODE_CONTROL_SGMII;
+ data = QP_USXG_OPTION1_DATAPASS_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ hw_ifmode = MODE_CONTROL_SGMII_PLUS;
+ data = QP_USXG_OPTION1_DATAPASS_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ hw_ifmode = MODE_CONTROL_XPCS;
+ data = QP_USXG_OPTION1_DATAPASS_USXGMII;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* For PLL stable under high temperature */
+ ret = mdiodev_modify(mdio_dev, PLL_CONTROL,
+ PLL_CONTROL_CMLDIV2_IBSEL_MASK,
+ FIELD_PREP(PLL_CONTROL_CMLDIV2_IBSEL_MASK, 3));
+ if (ret)
+ return ret;
+
+ /* Configure the interface mode of PCS */
+ ret = mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, MODE_CONTROL,
+ MODE_CONTROL_SEL_MASK,
+ FIELD_PREP(MODE_CONTROL_SEL_MASK, hw_ifmode));
+ if (ret)
+ return ret;
+
+ /* Data pass selects SGMII or USXGMII */
+ return mdiodev_c45_modify(mdio_dev, MDIO_MMD_PMAPMD, QP_USXG_OPTION1,
+ QP_USXG_OPTION1_DATAPASS,
+ FIELD_PREP(QP_USXG_OPTION1_DATAPASS, data));
+}
+
+static int qca8084_do_calibration(struct mdio_device *mdio_dev)
+{
+ int ret;
+
+ ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
+ PCS_ANA_SW_RESET, 0);
+ if (ret)
+ return ret;
+
+ usleep_range(10000, 11000);
+ ret = mdiodev_modify(mdio_dev, PLL_POWER_ON_AND_RESET,
+ PCS_ANA_SW_RESET, PCS_ANA_SW_RESET);
+ if (ret)
+ return ret;
+
+ /* Wait calibration done */
+ return read_poll_timeout(mdiodev_c45_read, ret,
+ (ret & CALIBRATION_DONE),
+ 100, 100000, true, mdio_dev,
+ MDIO_MMD_PMAPMD, CALIBRATION4);
+}
+
+
+static int qca8084_xpcs_set_mode(struct mdio_device *xpcs_mdiodev)
+{
+ int ret, val, i;
+
+ /* Configure BaseR mode */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_CONTROL2,
+ PCS_TYPE_MASK,
+ FIELD_PREP(PCS_TYPE_MASK, PCS_TYPE_BASER));
+ if (ret)
+ return ret;
+
+ /* Wait BaseR link up */
+ ret = read_poll_timeout(mdiodev_c45_read, val,
+ (val & PCS_BASER_UP), 1000, 100000, true,
+ xpcs_mdiodev,
+ MDIO_MMD_PCS, PCS_STATUS1);
+ if (ret) {
+ dev_err(&xpcs_mdiodev->dev, "BaseR link failed!\n");
+ return ret;
+ }
+
+ /* Enable USXGMII mode */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
+ DIG_CTRL1_USXGMII_EN,
+ DIG_CTRL1_USXGMII_EN);
+ if (ret)
+ return ret;
+
+ /* Configure QXGMII mode */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, PCS_TPC,
+ PCS_QXGMII_MODE_MASK,
+ FIELD_PREP(PCS_QXGMII_MODE_MASK,
+ PCS_QXGMII_EN));
+ if (ret)
+ return ret;
+
+ /* Configure AM interval */
+ ret = mdiodev_c45_write(xpcs_mdiodev, MDIO_MMD_PCS, AM_LINK_TIMER,
+ AM_LINK_TIMER_VAL);
+ if (ret)
+ return ret;
+
+ /* Reset XPCS */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS, DIG_CTRL1,
+ DIG_CTRL1_XPCS_RESET,
+ DIG_CTRL1_XPCS_RESET);
+ if (ret)
+ return ret;
+
+ /* Wait XPCS reset done */
+ ret = read_poll_timeout(mdiodev_c45_read, val,
+ !(val & DIG_CTRL1_XPCS_RESET),
+ 1000, 100000, true, xpcs_mdiodev,
+ MDIO_MMD_PCS, DIG_CTRL1);
+ if (ret) {
+ dev_err(&xpcs_mdiodev->dev, "XPCS reset failed!\n");
+ return ret;
+ }
+
+ /* Enable auto-negotiation complete interrupt, using mii-4bit
+ * and TX configureation of PHY side on all XPCS channels.
+ */
+ for (i = 0; i < QCA8084_CHANNEL_MAX; i++) {
+ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
+ DIG_CONTROL2,
+ (MII_BIT_CONTROL | TX_CONFIG |
+ AUTO_NEGOTIATION_CMPLT_INTR),
+ (TX_CONFIG | AUTO_NEGOTIATION_CMPLT_INTR));
+ if (ret)
+ return ret;
+
+ /* Enable auto-negotiation capability */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
+ MII_CONTROL,
+ AUTO_NEGOTIATION_EN,
+ AUTO_NEGOTIATION_EN);
+ if (ret)
+ return ret;
+
+ /* Disable TX IPG check */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, qca8084_xpcs_ch_mmd[i],
+ XAUI_CONTROL,
+ TX_IPG_CHECK_DISABLE,
+ TX_IPG_CHECK_DISABLE);
+ if (ret)
+ return ret;
+ }
+
+ /* Check EEE capability supported or not */
+ ret = mdiodev_c45_read(xpcs_mdiodev, MDIO_MMD_PCS, PCS_EEE_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ if (ret & EEE_CAPABILITY) {
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
+ EEE_MODE_CONTROL,
+ EEE_LCT_RES | EEE_SIGN,
+ FIELD_PREP(EEE_LCT_RES, 1) | EEE_SIGN);
+ if (ret)
+ return ret;
+
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
+ EEE_RX_TIMER,
+ EEE_RX_TIMER_100US_RES | EEE_RX_TIMER_RWR_RES,
+ FIELD_PREP(EEE_RX_TIMER_100US_RES, 0xc8) |
+ FIELD_PREP(EEE_RX_TIMER_RWR_RES, 0x1c));
+ if (ret)
+ return ret;
+
+ /* Enable EEE LPI */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
+ EEE_MODE_CONTROL1,
+ TRANS_LPI_MODE | TRANS_RX_LPI_MODE,
+ TRANS_LPI_MODE | TRANS_RX_LPI_MODE);
+ if (ret)
+ return ret;
+
+ /* Enable TX/RX LPI pattern */
+ ret = mdiodev_c45_modify(xpcs_mdiodev, MDIO_MMD_PCS,
+ EEE_MODE_CONTROL,
+ EEE_LRX_EN | EEE_LTX_EN,
+ EEE_LRX_EN | EEE_LTX_EN);
+ }
+
+ return ret;
+}
+
+static int qca8084_pcs_set_mode(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev)
+{
+ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
+ struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
+ struct qca8084_xpcs_channel_priv xpcs_ch;
+ int ret, channel;
+
+ /* Enable clock and de-assert for PCS. */
+ ret = clk_prepare_enable(pcs_data->clks[PCS_CLK]);
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(pcs_data->rstc);
+ if (ret)
+ return ret;
+
+ /* IPG tunning selection for RX, TX and XGMII of all channels. */
+ ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
+ BYPASS_TUNNING_IPG,
+ BYPASS_TUNNING_IPG_MASK, 0);
+ if (ret)
+ return ret;
+
+ reset_control_assert(xpcs_data->rstc);
+
+ ret = qca8084_pcs_set_interface_mode(pcs_mdiodev,
+ PHY_INTERFACE_MODE_10G_QXGMII);
+ if (ret)
+ return ret;
+
+ /* Reset of 4 channels */
+ for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
+ xpcs_ch = xpcs_data->xpcs_ch[channel];
+ ret = reset_control_reset(xpcs_ch.rstcs);
+ if (ret)
+ return ret;
+ }
+
+ ret = qca8084_do_calibration(pcs_mdiodev);
+ if (ret) {
+ dev_err(&pcs_mdiodev->dev, "PCS calibration timeout!\n");
+ return ret;
+ }
+
+ /* Enable PCS SSC to fix mode */
+ ret = mdiodev_c45_modify(pcs_mdiodev, MDIO_MMD_PMAPMD,
+ CDR_CONTRL, SSC_FIX_MODE, SSC_FIX_MODE);
+ if (ret)
+ return ret;
+
+ return reset_control_deassert(xpcs_data->rstc);
+}
+
+static int qca8084_xpcs_clock_parent_set(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev)
+{
+ struct qca8084_xpcs_data *xpcs_data = mdiodev_get_drvdata(xpcs_mdiodev);
+ struct qca8084_pcs_data *pcs_data = mdiodev_get_drvdata(pcs_mdiodev);
+ struct qca8084_xpcs_channel_priv xpcs_ch;
+ int ret, channel;
+
+ for (channel = 0; channel < QCA8084_CHANNEL_MAX; channel++) {
+ xpcs_ch = xpcs_data->xpcs_ch[channel];
+ ret = clk_set_parent(xpcs_ch.clks[XPCS_RX_SRC_CLK],
+ pcs_data->clks[PCS_RX_ROOT_CLK]);
+ if (ret)
+ return ret;
+
+ ret = clk_set_parent(xpcs_ch.clks[XPCS_TX_SRC_CLK],
+ pcs_data->clks[PCS_TX_ROOT_CLK]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev)
+{
+ int ret;
+
+ ret = qca8084_xpcs_clock_parent_set(xpcs_mdiodev, pcs_mdiodev);
+ if (ret)
+ return ret;
+
+ ret = qca8084_pcs_set_mode(xpcs_mdiodev, pcs_mdiodev);
+ if (ret)
+ return ret;
+
+ return qca8084_xpcs_set_mode(xpcs_mdiodev);
+}
--- a/drivers/net/phy/qcom/qca8084_serdes.h
+++ b/drivers/net/phy/qcom/qca8084_serdes.h
@@ -15,4 +15,6 @@ struct mdio_device *qca8084_package_pcs_
struct mdio_device *qca8084_package_xpcs_probe(struct device_node *xpcs_np);
void qca8084_package_xpcs_and_pcs_remove(struct mdio_device *xpcs_mdiodev,
struct mdio_device *pcs_mdiodev);
+int qca8084_qxgmii_set_mode(struct mdio_device *xpcs_mdiodev,
+ struct mdio_device *pcs_mdiodev);
#endif /* _QCA8084_SERDES_H_ */
--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
@@ -926,6 +926,14 @@ static int qca8084_phy_package_config_in
usleep_range(10000, 11000);
+ /* Configure PCS working on 10G-QXGMII mode */
+ if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ ret = qca8084_qxgmii_set_mode(shared_priv->mdiodev[1],
+ shared_priv->mdiodev[0]);
+ if (ret)
+ return ret;
+ }
+
/* Initialize the PHY package clock and reset, which is the
* necessary config sequence after GPIO reset on the PHY package.
*/
@@ -1164,6 +1172,9 @@ static int qca8084_probe(struct phy_devi
static void qca8084_remove(struct phy_device *phydev)
{
+ if (phydev->interface != PHY_INTERFACE_MODE_10G_QXGMII)
+ return;
+
if (phy_package_remove_once(phydev))
qca8084_phy_package_remove_once(phydev);
}