Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: pcs: xpcs: support to switch mode for Wangxun NICs

According to chapter 6 of DesignWare Cores Ethernet PCS (version 3.20a)
and custom design manual, add a configuration flow for switching interface
mode.

If the interface changes, the following setting is required:
1. wait VR_XS_PCS_DIG_STS bit(4, 2) [PSEQ_STATE] = 100b (Power-Good)
2. write SR_XS_PCS_CTRL2 to select various PCS type
3. write SR_PMA_CTRL1 and/or SR_XS_PCS_CTRL1 for link speed
4. program PMA registers
5. write VR_XS_PCS_DIG_CTRL1 bit(15) [VR_RST] = 1b (Vendor-Specific
Soft Reset)
6. wait for VR_XS_PCS_DIG_CTRL1 bit(15) [VR_RST] to get cleared

Only 10GBASE-R/SGMII/1000BASE-X modes are planned for the current Wangxun
devices. And there is a quirk for Wangxun devices to switch mode although
the interface in phylink state has not changed, since PCS will change to
default 10GBASE-R when the ethernet driver(txgbe) do LAN reset.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jiawen Wu and committed by
David S. Miller
f629acc6 d55595f0

+227 -6
+1
MAINTAINERS
··· 22925 22925 W: https://www.net-swift.com 22926 22926 F: Documentation/networking/device_drivers/ethernet/wangxun/* 22927 22927 F: drivers/net/ethernet/wangxun/ 22928 + F: drivers/net/pcs/pcs-xpcs-wx.c 22928 22929 22929 22930 WATCHDOG DEVICE DRIVERS 22930 22931 M: Wim Van Sebroeck <wim@linux-watchdog.org>
+1 -1
drivers/net/pcs/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 # Makefile for Linux PCS drivers 3 3 4 - pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o 4 + pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs.o pcs-xpcs-nxp.o pcs-xpcs-wx.o 5 5 6 6 obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o 7 7 obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
+208
drivers/net/pcs/pcs-xpcs-wx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */ 3 + 4 + #include <linux/pcs/pcs-xpcs.h> 5 + #include <linux/mdio.h> 6 + #include "pcs-xpcs.h" 7 + 8 + /* VR_XS_PMA_MMD */ 9 + #define TXGBE_PMA_MMD 0x8020 10 + #define TXGBE_TX_GENCTL1 0x11 11 + #define TXGBE_TX_GENCTL1_VBOOST_LVL GENMASK(10, 8) 12 + #define TXGBE_TX_GENCTL1_VBOOST_EN0 BIT(4) 13 + #define TXGBE_TX_GEN_CTL2 0x12 14 + #define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v) 15 + #define TXGBE_TX_RATE_CTL 0x14 16 + #define TXGBE_TX_RATE_CTL_TX0_RATE(v) FIELD_PREP(GENMASK(2, 0), v) 17 + #define TXGBE_RX_GEN_CTL2 0x32 18 + #define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v) 19 + #define TXGBE_RX_GEN_CTL3 0x33 20 + #define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0 GENMASK(2, 0) 21 + #define TXGBE_RX_RATE_CTL 0x34 22 + #define TXGBE_RX_RATE_CTL_RX0_RATE(v) FIELD_PREP(GENMASK(1, 0), v) 23 + #define TXGBE_RX_EQ_ATTN_CTL 0x37 24 + #define TXGBE_RX_EQ_ATTN_LVL0 GENMASK(2, 0) 25 + #define TXGBE_RX_EQ_CTL0 0x38 26 + #define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v) FIELD_PREP(GENMASK(15, 12), v) 27 + #define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v) FIELD_PREP(GENMASK(11, 8), v) 28 + #define TXGBE_RX_EQ_CTL0_CTLE_POLE(v) FIELD_PREP(GENMASK(7, 5), v) 29 + #define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v) FIELD_PREP(GENMASK(4, 0), v) 30 + #define TXGBE_RX_EQ_CTL4 0x3C 31 + #define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0 BIT(4) 32 + #define TXGBE_RX_EQ_CTL4_CONT_ADAPT0 BIT(0) 33 + #define TXGBE_AFE_DFE_ENABLE 0x3D 34 + #define TXGBE_DFE_EN_0 BIT(4) 35 + #define TXGBE_AFE_EN_0 BIT(0) 36 + #define TXGBE_DFE_TAP_CTL0 0x3E 37 + #define TXGBE_MPLLA_CTL0 0x51 38 + #define TXGBE_MPLLA_CTL2 0x53 39 + #define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN BIT(10) 40 + #define TXGBE_MPLLA_CTL2_DIV10_CLK_EN BIT(9) 41 + #define TXGBE_MPLLA_CTL3 0x57 42 + #define TXGBE_MISC_CTL0 0x70 43 + #define TXGBE_MISC_CTL0_PLL BIT(15) 44 + #define TXGBE_MISC_CTL0_CR_PARA_SEL BIT(14) 45 + #define TXGBE_MISC_CTL0_RX_VREF(v) FIELD_PREP(GENMASK(12, 8), v) 46 + #define TXGBE_VCO_CAL_LD0 0x72 47 + #define TXGBE_VCO_CAL_REF0 0x76 48 + 49 + static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg) 50 + { 51 + return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg); 52 + } 53 + 54 + static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val) 55 + { 56 + return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val); 57 + } 58 + 59 + static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs) 60 + { 61 + int val; 62 + 63 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21); 64 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0); 65 + val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1); 66 + val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL); 67 + txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val); 68 + txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL | 69 + TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF)); 70 + txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549); 71 + txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29); 72 + txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0); 73 + txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0); 74 + txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3)); 75 + txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3)); 76 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN | 77 + TXGBE_MPLLA_CTL2_DIV10_CLK_EN); 78 + 79 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) | 80 + TXGBE_RX_EQ_CTL0_CTLE_BOOST(5)); 81 + val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL); 82 + val &= ~TXGBE_RX_EQ_ATTN_LVL0; 83 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); 84 + txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE); 85 + val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE); 86 + val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0); 87 + txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val); 88 + val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4); 89 + val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0; 90 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val); 91 + } 92 + 93 + static void txgbe_pma_config_1g(struct dw_xpcs *xpcs) 94 + { 95 + int val; 96 + 97 + val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1); 98 + val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL); 99 + val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0; 100 + txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val); 101 + txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL | 102 + TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF)); 103 + 104 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) | 105 + TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6)); 106 + val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL); 107 + val &= ~TXGBE_RX_EQ_ATTN_LVL0; 108 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); 109 + txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0); 110 + val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3); 111 + val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0); 112 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val); 113 + 114 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20); 115 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46); 116 + txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540); 117 + txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A); 118 + txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0); 119 + txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0); 120 + txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3)); 121 + txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3)); 122 + txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1)); 123 + txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1)); 124 + txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN); 125 + } 126 + 127 + static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs) 128 + { 129 + int val, ret; 130 + 131 + /* Wait xpcs power-up good */ 132 + ret = read_poll_timeout(xpcs_read_vpcs, val, 133 + (val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD, 134 + 10000, 1000000, false, 135 + xpcs, DW_VR_XS_PCS_DIG_STS); 136 + if (ret < 0) 137 + dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n"); 138 + 139 + return ret; 140 + } 141 + 142 + static int txgbe_pma_init_done(struct dw_xpcs *xpcs) 143 + { 144 + int val, ret; 145 + 146 + xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1); 147 + 148 + /* wait pma initialization done */ 149 + ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST), 150 + 100000, 10000000, false, 151 + xpcs, DW_VR_XS_PCS_DIG_CTRL1); 152 + if (ret < 0) 153 + dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n"); 154 + 155 + return ret; 156 + } 157 + 158 + static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs) 159 + { 160 + int ret; 161 + 162 + /* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */ 163 + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2); 164 + ret &= MDIO_PCS_CTRL2_TYPE; 165 + if (ret == MDIO_PCS_CTRL2_10GBR && 166 + xpcs->interface != PHY_INTERFACE_MODE_10GBASER) 167 + return true; 168 + 169 + return false; 170 + } 171 + 172 + int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface) 173 + { 174 + int val, ret; 175 + 176 + switch (interface) { 177 + case PHY_INTERFACE_MODE_10GBASER: 178 + case PHY_INTERFACE_MODE_SGMII: 179 + case PHY_INTERFACE_MODE_1000BASEX: 180 + break; 181 + default: 182 + return 0; 183 + } 184 + 185 + if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs)) 186 + return 0; 187 + 188 + xpcs->interface = interface; 189 + 190 + ret = txgbe_pcs_poll_power_up(xpcs); 191 + if (ret < 0) 192 + return ret; 193 + 194 + if (interface == PHY_INTERFACE_MODE_10GBASER) { 195 + xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR); 196 + val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1); 197 + val |= MDIO_CTRL1_SPEED10G; 198 + xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val); 199 + txgbe_pma_config_10gbaser(xpcs); 200 + } else { 201 + xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX); 202 + xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0); 203 + xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0); 204 + txgbe_pma_config_1g(xpcs); 205 + } 206 + 207 + return txgbe_pma_init_done(xpcs); 208 + }
+8 -5
drivers/net/pcs/pcs-xpcs.c
··· 228 228 return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); 229 229 } 230 230 231 - static int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) 231 + int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg) 232 232 { 233 233 return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); 234 234 } 235 235 236 - static int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) 236 + int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val) 237 237 { 238 238 return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); 239 239 } ··· 841 841 if (!compat) 842 842 return -ENODEV; 843 843 844 + if (xpcs->dev_flag == DW_DEV_TXGBE) { 845 + ret = txgbe_xpcs_switch_mode(xpcs, interface); 846 + if (ret) 847 + return ret; 848 + } 849 + 844 850 switch (compat->an_mode) { 845 851 case DW_10GBASER: 846 852 break; ··· 1319 1313 1320 1314 xpcs->pcs.ops = &xpcs_phylink_ops; 1321 1315 xpcs->pcs.neg_mode = true; 1322 - if (compat->an_mode == DW_10GBASER) 1323 - return xpcs; 1324 - 1325 1316 xpcs->pcs.poll = true; 1326 1317 1327 1318 if (xpcs->dev_flag != DW_DEV_TXGBE) {
+8
drivers/net/pcs/pcs-xpcs.h
··· 15 15 /* VR_XS_PCS */ 16 16 #define DW_USXGMII_RST BIT(10) 17 17 #define DW_USXGMII_EN BIT(9) 18 + #define DW_VR_XS_PCS_DIG_CTRL1 0x0000 19 + #define DW_VR_RST BIT(15) 20 + #define DW_EN_VSMMD1 BIT(13) 18 21 #define DW_VR_XS_PCS_DIG_STS 0x0010 19 22 #define DW_RXFIFO_ERR GENMASK(6, 5) 23 + #define DW_PSEQ_ST GENMASK(4, 2) 24 + #define DW_PSEQ_ST_GOOD FIELD_PREP(GENMASK(4, 2), 0x4) 20 25 21 26 /* SR_MII */ 22 27 #define DW_USXGMII_FULL BIT(8) ··· 111 106 112 107 int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg); 113 108 int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val); 109 + int xpcs_read_vpcs(struct dw_xpcs *xpcs, int reg); 110 + int xpcs_write_vpcs(struct dw_xpcs *xpcs, int reg, u16 val); 114 111 int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs); 115 112 int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs); 116 113 int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs); 114 + int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
+1
include/linux/pcs/pcs-xpcs.h
··· 32 32 struct mdio_device *mdiodev; 33 33 const struct xpcs_id *id; 34 34 struct phylink_pcs pcs; 35 + phy_interface_t interface; 35 36 int dev_flag; 36 37 }; 37 38