From d96ec0527b0f5618b3a0757b47606705555ee996 Mon Sep 17 00:00:00 2001 From: Lei Wei 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 --- 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