From fcd1c53b460aa39cfd15f842126af62b27a4fad5 Mon Sep 17 00:00:00 2001 From: Lei Wei Date: Tue, 2 Apr 2024 18:28:42 +0800 Subject: [PATCH 13/50] net: pcs: Add 2500BASEX interface mode support to IPQ UNIPHY PCS driver 2500BASEX mode is used when PCS connects with QCA8386 switch in a fixed 2500M link. It is also used when PCS connectes with QCA8081 PHY which works at 2500M link speed. In addition, it can be also used when PCS connects with a 2.5G SFP module. Change-Id: I3fe61113c1b3685debc20659736a9488216a029d Signed-off-by: Lei Wei --- drivers/net/pcs/pcs-qcom-ipq-uniphy.c | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c index 68a1715531ef..ed9c55a6c0fa 100644 --- a/drivers/net/pcs/pcs-qcom-ipq-uniphy.c +++ b/drivers/net/pcs/pcs-qcom-ipq-uniphy.c @@ -25,6 +25,7 @@ #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_XPCS FIELD_PREP(PCS_MODE_SEL_MASK, 0x10) #define PCS_MODE_AN_MODE BIT(0) @@ -282,6 +283,24 @@ static void ipq_unipcs_get_state_sgmii(struct ipq_uniphy_pcs *qunipcs, state->pause |= MLO_PAUSE_RX; } +static void ipq_unipcs_get_state_2500basex(struct ipq_uniphy_pcs *qunipcs, + int channel, + struct phylink_link_state *state) +{ + u32 val; + + val = ipq_unipcs_reg_read32(qunipcs, PCS_CHANNEL_STS(channel)); + + state->link = !!(val & PCS_CHANNEL_LINK_STS); + + if (!state->link) + return; + + state->speed = SPEED_2500; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TXRX_MASK; +} + static void ipq_unipcs_get_state_usxgmii(struct ipq_uniphy_pcs *qunipcs, struct phylink_link_state *state) { @@ -373,6 +392,12 @@ static int ipq_unipcs_config_mode(struct ipq_uniphy_pcs *qunipcs, PCS_MODE_SEL_MASK | PCS_MODE_AN_MODE, PCS_MODE_PSGMII); break; + case PHY_INTERFACE_MODE_2500BASEX: + rate = 312500000; + ipq_unipcs_reg_modify32(qunipcs, PCS_MODE_CTRL, + PCS_MODE_SEL_MASK, + PCS_MODE_SGMII_PLUS); + break; case PHY_INTERFACE_MODE_USXGMII: case PHY_INTERFACE_MODE_10GBASER: rate = 312500000; @@ -450,6 +475,22 @@ static int ipq_unipcs_config_sgmii(struct ipq_uniphy_pcs *qunipcs, return ret; } +static int ipq_unipcs_config_2500basex(struct ipq_uniphy_pcs *qunipcs, + phy_interface_t interface) +{ + int ret; + + if (qunipcs->interface != interface) { + ret = ipq_unipcs_config_mode(qunipcs, interface); + if (ret) + return ret; + + qunipcs->interface = interface; + } + + return 0; +} + static int ipq_unipcs_config_usxgmii(struct ipq_uniphy_pcs *qunipcs, unsigned int neg_mode, phy_interface_t interface) @@ -522,6 +563,21 @@ static unsigned long ipq_unipcs_clock_rate_get_gmii(int speed) 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; @@ -566,6 +622,9 @@ ipq_unipcs_link_up_clock_rate_set(struct ipq_uniphy_pcs_ch *qunipcs_ch, 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); @@ -627,6 +686,21 @@ static void ipq_unipcs_link_up_config_sgmii(struct ipq_uniphy_pcs *qunipcs, PCS_CHANNEL_ADPT_RESET); } +static void ipq_unipcs_link_up_config_2500basex(struct ipq_uniphy_pcs *qunipcs, + int channel, + int speed) +{ + /* 2500BASEX do not support autoneg and do not need to + * configure PCS speed, only reset PCS adapter here. + */ + ipq_unipcs_reg_modify32(qunipcs, PCS_CHANNEL_CTRL(channel), + PCS_CHANNEL_ADPT_RESET, + 0); + ipq_unipcs_reg_modify32(qunipcs, PCS_CHANNEL_CTRL(channel), + PCS_CHANNEL_ADPT_RESET, + PCS_CHANNEL_ADPT_RESET); +} + static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs, int speed) { @@ -669,6 +743,17 @@ static void ipq_unipcs_link_up_config_usxgmii(struct ipq_uniphy_pcs *qunipcs, XPCS_USXG_ADPT_RESET); } +static int ipq_unipcs_validate(struct phylink_pcs *pcs, + unsigned long *supported, + const struct phylink_link_state *state) +{ + /* In-band autoneg is not supported for 2500BASEX */ + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phylink_clear(supported, Autoneg); + + return 0; +} + static void ipq_unipcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { @@ -682,6 +767,9 @@ static void ipq_unipcs_get_state(struct phylink_pcs *pcs, case PHY_INTERFACE_MODE_PSGMII: ipq_unipcs_get_state_sgmii(qunipcs, channel, state); break; + case PHY_INTERFACE_MODE_2500BASEX: + ipq_unipcs_get_state_2500basex(qunipcs, channel, state); + break; case PHY_INTERFACE_MODE_USXGMII: ipq_unipcs_get_state_usxgmii(qunipcs, state); break; @@ -716,6 +804,8 @@ static int ipq_unipcs_config(struct phylink_pcs *pcs, case PHY_INTERFACE_MODE_PSGMII: return ipq_unipcs_config_sgmii(qunipcs, channel, neg_mode, interface); + case PHY_INTERFACE_MODE_2500BASEX: + return ipq_unipcs_config_2500basex(qunipcs, interface); case PHY_INTERFACE_MODE_USXGMII: return ipq_unipcs_config_usxgmii(qunipcs, neg_mode, interface); @@ -748,6 +838,10 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs, ipq_unipcs_link_up_config_sgmii(qunipcs, channel, neg_mode, speed); break; + case PHY_INTERFACE_MODE_2500BASEX: + ipq_unipcs_link_up_config_2500basex(qunipcs, + channel, speed); + break; case PHY_INTERFACE_MODE_USXGMII: ipq_unipcs_link_up_config_usxgmii(qunipcs, speed); break; @@ -761,6 +855,7 @@ static void ipq_unipcs_link_up(struct phylink_pcs *pcs, } static const struct phylink_pcs_ops ipq_unipcs_phylink_ops = { + .pcs_validate = ipq_unipcs_validate, .pcs_get_state = ipq_unipcs_get_state, .pcs_config = ipq_unipcs_config, .pcs_link_up = ipq_unipcs_link_up, -- 2.45.2