xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c (revision 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88)
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 
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 
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 
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
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 
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 
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 *
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
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
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
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
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
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  **/
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  **/
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  **/
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