openwrt/target/linux/qualcommbe/patches-6.6/103-15-net-pcs-Add-10G_QXGMII-interface-mode-support-to-IPQ.patch
Christian Marangi 93173aee96
qualcommbe: ipq95xx: Add initial support for new target
Add initial support for new target with the initial patch for ethernet
support using pending upstream patches for PCS UNIPHY, PPE and EDMA.

Only initramfs currently working as support for new SPI/NAND
implementation, USB, CPUFreq and other devices is still unfinished and
needs to be evaluated.

Link: https://github.com/openwrt/openwrt/pull/17725
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
2025-01-25 21:24:06 +01:00

365 lines
11 KiB
Diff

From d96ec0527b0f5618b3a0757b47606705555ee996 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 15/50] 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@quicinc.com>
---
drivers/net/pcs/pcs-qcom-ipq-uniphy.c | 174 +++++++++++++++++++++-----
1 file changed, 142 insertions(+), 32 deletions(-)
diff --git a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
index 820d197744e8..a98180c91632 100644
--- a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
+++ b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c
@@ -50,6 +50,9 @@
#define PCS_CHANNEL_STS_PAUSE_TX_EN BIT(1)
#define PCS_CHANNEL_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)
@@ -65,10 +68,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))
@@ -80,9 +95,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_DUPLEX_FULL BIT(13)
#define XPCS_USXG_AN_SPEED_MASK GENMASK(12, 10)
@@ -93,6 +110,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)
+
/* UNIPHY PCS RAW clock ID */
enum {
PCS_RAW_RX_CLK = 0,
@@ -153,6 +174,7 @@ struct ipq_uniphy_pcs {
struct device *dev;
phy_interface_t interface;
struct mutex shared_lock; /* Lock to protect shared config */
+ spinlock_t reg_lock; /* Lock for register access */
struct clk *clk[PCS_CLK_MAX];
struct reset_control *reset[PCS_RESET_MAX];
struct ipq_unipcs_raw_clk raw_clk[PCS_RAW_CLK_MAX];
@@ -215,39 +237,55 @@ static const struct clk_ops ipq_unipcs_raw_clk_ops = {
static u32 ipq_unipcs_reg_read32(struct ipq_uniphy_pcs *qunipcs, u32 reg)
{
+ u32 val;
+
/* PCS use direct AHB access while XPCS use indirect AHB access */
if (reg >= XPCS_INDIRECT_ADDR) {
+ /* For XPCS, althrough the register is different for different
+ * channels, but they use the same indirect AHB address to
+ * access, so add protects here.
+ */
+ spin_lock(&qunipcs->reg_lock);
+
writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
- return readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
+ val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
+
+ spin_unlock(&qunipcs->reg_lock);
+ return val;
} else {
return readl(qunipcs->base + reg);
}
}
-static void ipq_unipcs_reg_write32(struct ipq_uniphy_pcs *qunipcs,
- u32 reg, u32 val)
+static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
+ u32 reg, u32 mask, u32 set)
{
+ u32 val;
+
if (reg >= XPCS_INDIRECT_ADDR) {
+ spin_lock(&qunipcs->reg_lock);
+
+ /* XPCS read */
writel(FIELD_GET(XPCS_INDIRECT_ADDR_H, reg),
qunipcs->base + XPCS_INDIRECT_AHB_ADDR);
+ val = readl(qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
+
+ val &= ~mask;
+ val |= set;
+
+ /* XPCS write */
writel(val, qunipcs->base + XPCS_INDIRECT_DATA_ADDR(reg));
+
+ spin_unlock(&qunipcs->reg_lock);
} else {
+ val = readl(qunipcs->base + reg);
+ val &= ~mask;
+ val |= set;
writel(val, qunipcs->base + reg);
}
}
-static void ipq_unipcs_reg_modify32(struct ipq_uniphy_pcs *qunipcs,
- u32 reg, u32 mask, u32 set)
-{
- u32 val;
-
- val = ipq_unipcs_reg_read32(qunipcs, reg);
- val &= ~mask;
- val |= set;
- ipq_unipcs_reg_write32(qunipcs, reg, val);
-}
-
static void ipq_unipcs_get_state_sgmii(struct ipq_uniphy_pcs *qunipcs,
int channel,
struct phylink_link_state *state)
@@ -305,11 +343,15 @@ static void ipq_unipcs_get_state_2500basex(struct ipq_uniphy_pcs *qunipcs,
}
static void ipq_unipcs_get_state_usxgmii(struct ipq_uniphy_pcs *qunipcs,
+ int channel,
struct phylink_link_state *state)
{
- u32 val;
+ u32 val, reg;
+
+ reg = (channel == 0) ? XPCS_MII_AN_INTR_STS :
+ XPCS_CHANNEL_MII_AN_INTR_STS(channel);
- val = ipq_unipcs_reg_read32(qunipcs, XPCS_MII_AN_INTR_STS);
+ val = ipq_unipcs_reg_read32(qunipcs, reg);
state->link = !!(val & XPCS_USXG_AN_LINK_STS);
@@ -415,6 +457,15 @@ static int ipq_unipcs_config_mode(struct ipq_uniphy_pcs *qunipcs,
PCS_MODE_SEL_MASK,
PCS_MODE_XPCS);
break;
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ rate = 312500000;
+ ipq_unipcs_reg_modify32(qunipcs, PCS_MODE_CTRL,
+ PCS_MODE_SEL_MASK,
+ PCS_MODE_XPCS);
+ ipq_unipcs_reg_modify32(qunipcs, PCS_QP_USXG_OPTION,
+ PCS_QP_USXG_GMII_SRC_XPCS,
+ PCS_QP_USXG_GMII_SRC_XPCS);
+ break;
default:
dev_err(qunipcs->dev,
"interface %s not supported\n", phy_modes(interface));
@@ -502,35 +553,82 @@ static int ipq_unipcs_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
}
static int ipq_unipcs_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
+ int channel,
unsigned int neg_mode,
phy_interface_t interface)
{
int ret;
+ u32 reg;
+
+ /* Only in-band autoneg mode is supported currently */
+ if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED)
+ return -EOPNOTSUPP;
+
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ mutex_lock(&qunipcs->shared_lock);
if (qunipcs->interface != interface) {
ret = ipq_unipcs_config_mode(qunipcs, interface);
if (ret)
- return ret;
+ goto err;
- /* Deassert XPCS and configure XPCS USXGMII */
+ /* Deassert XPCS and configure XPCS USXGMII or 10G_QXGMII */
reset_control_deassert(qunipcs->reset[XPCS_RESET]);
ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
XPCS_USXG_EN, XPCS_USXG_EN);
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
- ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_AN_CTRL,
- XPCS_MII_AN_8BIT,
- XPCS_MII_AN_8BIT);
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ ipq_unipcs_reg_modify32(qunipcs, XPCS_KR_CTRL,
+ XPCS_USXG_MODE_MASK,
+ XPCS_10G_QXGMII_MODE);
+
+ /* Set Alignment Marker Interval */
+ ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_STS,
+ XPCS_DIG_STS_AM_COUNT,
+ 0x6018);
- ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
- XPCS_MII_AN_EN, XPCS_MII_AN_EN);
+ ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
+ XPCS_SOFT_RESET,
+ XPCS_SOFT_RESET);
}
qunipcs->interface = interface;
}
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ mutex_unlock(&qunipcs->shared_lock);
+
+ /* Disable Tx IPG check for 10G_QXGMII */
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII) {
+ reg = (channel == 0) ? XPCS_XAUI_MODE_CTRL :
+ XPCS_CHANNEL_XAUI_MODE_CTRL(channel);
+
+ ipq_unipcs_reg_modify32(qunipcs, reg,
+ XPCS_TX_IPG_CHECK_DIS,
+ XPCS_TX_IPG_CHECK_DIS);
+ }
+
+ /* Enable autoneg */
+ reg = (channel == 0) ? XPCS_MII_AN_CTRL :
+ XPCS_CHANNEL_MII_AN_CTRL(channel);
+
+ ipq_unipcs_reg_modify32(qunipcs, reg,
+ XPCS_MII_AN_8BIT, XPCS_MII_AN_8BIT);
+
+ reg = (channel == 0) ? XPCS_MII_CTRL :
+ XPCS_CHANNEL_MII_CTRL(channel);
+
+ ipq_unipcs_reg_modify32(qunipcs, reg,
+ XPCS_MII_AN_EN, XPCS_MII_AN_EN);
+
return 0;
+
+err:
+ if (interface == PHY_INTERFACE_MODE_10G_QXGMII)
+ mutex_unlock(&qunipcs->shared_lock);
+
+ return ret;
}
static int ipq_unipcs_config_10gbaser(struct ipq_uniphy_pcs *qunipcs,
@@ -638,6 +736,7 @@ ipq_unipcs_link_up_clock_rate_set(struct ipq_uniphy_pcs_ch *qunipcs_ch,
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:
@@ -713,9 +812,10 @@ static void ipq_unipcs_link_up_config_2500basex(struct ipq_uniphy_pcs *qunipcs,
}
static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
+ int channel,
int speed)
{
- u32 val;
+ u32 val, reg;
switch (speed) {
case SPEED_10000:
@@ -744,14 +844,20 @@ static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs,
val |= XPCS_DUPLEX_FULL;
/* Config XPCS speed */
- ipq_unipcs_reg_modify32(qunipcs, XPCS_MII_CTRL,
+ reg = (channel == 0) ? XPCS_MII_CTRL : XPCS_CHANNEL_MII_CTRL(channel);
+ ipq_unipcs_reg_modify32(qunipcs, reg,
XPCS_SPEED_MASK | XPCS_DUPLEX_FULL,
val);
/* XPCS adapter reset */
- ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
- XPCS_USXG_ADPT_RESET,
- XPCS_USXG_ADPT_RESET);
+ if (channel == 0)
+ ipq_unipcs_reg_modify32(qunipcs, XPCS_DIG_CTRL,
+ XPCS_USXG_ADPT_RESET,
+ XPCS_USXG_ADPT_RESET);
+ else
+ ipq_unipcs_reg_modify32(qunipcs, XPCS_CHANNEL_DIG_CTRL(channel),
+ XPCS_CHANNEL_USXG_ADPT_RESET,
+ XPCS_CHANNEL_USXG_ADPT_RESET);
}
static int ipq_unipcs_validate(struct phylink_pcs *pcs,
@@ -786,7 +892,8 @@ static void ipq_unipcs_get_state(struct phylink_pcs *pcs,
ipq_unipcs_get_state_2500basex(qunipcs, channel, state);
break;
case PHY_INTERFACE_MODE_USXGMII:
- ipq_unipcs_get_state_usxgmii(qunipcs, state);
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ ipq_unipcs_get_state_usxgmii(qunipcs, channel, state);
break;
case PHY_INTERFACE_MODE_10GBASER:
ipq_unipcs_get_state_10gbaser(qunipcs, state);
@@ -823,7 +930,8 @@ static int ipq_unipcs_config(struct phylink_pcs *pcs,
case PHY_INTERFACE_MODE_2500BASEX:
return ipq_unipcs_config_2500basex(qunipcs, interface);
case PHY_INTERFACE_MODE_USXGMII:
- return ipq_unipcs_config_usxgmii(qunipcs,
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ return ipq_unipcs_config_usxgmii(qunipcs, channel,
neg_mode, interface);
case PHY_INTERFACE_MODE_10GBASER:
return ipq_unipcs_config_10gbaser(qunipcs, interface);
@@ -865,7 +973,8 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs,
channel, speed);
break;
case PHY_INTERFACE_MODE_USXGMII:
- ipq_unipcs_link_up_config_usxgmii(qunipcs, speed);
+ case PHY_INTERFACE_MODE_10G_QXGMII:
+ ipq_unipcs_link_up_config_usxgmii(qunipcs, channel, speed);
break;
case PHY_INTERFACE_MODE_10GBASER:
break;
@@ -1082,6 +1191,7 @@ static int ipq_uniphy_probe(struct platform_device *pdev)
return ret;
mutex_init(&priv->shared_lock);
+ spin_lock_init(&priv->reg_lock);
platform_set_drvdata(pdev, priv);
--
2.45.2