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