1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3 4 #include <linux/phy.h> 5 #include <linux/phylink.h> 6 7 #include "fbnic.h" 8 #include "fbnic_mac.h" 9 #include "fbnic_netdev.h" 10 11 static phy_interface_t fbnic_phylink_select_interface(u8 aui) 12 { 13 switch (aui) { 14 case FBNIC_AUI_100GAUI2: 15 return PHY_INTERFACE_MODE_100GBASEP; 16 case FBNIC_AUI_50GAUI1: 17 return PHY_INTERFACE_MODE_50GBASER; 18 case FBNIC_AUI_LAUI2: 19 return PHY_INTERFACE_MODE_LAUI; 20 case FBNIC_AUI_25GAUI: 21 return PHY_INTERFACE_MODE_25GBASER; 22 } 23 24 return PHY_INTERFACE_MODE_NA; 25 } 26 27 void fbnic_phylink_get_pauseparam(struct net_device *netdev, 28 struct ethtool_pauseparam *pause) 29 { 30 struct fbnic_net *fbn = netdev_priv(netdev); 31 32 phylink_ethtool_get_pauseparam(fbn->phylink, pause); 33 } 34 35 int fbnic_phylink_set_pauseparam(struct net_device *netdev, 36 struct ethtool_pauseparam *pause) 37 { 38 struct fbnic_net *fbn = netdev_priv(netdev); 39 40 return phylink_ethtool_set_pauseparam(fbn->phylink, pause); 41 } 42 43 static void 44 fbnic_phylink_get_supported_fec_modes(unsigned long *supported) 45 { 46 /* The NIC can support up to 8 possible combinations. 47 * Either 50G-CR, or 100G-CR2 48 * This is with RS FEC mode only 49 * Either 25G-CR, or 50G-CR2 50 * This is with No FEC, RS, or Base-R 51 */ 52 if (phylink_test(supported, 100000baseCR2_Full) || 53 phylink_test(supported, 50000baseCR_Full)) 54 phylink_set(supported, FEC_RS); 55 if (phylink_test(supported, 50000baseCR2_Full) || 56 phylink_test(supported, 25000baseCR_Full)) { 57 phylink_set(supported, FEC_BASER); 58 phylink_set(supported, FEC_NONE); 59 phylink_set(supported, FEC_RS); 60 } 61 } 62 63 int fbnic_phylink_ethtool_ksettings_get(struct net_device *netdev, 64 struct ethtool_link_ksettings *cmd) 65 { 66 struct fbnic_net *fbn = netdev_priv(netdev); 67 int err; 68 69 err = phylink_ethtool_ksettings_get(fbn->phylink, cmd); 70 if (!err) { 71 unsigned long *supp = cmd->link_modes.supported; 72 73 cmd->base.port = PORT_DA; 74 cmd->lanes = (fbn->aui & FBNIC_AUI_MODE_R2) ? 2 : 1; 75 76 fbnic_phylink_get_supported_fec_modes(supp); 77 } 78 79 return err; 80 } 81 82 int fbnic_phylink_get_fecparam(struct net_device *netdev, 83 struct ethtool_fecparam *fecparam) 84 { 85 struct fbnic_net *fbn = netdev_priv(netdev); 86 87 if (fbn->fec & FBNIC_FEC_RS) { 88 fecparam->active_fec = ETHTOOL_FEC_RS; 89 fecparam->fec = ETHTOOL_FEC_RS; 90 } else if (fbn->fec & FBNIC_FEC_BASER) { 91 fecparam->active_fec = ETHTOOL_FEC_BASER; 92 fecparam->fec = ETHTOOL_FEC_BASER; 93 } else { 94 fecparam->active_fec = ETHTOOL_FEC_OFF; 95 fecparam->fec = ETHTOOL_FEC_OFF; 96 } 97 98 if (fbn->aui & FBNIC_AUI_MODE_PAM4) 99 fecparam->fec |= ETHTOOL_FEC_AUTO; 100 101 return 0; 102 } 103 104 static struct fbnic_net * 105 fbnic_pcs_to_net(struct phylink_pcs *pcs) 106 { 107 return container_of(pcs, struct fbnic_net, phylink_pcs); 108 } 109 110 static void 111 fbnic_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, 112 struct phylink_link_state *state) 113 { 114 struct fbnic_net *fbn = fbnic_pcs_to_net(pcs); 115 struct fbnic_dev *fbd = fbn->fbd; 116 117 switch (fbn->aui) { 118 case FBNIC_AUI_25GAUI: 119 state->speed = SPEED_25000; 120 break; 121 case FBNIC_AUI_LAUI2: 122 case FBNIC_AUI_50GAUI1: 123 state->speed = SPEED_50000; 124 break; 125 case FBNIC_AUI_100GAUI2: 126 state->speed = SPEED_100000; 127 break; 128 default: 129 state->link = 0; 130 return; 131 } 132 133 state->duplex = DUPLEX_FULL; 134 135 state->link = fbd->mac->pcs_get_link(fbd); 136 } 137 138 static int 139 fbnic_phylink_pcs_enable(struct phylink_pcs *pcs) 140 { 141 struct fbnic_net *fbn = fbnic_pcs_to_net(pcs); 142 struct fbnic_dev *fbd = fbn->fbd; 143 144 return fbd->mac->pcs_enable(fbd); 145 } 146 147 static void 148 fbnic_phylink_pcs_disable(struct phylink_pcs *pcs) 149 { 150 struct fbnic_net *fbn = fbnic_pcs_to_net(pcs); 151 struct fbnic_dev *fbd = fbn->fbd; 152 153 return fbd->mac->pcs_disable(fbd); 154 } 155 156 static int 157 fbnic_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 158 phy_interface_t interface, 159 const unsigned long *advertising, 160 bool permit_pause_to_mac) 161 { 162 return 0; 163 } 164 165 static const struct phylink_pcs_ops fbnic_phylink_pcs_ops = { 166 .pcs_config = fbnic_phylink_pcs_config, 167 .pcs_enable = fbnic_phylink_pcs_enable, 168 .pcs_disable = fbnic_phylink_pcs_disable, 169 .pcs_get_state = fbnic_phylink_pcs_get_state, 170 }; 171 172 static struct phylink_pcs * 173 fbnic_phylink_mac_select_pcs(struct phylink_config *config, 174 phy_interface_t interface) 175 { 176 struct net_device *netdev = to_net_dev(config->dev); 177 struct fbnic_net *fbn = netdev_priv(netdev); 178 179 return &fbn->phylink_pcs; 180 } 181 182 static void 183 fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode, 184 const struct phylink_link_state *state) 185 { 186 } 187 188 static void 189 fbnic_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, 190 phy_interface_t interface) 191 { 192 struct net_device *netdev = to_net_dev(config->dev); 193 struct fbnic_net *fbn = netdev_priv(netdev); 194 struct fbnic_dev *fbd = fbn->fbd; 195 196 fbd->mac->link_down(fbd); 197 198 fbn->link_down_events++; 199 } 200 201 static void 202 fbnic_phylink_mac_link_up(struct phylink_config *config, 203 struct phy_device *phy, unsigned int mode, 204 phy_interface_t interface, int speed, int duplex, 205 bool tx_pause, bool rx_pause) 206 { 207 struct net_device *netdev = to_net_dev(config->dev); 208 struct fbnic_net *fbn = netdev_priv(netdev); 209 struct fbnic_dev *fbd = fbn->fbd; 210 211 fbd->mac->link_up(fbd, tx_pause, rx_pause); 212 } 213 214 static const struct phylink_mac_ops fbnic_phylink_mac_ops = { 215 .mac_select_pcs = fbnic_phylink_mac_select_pcs, 216 .mac_config = fbnic_phylink_mac_config, 217 .mac_link_down = fbnic_phylink_mac_link_down, 218 .mac_link_up = fbnic_phylink_mac_link_up, 219 }; 220 221 int fbnic_phylink_init(struct net_device *netdev) 222 { 223 struct fbnic_net *fbn = netdev_priv(netdev); 224 struct fbnic_dev *fbd = fbn->fbd; 225 struct phylink *phylink; 226 227 fbn->phylink_pcs.ops = &fbnic_phylink_pcs_ops; 228 229 fbn->phylink_config.dev = &netdev->dev; 230 fbn->phylink_config.type = PHYLINK_NETDEV; 231 fbn->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | 232 MAC_25000FD | MAC_50000FD | 233 MAC_100000FD; 234 fbn->phylink_config.default_an_inband = true; 235 236 __set_bit(PHY_INTERFACE_MODE_100GBASEP, 237 fbn->phylink_config.supported_interfaces); 238 __set_bit(PHY_INTERFACE_MODE_50GBASER, 239 fbn->phylink_config.supported_interfaces); 240 __set_bit(PHY_INTERFACE_MODE_LAUI, 241 fbn->phylink_config.supported_interfaces); 242 __set_bit(PHY_INTERFACE_MODE_25GBASER, 243 fbn->phylink_config.supported_interfaces); 244 245 fbnic_mac_get_fw_settings(fbd, &fbn->aui, &fbn->fec); 246 247 phylink = phylink_create(&fbn->phylink_config, NULL, 248 fbnic_phylink_select_interface(fbn->aui), 249 &fbnic_phylink_mac_ops); 250 if (IS_ERR(phylink)) 251 return PTR_ERR(phylink); 252 253 fbn->phylink = phylink; 254 255 return 0; 256 } 257