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

net: pcs: xpcs: add CL37 1000BASE-X AN support

For CL37 1000BASE-X AN, DW xPCS does not support C22 method but offers
C45 vendor-specific MII MMD for programming.

We also add the ability to disable Autoneg (through ethtool for certain
network switch that supports 1000BASE-X (1000Mbps and Full-Duplex) but
not Autoneg capability.

v4: Fixes to comment from Russell King. Thanks!
https://patchwork.kernel.org/comment/24894239/
Make xpcs_modify_changed() as private, change to use
mdiodev_modify_changed() for cleaner code.

v3: Fixes to issues spotted by Russell King. Thanks!
https://patchwork.kernel.org/comment/24890210/
Use phylink_mii_c22_pcs_decode_state(), remove unnecessary
interrupt clearing and skip speed & duplex setting if AN
is enabled.

v2: Fixes to issues spotted by Russell King in v1. Thanks!
https://patchwork.kernel.org/comment/24826650/
Use phylink_mii_c22_pcs_encode_advertisement() and implement
C45 MII ADV handling since IP only support C45 access.

Tested-by: Emilio Riva <emilio.riva@ericsson.com>
Signed-off-by: Ong Boon Leong <boon.leong.ong@intel.com>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ong Boon Leong and committed by
David S. Miller
b47aec88 c8238631

+171 -1
+170
drivers/net/pcs/pcs-xpcs.c
··· 77 77 __ETHTOOL_LINK_MODE_MASK_NBITS, 78 78 }; 79 79 80 + static const int xpcs_1000basex_features[] = { 81 + ETHTOOL_LINK_MODE_Pause_BIT, 82 + ETHTOOL_LINK_MODE_Asym_Pause_BIT, 83 + ETHTOOL_LINK_MODE_Autoneg_BIT, 84 + ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 85 + __ETHTOOL_LINK_MODE_MASK_NBITS, 86 + }; 87 + 80 88 static const int xpcs_2500basex_features[] = { 81 89 ETHTOOL_LINK_MODE_Pause_BIT, 82 90 ETHTOOL_LINK_MODE_Asym_Pause_BIT, ··· 110 102 PHY_INTERFACE_MODE_SGMII, 111 103 }; 112 104 105 + static const phy_interface_t xpcs_1000basex_interfaces[] = { 106 + PHY_INTERFACE_MODE_1000BASEX, 107 + }; 108 + 113 109 static const phy_interface_t xpcs_2500basex_interfaces[] = { 114 110 PHY_INTERFACE_MODE_2500BASEX, 115 111 PHY_INTERFACE_MODE_MAX, ··· 124 112 DW_XPCS_10GKR, 125 113 DW_XPCS_XLGMII, 126 114 DW_XPCS_SGMII, 115 + DW_XPCS_1000BASEX, 127 116 DW_XPCS_2500BASEX, 128 117 DW_XPCS_INTERFACE_MAX, 129 118 }; ··· 202 189 return mdiobus_c45_write(bus, addr, dev, reg, val); 203 190 } 204 191 192 + static int xpcs_modify_changed(struct dw_xpcs *xpcs, int dev, u32 reg, 193 + u16 mask, u16 set) 194 + { 195 + u32 reg_addr = mdiobus_c45_addr(dev, reg); 196 + 197 + return mdiodev_modify_changed(xpcs->mdiodev, reg_addr, mask, set); 198 + } 199 + 205 200 static int xpcs_read_vendor(struct dw_xpcs *xpcs, int dev, u32 reg) 206 201 { 207 202 return xpcs_read(xpcs, dev, DW_VENDOR | reg); ··· 258 237 break; 259 238 case DW_AN_C37_SGMII: 260 239 case DW_2500BASEX: 240 + case DW_AN_C37_1000BASEX: 261 241 dev = MDIO_MMD_VEND2; 262 242 break; 263 243 default: ··· 794 772 return ret; 795 773 } 796 774 775 + static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, unsigned int mode, 776 + const unsigned long *advertising) 777 + { 778 + phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX; 779 + int ret, mdio_ctrl, adv; 780 + bool changed = 0; 781 + 782 + /* According to Chap 7.12, to set 1000BASE-X C37 AN, AN must 783 + * be disabled first:- 784 + * 1) VR_MII_MMD_CTRL Bit(12)[AN_ENABLE] = 0b 785 + * 2) VR_MII_AN_CTRL Bit(2:1)[PCS_MODE] = 00b (1000BASE-X C37) 786 + */ 787 + mdio_ctrl = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL); 788 + if (mdio_ctrl < 0) 789 + return mdio_ctrl; 790 + 791 + if (mdio_ctrl & AN_CL37_EN) { 792 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 793 + mdio_ctrl & ~AN_CL37_EN); 794 + if (ret < 0) 795 + return ret; 796 + } 797 + 798 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL); 799 + if (ret < 0) 800 + return ret; 801 + 802 + ret &= ~DW_VR_MII_PCS_MODE_MASK; 803 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_CTRL, ret); 804 + if (ret < 0) 805 + return ret; 806 + 807 + /* Check for advertising changes and update the C45 MII ADV 808 + * register accordingly. 809 + */ 810 + adv = phylink_mii_c22_pcs_encode_advertisement(interface, 811 + advertising); 812 + if (adv >= 0) { 813 + ret = xpcs_modify_changed(xpcs, MDIO_MMD_VEND2, 814 + MII_ADVERTISE, 0xffff, adv); 815 + if (ret < 0) 816 + return ret; 817 + 818 + changed = ret; 819 + } 820 + 821 + /* Clear CL37 AN complete status */ 822 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_AN_INTR_STS, 0); 823 + if (ret < 0) 824 + return ret; 825 + 826 + if (phylink_autoneg_inband(mode) && 827 + linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) { 828 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL, 829 + mdio_ctrl | AN_CL37_EN); 830 + if (ret < 0) 831 + return ret; 832 + } 833 + 834 + return changed; 835 + } 836 + 797 837 static int xpcs_config_2500basex(struct dw_xpcs *xpcs) 798 838 { 799 839 int ret; ··· 898 814 break; 899 815 case DW_AN_C37_SGMII: 900 816 ret = xpcs_config_aneg_c37_sgmii(xpcs, mode); 817 + if (ret) 818 + return ret; 819 + break; 820 + case DW_AN_C37_1000BASEX: 821 + ret = xpcs_config_aneg_c37_1000basex(xpcs, mode, 822 + advertising); 901 823 if (ret) 902 824 return ret; 903 825 break; ··· 1011 921 return 0; 1012 922 } 1013 923 924 + static int xpcs_get_state_c37_1000basex(struct dw_xpcs *xpcs, 925 + struct phylink_link_state *state) 926 + { 927 + int lpa, bmsr; 928 + 929 + if (state->an_enabled) { 930 + /* Reset link state */ 931 + state->link = false; 932 + 933 + lpa = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_LPA); 934 + if (lpa < 0 || lpa & LPA_RFAULT) 935 + return lpa; 936 + 937 + bmsr = xpcs_read(xpcs, MDIO_MMD_VEND2, MII_BMSR); 938 + if (bmsr < 0) 939 + return bmsr; 940 + 941 + phylink_mii_c22_pcs_decode_state(state, bmsr, lpa); 942 + } 943 + 944 + return 0; 945 + } 946 + 1014 947 static void xpcs_get_state(struct phylink_pcs *pcs, 1015 948 struct phylink_link_state *state) 1016 949 { ··· 1058 945 ret = xpcs_get_state_c37_sgmii(xpcs, state); 1059 946 if (ret) { 1060 947 pr_err("xpcs_get_state_c37_sgmii returned %pe\n", 948 + ERR_PTR(ret)); 949 + } 950 + break; 951 + case DW_AN_C37_1000BASEX: 952 + ret = xpcs_get_state_c37_1000basex(xpcs, state); 953 + if (ret) { 954 + pr_err("xpcs_get_state_c37_1000basex returned %pe\n", 1061 955 ERR_PTR(ret)); 1062 956 } 1063 957 break; ··· 1103 983 pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 1104 984 } 1105 985 986 + static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int mode, 987 + int speed, int duplex) 988 + { 989 + int val, ret; 990 + 991 + if (phylink_autoneg_inband(mode)) 992 + return; 993 + 994 + switch (speed) { 995 + case SPEED_1000: 996 + val = BMCR_SPEED1000; 997 + break; 998 + case SPEED_100: 999 + case SPEED_10: 1000 + default: 1001 + pr_err("%s: speed = %d\n", __func__, speed); 1002 + return; 1003 + } 1004 + 1005 + if (duplex == DUPLEX_FULL) 1006 + val |= BMCR_FULLDPLX; 1007 + else 1008 + pr_err("%s: half duplex not supported\n", __func__); 1009 + 1010 + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val); 1011 + if (ret) 1012 + pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret)); 1013 + } 1014 + 1106 1015 void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode, 1107 1016 phy_interface_t interface, int speed, int duplex) 1108 1017 { ··· 1141 992 return xpcs_config_usxgmii(xpcs, speed); 1142 993 if (interface == PHY_INTERFACE_MODE_SGMII) 1143 994 return xpcs_link_up_sgmii(xpcs, mode, speed, duplex); 995 + if (interface == PHY_INTERFACE_MODE_1000BASEX) 996 + return xpcs_link_up_1000basex(xpcs, mode, speed, duplex); 1144 997 } 1145 998 EXPORT_SYMBOL_GPL(xpcs_link_up); 999 + 1000 + static void xpcs_an_restart(struct phylink_pcs *pcs) 1001 + { 1002 + struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs); 1003 + int ret; 1004 + 1005 + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); 1006 + if (ret >= 0) { 1007 + ret |= BMCR_ANRESTART; 1008 + xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); 1009 + } 1010 + } 1146 1011 1147 1012 static u32 xpcs_get_id(struct dw_xpcs *xpcs) 1148 1013 { ··· 1223 1060 .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), 1224 1061 .an_mode = DW_AN_C37_SGMII, 1225 1062 }, 1063 + [DW_XPCS_1000BASEX] = { 1064 + .supported = xpcs_1000basex_features, 1065 + .interface = xpcs_1000basex_interfaces, 1066 + .num_interfaces = ARRAY_SIZE(xpcs_1000basex_interfaces), 1067 + .an_mode = DW_AN_C37_1000BASEX, 1068 + }, 1226 1069 [DW_XPCS_2500BASEX] = { 1227 1070 .supported = xpcs_2500basex_features, 1228 1071 .interface = xpcs_2500basex_interfaces, ··· 1284 1115 .pcs_validate = xpcs_validate, 1285 1116 .pcs_config = xpcs_config, 1286 1117 .pcs_get_state = xpcs_get_state, 1118 + .pcs_an_restart = xpcs_an_restart, 1287 1119 .pcs_link_up = xpcs_link_up, 1288 1120 }; 1289 1121
-1
drivers/net/pcs/pcs-xpcs.h
··· 109 109 110 110 int xpcs_read(struct dw_xpcs *xpcs, int dev, u32 reg); 111 111 int xpcs_write(struct dw_xpcs *xpcs, int dev, u32 reg, u16 val); 112 - 113 112 int nxp_sja1105_sgmii_pma_config(struct dw_xpcs *xpcs); 114 113 int nxp_sja1110_sgmii_pma_config(struct dw_xpcs *xpcs); 115 114 int nxp_sja1110_2500basex_pma_config(struct dw_xpcs *xpcs);
+1
include/linux/pcs/pcs-xpcs.h
··· 17 17 #define DW_AN_C73 1 18 18 #define DW_AN_C37_SGMII 2 19 19 #define DW_2500BASEX 3 20 + #define DW_AN_C37_1000BASEX 4 20 21 21 22 struct xpcs_id; 22 23