File Index
Files and directories
Sign in
FILE

fbnic_phylink.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c C
  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
 12static 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
 28void 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
 36int 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
 44static void
 45fbnic_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
 64int 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
 83int 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
105static struct phylink_pcs *
106fbnic_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
115static int
116fbnic_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
128static void
129fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
130			 const struct phylink_link_state *state)
131{
132}
133
134static int
135fbnic_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
148static void
149fbnic_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
161static void
162fbnic_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
177static 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 **/
197int 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 **/
254void 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 **/
274void 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}
~/ Read-only file preview.