xref: /linux/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c (revision 8be4d31cb8aaeea27bde4b7ddb26e28a89062ebf)
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 
fbnic_phylink_select_interface(u8 aui)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 
fbnic_phylink_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)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 
fbnic_phylink_set_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)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
fbnic_phylink_get_supported_fec_modes(unsigned long * supported)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 
fbnic_phylink_ethtool_ksettings_get(struct net_device * netdev,struct ethtool_link_ksettings * cmd)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 
fbnic_phylink_get_fecparam(struct net_device * netdev,struct ethtool_fecparam * fecparam)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 *
fbnic_pcs_to_net(struct phylink_pcs * pcs)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
fbnic_phylink_pcs_get_state(struct phylink_pcs * pcs,unsigned int neg_mode,struct phylink_link_state * state)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
fbnic_phylink_pcs_enable(struct phylink_pcs * pcs)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
fbnic_phylink_pcs_disable(struct phylink_pcs * pcs)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
fbnic_phylink_pcs_config(struct phylink_pcs * pcs,unsigned int neg_mode,phy_interface_t interface,const unsigned long * advertising,bool permit_pause_to_mac)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 *
fbnic_phylink_mac_select_pcs(struct phylink_config * config,phy_interface_t interface)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
fbnic_phylink_mac_config(struct phylink_config * config,unsigned int mode,const struct phylink_link_state * state)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
fbnic_phylink_mac_link_down(struct phylink_config * config,unsigned int mode,phy_interface_t interface)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
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)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 
fbnic_phylink_init(struct net_device * netdev)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