1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3
4 #include <linux/pcs/pcs-xpcs.h>
5 #include <linux/phy.h>
6 #include <linux/phylink.h>
7
8 #include "fbnic.h"
9 #include "fbnic_mac.h"
10 #include "fbnic_netdev.h"
11
fbnic_phylink_select_interface(u8 aui)12 static phy_interface_t fbnic_phylink_select_interface(u8 aui)
13 {
14 switch (aui) {
15 case FBNIC_AUI_100GAUI2:
16 return PHY_INTERFACE_MODE_100GBASEP;
17 case FBNIC_AUI_50GAUI1:
18 return PHY_INTERFACE_MODE_50GBASER;
19 case FBNIC_AUI_LAUI2:
20 return PHY_INTERFACE_MODE_LAUI;
21 case FBNIC_AUI_25GAUI:
22 return PHY_INTERFACE_MODE_25GBASER;
23 }
24
25 return PHY_INTERFACE_MODE_NA;
26 }
27
fbnic_phylink_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)28 void fbnic_phylink_get_pauseparam(struct net_device *netdev,
29 struct ethtool_pauseparam *pause)
30 {
31 struct fbnic_net *fbn = netdev_priv(netdev);
32
33 phylink_ethtool_get_pauseparam(fbn->phylink, pause);
34 }
35
fbnic_phylink_set_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)36 int fbnic_phylink_set_pauseparam(struct net_device *netdev,
37 struct ethtool_pauseparam *pause)
38 {
39 struct fbnic_net *fbn = netdev_priv(netdev);
40
41 return phylink_ethtool_set_pauseparam(fbn->phylink, pause);
42 }
43
44 static void
fbnic_phylink_get_supported_fec_modes(unsigned long * supported)45 fbnic_phylink_get_supported_fec_modes(unsigned long *supported)
46 {
47 /* The NIC can support up to 8 possible combinations.
48 * Either 50G-CR, or 100G-CR2
49 * This is with RS FEC mode only
50 * Either 25G-CR, or 50G-CR2
51 * This is with No FEC, RS, or Base-R
52 */
53 if (phylink_test(supported, 100000baseCR2_Full) ||
54 phylink_test(supported, 50000baseCR_Full))
55 phylink_set(supported, FEC_RS);
56 if (phylink_test(supported, 50000baseCR2_Full) ||
57 phylink_test(supported, 25000baseCR_Full)) {
58 phylink_set(supported, FEC_BASER);
59 phylink_set(supported, FEC_NONE);
60 phylink_set(supported, FEC_RS);
61 }
62 }
63
fbnic_phylink_ethtool_ksettings_get(struct net_device * netdev,struct ethtool_link_ksettings * cmd)64 int fbnic_phylink_ethtool_ksettings_get(struct net_device *netdev,
65 struct ethtool_link_ksettings *cmd)
66 {
67 struct fbnic_net *fbn = netdev_priv(netdev);
68 int err;
69
70 err = phylink_ethtool_ksettings_get(fbn->phylink, cmd);
71 if (!err) {
72 unsigned long *supp = cmd->link_modes.supported;
73
74 cmd->base.port = PORT_DA;
75 cmd->lanes = (fbn->aui & FBNIC_AUI_MODE_R2) ? 2 : 1;
76
77 fbnic_phylink_get_supported_fec_modes(supp);
78 }
79
80 return err;
81 }
82
fbnic_phylink_get_fecparam(struct net_device * netdev,struct ethtool_fecparam * fecparam)83 int fbnic_phylink_get_fecparam(struct net_device *netdev,
84 struct ethtool_fecparam *fecparam)
85 {
86 struct fbnic_net *fbn = netdev_priv(netdev);
87
88 if (fbn->fec & FBNIC_FEC_RS) {
89 fecparam->active_fec = ETHTOOL_FEC_RS;
90 fecparam->fec = ETHTOOL_FEC_RS;
91 } else if (fbn->fec & FBNIC_FEC_BASER) {
92 fecparam->active_fec = ETHTOOL_FEC_BASER;
93 fecparam->fec = ETHTOOL_FEC_BASER;
94 } else {
95 fecparam->active_fec = ETHTOOL_FEC_OFF;
96 fecparam->fec = ETHTOOL_FEC_OFF;
97 }
98
99 if (fbn->aui & FBNIC_AUI_MODE_PAM4)
100 fecparam->fec |= ETHTOOL_FEC_AUTO;
101
102 return 0;
103 }
104
105 static struct phylink_pcs *
fbnic_phylink_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)106 fbnic_phylink_mac_select_pcs(struct phylink_config *config,
107 phy_interface_t interface)
108 {
109 struct net_device *netdev = to_net_dev(config->dev);
110 struct fbnic_net *fbn = netdev_priv(netdev);
111
112 return fbn->pcs;
113 }
114
115 static int
fbnic_phylink_mac_prepare(struct phylink_config * config,unsigned int mode,phy_interface_t iface)116 fbnic_phylink_mac_prepare(struct phylink_config *config, unsigned int mode,
117 phy_interface_t iface)
118 {
119 struct net_device *netdev = to_net_dev(config->dev);
120 struct fbnic_net *fbn = netdev_priv(netdev);
121 struct fbnic_dev *fbd = fbn->fbd;
122
123 fbd->mac->prepare(fbd, fbn->aui, fbn->fec);
124
125 return 0;
126 }
127
128 static void
fbnic_phylink_mac_config(struct phylink_config * config,unsigned int mode,const struct phylink_link_state * state)129 fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
130 const struct phylink_link_state *state)
131 {
132 }
133
134 static int
fbnic_phylink_mac_finish(struct phylink_config * config,unsigned int mode,phy_interface_t iface)135 fbnic_phylink_mac_finish(struct phylink_config *config, unsigned int mode,
136 phy_interface_t iface)
137 {
138 struct net_device *netdev = to_net_dev(config->dev);
139 struct fbnic_net *fbn = netdev_priv(netdev);
140 struct fbnic_dev *fbd = fbn->fbd;
141
142 /* Retest the link state and restart interrupts */
143 fbd->mac->get_link(fbd, fbn->aui, fbn->fec);
144
145 return 0;
146 }
147
148 static void
fbnic_phylink_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)149 fbnic_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
150 phy_interface_t interface)
151 {
152 struct net_device *netdev = to_net_dev(config->dev);
153 struct fbnic_net *fbn = netdev_priv(netdev);
154 struct fbnic_dev *fbd = fbn->fbd;
155
156 fbd->mac->link_down(fbd);
157
158 fbn->link_down_events++;
159 }
160
161 static void
fbnic_phylink_mac_link_up(struct phylink_config * config,struct phy_device * phy,unsigned int mode,phy_interface_t interface,int speed,int duplex,bool tx_pause,bool rx_pause)162 fbnic_phylink_mac_link_up(struct phylink_config *config,
163 struct phy_device *phy, unsigned int mode,
164 phy_interface_t interface, int speed, int duplex,
165 bool tx_pause, bool rx_pause)
166 {
167 struct net_device *netdev = to_net_dev(config->dev);
168 struct fbnic_net *fbn = netdev_priv(netdev);
169 struct fbnic_dev *fbd = fbn->fbd;
170
171 fbn->tx_pause = tx_pause;
172 fbnic_config_drop_mode(fbn, tx_pause);
173
174 fbd->mac->link_up(fbd, tx_pause, rx_pause);
175 }
176
177 static const struct phylink_mac_ops fbnic_phylink_mac_ops = {
178 .mac_select_pcs = fbnic_phylink_mac_select_pcs,
179 .mac_prepare = fbnic_phylink_mac_prepare,
180 .mac_config = fbnic_phylink_mac_config,
181 .mac_finish = fbnic_phylink_mac_finish,
182 .mac_link_down = fbnic_phylink_mac_link_down,
183 .mac_link_up = fbnic_phylink_mac_link_up,
184 };
185
186 /**
187 * fbnic_phylink_create - Phylink device creation
188 * @netdev: Network Device struct to attach phylink device
189 *
190 * Initialize and attach a phylink instance to the device. The phylink
191 * device will make use of the netdev struct to track carrier and will
192 * eventually be used to expose the current state of the MAC and PCS
193 * setup.
194 *
195 * Return: 0 on success, negative on failure
196 **/
fbnic_phylink_create(struct net_device * netdev)197 int fbnic_phylink_create(struct net_device *netdev)
198 {
199 struct fbnic_net *fbn = netdev_priv(netdev);
200 struct fbnic_dev *fbd = fbn->fbd;
201 struct phylink_pcs *pcs;
202 struct phylink *phylink;
203 int err;
204
205 pcs = xpcs_create_pcs_mdiodev(fbd->mdio_bus, 0);
206 if (IS_ERR(pcs)) {
207 err = PTR_ERR(pcs);
208 dev_err(fbd->dev, "Failed to create PCS device: %d\n", err);
209 return err;
210 }
211
212 fbn->pcs = pcs;
213
214 fbn->phylink_config.dev = &netdev->dev;
215 fbn->phylink_config.type = PHYLINK_NETDEV;
216 fbn->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE |
217 MAC_25000FD | MAC_50000FD |
218 MAC_100000FD;
219 fbn->phylink_config.default_an_inband = true;
220
221 __set_bit(PHY_INTERFACE_MODE_100GBASEP,
222 fbn->phylink_config.supported_interfaces);
223 __set_bit(PHY_INTERFACE_MODE_50GBASER,
224 fbn->phylink_config.supported_interfaces);
225 __set_bit(PHY_INTERFACE_MODE_LAUI,
226 fbn->phylink_config.supported_interfaces);
227 __set_bit(PHY_INTERFACE_MODE_25GBASER,
228 fbn->phylink_config.supported_interfaces);
229
230 fbnic_mac_get_fw_settings(fbd, &fbn->aui, &fbn->fec);
231
232 phylink = phylink_create(&fbn->phylink_config, NULL,
233 fbnic_phylink_select_interface(fbn->aui),
234 &fbnic_phylink_mac_ops);
235 if (IS_ERR(phylink)) {
236 err = PTR_ERR(phylink);
237 dev_err(netdev->dev.parent,
238 "Failed to create Phylink interface, err: %d\n", err);
239 xpcs_destroy_pcs(pcs);
240 return err;
241 }
242
243 fbn->phylink = phylink;
244
245 return 0;
246 }
247
248 /**
249 * fbnic_phylink_destroy - Teardown phylink related interfaces
250 * @netdev: Network Device struct containing phylink device
251 *
252 * Detach and free resources related to phylink interface.
253 **/
fbnic_phylink_destroy(struct net_device * netdev)254 void fbnic_phylink_destroy(struct net_device *netdev)
255 {
256 struct fbnic_net *fbn = netdev_priv(netdev);
257
258 if (fbn->phylink)
259 phylink_destroy(fbn->phylink);
260 if (fbn->pcs)
261 xpcs_destroy_pcs(fbn->pcs);
262 }
263
264 /**
265 * fbnic_phylink_pmd_training_complete_notify - PMD training complete notifier
266 * @netdev: Netdev struct phylink device attached to
267 *
268 * When the link first comes up the PMD will have a period of 2 to 3 seconds
269 * where the link will flutter due to link training. To avoid spamming the
270 * kernel log with messages about this we add a delay of 4 seconds from the
271 * time of the last PCS report of link so that we can guarantee we are unlikely
272 * to see any further link loss events due to link training.
273 **/
fbnic_phylink_pmd_training_complete_notify(struct net_device * netdev)274 void fbnic_phylink_pmd_training_complete_notify(struct net_device *netdev)
275 {
276 struct fbnic_net *fbn = netdev_priv(netdev);
277 struct fbnic_dev *fbd = fbn->fbd;
278
279 if (fbd->pmd_state != FBNIC_PMD_TRAINING)
280 return;
281
282 /* Prevent reading end_of_pmd_training until we verified state */
283 smp_rmb();
284
285 if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies))
286 return;
287
288 /* At this point we have verified that the link has been up for
289 * the full training duration. As a first step we will try
290 * transitioning to link ready.
291 */
292 if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_TRAINING,
293 FBNIC_PMD_LINK_READY) != FBNIC_PMD_TRAINING)
294 return;
295
296 /* Perform a follow-up check to verify that the link didn't flap
297 * just before our transition by rechecking the training timer.
298 */
299 if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies))
300 return;
301
302 /* The training timeout has been completed. We are good to swap out
303 * link_ready for send_data assuming no other events have occurred
304 * that would have pulled us back into initialization or training.
305 */
306 if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_LINK_READY,
307 FBNIC_PMD_SEND_DATA) != FBNIC_PMD_LINK_READY)
308 return;
309
310 phylink_pcs_change(fbn->pcs, false);
311 }
312