1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /**************************************************************************** |
3 | * Driver for Solarflare network controllers and boards |
4 | * Copyright 2018 Solarflare Communications Inc. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License version 2 as published |
8 | * by the Free Software Foundation, incorporated herein by reference. |
9 | */ |
10 | |
11 | #include "mcdi_port_common.h" |
12 | #include "efx_common.h" |
13 | #include "nic.h" |
14 | |
15 | int efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_data *cfg) |
16 | { |
17 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_CFG_OUT_LEN); |
18 | size_t outlen; |
19 | int rc; |
20 | |
21 | BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0); |
22 | BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name)); |
23 | |
24 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, inlen: 0, |
25 | outbuf, outlen: sizeof(outbuf), outlen_actual: &outlen); |
26 | if (rc) |
27 | goto fail; |
28 | |
29 | if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) { |
30 | rc = -EIO; |
31 | goto fail; |
32 | } |
33 | |
34 | cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS); |
35 | cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE); |
36 | cfg->supported_cap = |
37 | MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP); |
38 | cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL); |
39 | cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT); |
40 | cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK); |
41 | memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME), |
42 | sizeof(cfg->name)); |
43 | cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE); |
44 | cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK); |
45 | memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION), |
46 | sizeof(cfg->revision)); |
47 | |
48 | return 0; |
49 | |
50 | fail: |
51 | netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n" , __func__, rc); |
52 | return rc; |
53 | } |
54 | |
55 | void efx_link_set_advertising(struct efx_nic *efx, |
56 | const unsigned long *advertising) |
57 | { |
58 | memcpy(efx->link_advertising, advertising, |
59 | sizeof(__ETHTOOL_DECLARE_LINK_MODE_MASK())); |
60 | |
61 | efx->link_advertising[0] |= ADVERTISED_Autoneg; |
62 | if (advertising[0] & ADVERTISED_Pause) |
63 | efx->wanted_fc |= (EFX_FC_TX | EFX_FC_RX); |
64 | else |
65 | efx->wanted_fc &= ~(EFX_FC_TX | EFX_FC_RX); |
66 | if (advertising[0] & ADVERTISED_Asym_Pause) |
67 | efx->wanted_fc ^= EFX_FC_TX; |
68 | } |
69 | |
70 | int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities, |
71 | u32 flags, u32 loopback_mode, u32 loopback_speed) |
72 | { |
73 | MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_LINK_IN_LEN); |
74 | |
75 | BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0); |
76 | |
77 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities); |
78 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags); |
79 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode); |
80 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed); |
81 | |
82 | return efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, inlen: sizeof(inbuf), |
83 | NULL, outlen: 0, NULL); |
84 | } |
85 | |
86 | int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes) |
87 | { |
88 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LOOPBACK_MODES_OUT_LEN); |
89 | size_t outlen; |
90 | int rc; |
91 | |
92 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, inlen: 0, |
93 | outbuf, outlen: sizeof(outbuf), outlen_actual: &outlen); |
94 | if (rc) |
95 | goto fail; |
96 | |
97 | if (outlen < (MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_OFST + |
98 | MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_LEN)) { |
99 | rc = -EIO; |
100 | goto fail; |
101 | } |
102 | |
103 | *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_OUT_SUGGESTED); |
104 | |
105 | return 0; |
106 | |
107 | fail: |
108 | netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n" , __func__, rc); |
109 | return rc; |
110 | } |
111 | |
112 | void mcdi_to_ethtool_linkset(u32 media, u32 cap, unsigned long *linkset) |
113 | { |
114 | #define SET_BIT(name) __set_bit(ETHTOOL_LINK_MODE_ ## name ## _BIT, \ |
115 | linkset) |
116 | |
117 | bitmap_zero(dst: linkset, nbits: __ETHTOOL_LINK_MODE_MASK_NBITS); |
118 | switch (media) { |
119 | case MC_CMD_MEDIA_KX4: |
120 | SET_BIT(Backplane); |
121 | if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) |
122 | SET_BIT(1000baseKX_Full); |
123 | if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) |
124 | SET_BIT(10000baseKX4_Full); |
125 | if (cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) |
126 | SET_BIT(40000baseKR4_Full); |
127 | break; |
128 | |
129 | case MC_CMD_MEDIA_XFP: |
130 | case MC_CMD_MEDIA_SFP_PLUS: |
131 | case MC_CMD_MEDIA_QSFP_PLUS: |
132 | SET_BIT(FIBRE); |
133 | if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) { |
134 | SET_BIT(1000baseT_Full); |
135 | SET_BIT(1000baseX_Full); |
136 | } |
137 | if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) { |
138 | SET_BIT(10000baseCR_Full); |
139 | SET_BIT(10000baseLR_Full); |
140 | SET_BIT(10000baseSR_Full); |
141 | } |
142 | if (cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) { |
143 | SET_BIT(40000baseCR4_Full); |
144 | SET_BIT(40000baseSR4_Full); |
145 | } |
146 | if (cap & (1 << MC_CMD_PHY_CAP_100000FDX_LBN)) { |
147 | SET_BIT(100000baseCR4_Full); |
148 | SET_BIT(100000baseSR4_Full); |
149 | } |
150 | if (cap & (1 << MC_CMD_PHY_CAP_25000FDX_LBN)) { |
151 | SET_BIT(25000baseCR_Full); |
152 | SET_BIT(25000baseSR_Full); |
153 | } |
154 | if (cap & (1 << MC_CMD_PHY_CAP_50000FDX_LBN)) |
155 | SET_BIT(50000baseCR2_Full); |
156 | break; |
157 | |
158 | case MC_CMD_MEDIA_BASE_T: |
159 | SET_BIT(TP); |
160 | if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) |
161 | SET_BIT(10baseT_Half); |
162 | if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) |
163 | SET_BIT(10baseT_Full); |
164 | if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) |
165 | SET_BIT(100baseT_Half); |
166 | if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) |
167 | SET_BIT(100baseT_Full); |
168 | if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) |
169 | SET_BIT(1000baseT_Half); |
170 | if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) |
171 | SET_BIT(1000baseT_Full); |
172 | if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) |
173 | SET_BIT(10000baseT_Full); |
174 | break; |
175 | } |
176 | |
177 | if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) |
178 | SET_BIT(Pause); |
179 | if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) |
180 | SET_BIT(Asym_Pause); |
181 | if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) |
182 | SET_BIT(Autoneg); |
183 | |
184 | #undef SET_BIT |
185 | } |
186 | |
187 | u32 ethtool_linkset_to_mcdi_cap(const unsigned long *linkset) |
188 | { |
189 | u32 result = 0; |
190 | |
191 | #define TEST_BIT(name) test_bit(ETHTOOL_LINK_MODE_ ## name ## _BIT, \ |
192 | linkset) |
193 | |
194 | if (TEST_BIT(10baseT_Half)) |
195 | result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); |
196 | if (TEST_BIT(10baseT_Full)) |
197 | result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); |
198 | if (TEST_BIT(100baseT_Half)) |
199 | result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); |
200 | if (TEST_BIT(100baseT_Full)) |
201 | result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); |
202 | if (TEST_BIT(1000baseT_Half)) |
203 | result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); |
204 | if (TEST_BIT(1000baseT_Full) || TEST_BIT(1000baseKX_Full) || |
205 | TEST_BIT(1000baseX_Full)) |
206 | result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); |
207 | if (TEST_BIT(10000baseT_Full) || TEST_BIT(10000baseKX4_Full) || |
208 | TEST_BIT(10000baseCR_Full) || TEST_BIT(10000baseLR_Full) || |
209 | TEST_BIT(10000baseSR_Full)) |
210 | result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); |
211 | if (TEST_BIT(40000baseCR4_Full) || TEST_BIT(40000baseKR4_Full) || |
212 | TEST_BIT(40000baseSR4_Full)) |
213 | result |= (1 << MC_CMD_PHY_CAP_40000FDX_LBN); |
214 | if (TEST_BIT(100000baseCR4_Full) || TEST_BIT(100000baseSR4_Full)) |
215 | result |= (1 << MC_CMD_PHY_CAP_100000FDX_LBN); |
216 | if (TEST_BIT(25000baseCR_Full) || TEST_BIT(25000baseSR_Full)) |
217 | result |= (1 << MC_CMD_PHY_CAP_25000FDX_LBN); |
218 | if (TEST_BIT(50000baseCR2_Full)) |
219 | result |= (1 << MC_CMD_PHY_CAP_50000FDX_LBN); |
220 | if (TEST_BIT(Pause)) |
221 | result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); |
222 | if (TEST_BIT(Asym_Pause)) |
223 | result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); |
224 | if (TEST_BIT(Autoneg)) |
225 | result |= (1 << MC_CMD_PHY_CAP_AN_LBN); |
226 | |
227 | #undef TEST_BIT |
228 | |
229 | return result; |
230 | } |
231 | |
232 | u32 efx_get_mcdi_phy_flags(struct efx_nic *efx) |
233 | { |
234 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
235 | enum efx_phy_mode mode, supported; |
236 | u32 flags; |
237 | |
238 | /* TODO: Advertise the capabilities supported by this PHY */ |
239 | supported = 0; |
240 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_TXDIS_LBN)) |
241 | supported |= PHY_MODE_TX_DISABLED; |
242 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_LOWPOWER_LBN)) |
243 | supported |= PHY_MODE_LOW_POWER; |
244 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_POWEROFF_LBN)) |
245 | supported |= PHY_MODE_OFF; |
246 | |
247 | mode = efx->phy_mode & supported; |
248 | |
249 | flags = 0; |
250 | if (mode & PHY_MODE_TX_DISABLED) |
251 | flags |= (1 << MC_CMD_SET_LINK_IN_TXDIS_LBN); |
252 | if (mode & PHY_MODE_LOW_POWER) |
253 | flags |= (1 << MC_CMD_SET_LINK_IN_LOWPOWER_LBN); |
254 | if (mode & PHY_MODE_OFF) |
255 | flags |= (1 << MC_CMD_SET_LINK_IN_POWEROFF_LBN); |
256 | |
257 | return flags; |
258 | } |
259 | |
260 | u8 mcdi_to_ethtool_media(u32 media) |
261 | { |
262 | switch (media) { |
263 | case MC_CMD_MEDIA_XAUI: |
264 | case MC_CMD_MEDIA_CX4: |
265 | case MC_CMD_MEDIA_KX4: |
266 | return PORT_OTHER; |
267 | |
268 | case MC_CMD_MEDIA_XFP: |
269 | case MC_CMD_MEDIA_SFP_PLUS: |
270 | case MC_CMD_MEDIA_QSFP_PLUS: |
271 | return PORT_FIBRE; |
272 | |
273 | case MC_CMD_MEDIA_BASE_T: |
274 | return PORT_TP; |
275 | |
276 | default: |
277 | return PORT_OTHER; |
278 | } |
279 | } |
280 | |
281 | void efx_mcdi_phy_decode_link(struct efx_nic *efx, |
282 | struct efx_link_state *link_state, |
283 | u32 speed, u32 flags, u32 fcntl) |
284 | { |
285 | switch (fcntl) { |
286 | case MC_CMD_FCNTL_AUTO: |
287 | WARN_ON(1); /* This is not a link mode */ |
288 | link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX; |
289 | break; |
290 | case MC_CMD_FCNTL_BIDIR: |
291 | link_state->fc = EFX_FC_TX | EFX_FC_RX; |
292 | break; |
293 | case MC_CMD_FCNTL_RESPOND: |
294 | link_state->fc = EFX_FC_RX; |
295 | break; |
296 | default: |
297 | WARN_ON(1); |
298 | fallthrough; |
299 | case MC_CMD_FCNTL_OFF: |
300 | link_state->fc = 0; |
301 | break; |
302 | } |
303 | |
304 | link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN)); |
305 | link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN)); |
306 | link_state->speed = speed; |
307 | } |
308 | |
309 | /* The semantics of the ethtool FEC mode bitmask are not well defined, |
310 | * particularly the meaning of combinations of bits. Which means we get to |
311 | * define our own semantics, as follows: |
312 | * OFF overrides any other bits, and means "disable all FEC" (with the |
313 | * exception of 25G KR4/CR4, where it is not possible to reject it if AN |
314 | * partner requests it). |
315 | * AUTO on its own means use cable requirements and link partner autoneg with |
316 | * fw-default preferences for the cable type. |
317 | * AUTO and either RS or BASER means use the specified FEC type if cable and |
318 | * link partner support it, otherwise autoneg/fw-default. |
319 | * RS or BASER alone means use the specified FEC type if cable and link partner |
320 | * support it and either requests it, otherwise no FEC. |
321 | * Both RS and BASER (whether AUTO or not) means use FEC if cable and link |
322 | * partner support it, preferring RS to BASER. |
323 | */ |
324 | u32 ethtool_fec_caps_to_mcdi(u32 supported_cap, u32 ethtool_cap) |
325 | { |
326 | u32 ret = 0; |
327 | |
328 | if (ethtool_cap & ETHTOOL_FEC_OFF) |
329 | return 0; |
330 | |
331 | if (ethtool_cap & ETHTOOL_FEC_AUTO) |
332 | ret |= ((1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | |
333 | (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | |
334 | (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) & supported_cap; |
335 | if (ethtool_cap & ETHTOOL_FEC_RS && |
336 | supported_cap & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) |
337 | ret |= (1 << MC_CMD_PHY_CAP_RS_FEC_LBN) | |
338 | (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN); |
339 | if (ethtool_cap & ETHTOOL_FEC_BASER) { |
340 | if (supported_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN)) |
341 | ret |= (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN) | |
342 | (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN); |
343 | if (supported_cap & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN)) |
344 | ret |= (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) | |
345 | (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN); |
346 | } |
347 | return ret; |
348 | } |
349 | |
350 | /* Invert ethtool_fec_caps_to_mcdi. There are two combinations that function |
351 | * can never produce, (baser xor rs) and neither req; the implementation below |
352 | * maps both of those to AUTO. This should never matter, and it's not clear |
353 | * what a better mapping would be anyway. |
354 | */ |
355 | u32 mcdi_fec_caps_to_ethtool(u32 caps, bool is_25g) |
356 | { |
357 | bool rs = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN), |
358 | rs_req = caps & (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN), |
359 | baser = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN) |
360 | : caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN), |
361 | baser_req = is_25g ? caps & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN) |
362 | : caps & (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN); |
363 | |
364 | if (!baser && !rs) |
365 | return ETHTOOL_FEC_OFF; |
366 | return (rs_req ? ETHTOOL_FEC_RS : 0) | |
367 | (baser_req ? ETHTOOL_FEC_BASER : 0) | |
368 | (baser == baser_req && rs == rs_req ? 0 : ETHTOOL_FEC_AUTO); |
369 | } |
370 | |
371 | /* Verify that the forced flow control settings (!EFX_FC_AUTO) are |
372 | * supported by the link partner. Warn the user if this isn't the case |
373 | */ |
374 | void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa) |
375 | { |
376 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
377 | u32 rmtadv; |
378 | |
379 | /* The link partner capabilities are only relevant if the |
380 | * link supports flow control autonegotiation |
381 | */ |
382 | if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) |
383 | return; |
384 | |
385 | /* If flow control autoneg is supported and enabled, then fine */ |
386 | if (efx->wanted_fc & EFX_FC_AUTO) |
387 | return; |
388 | |
389 | rmtadv = 0; |
390 | if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) |
391 | rmtadv |= ADVERTISED_Pause; |
392 | if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) |
393 | rmtadv |= ADVERTISED_Asym_Pause; |
394 | |
395 | if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause) |
396 | netif_err(efx, link, efx->net_dev, |
397 | "warning: link partner doesn't support pause frames" ); |
398 | } |
399 | |
400 | bool efx_mcdi_phy_poll(struct efx_nic *efx) |
401 | { |
402 | struct efx_link_state old_state = efx->link_state; |
403 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN); |
404 | int rc; |
405 | |
406 | WARN_ON(!mutex_is_locked(&efx->mac_lock)); |
407 | |
408 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); |
409 | |
410 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, inlen: 0, |
411 | outbuf, outlen: sizeof(outbuf), NULL); |
412 | if (rc) |
413 | efx->link_state.up = false; |
414 | else |
415 | efx_mcdi_phy_decode_link( |
416 | efx, link_state: &efx->link_state, |
417 | MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), |
418 | MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), |
419 | MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); |
420 | |
421 | return !efx_link_state_equal(left: &efx->link_state, right: &old_state); |
422 | } |
423 | |
424 | int efx_mcdi_phy_probe(struct efx_nic *efx) |
425 | { |
426 | struct efx_mcdi_phy_data *phy_data; |
427 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN); |
428 | u32 caps; |
429 | int rc; |
430 | |
431 | /* Initialise and populate phy_data */ |
432 | phy_data = kzalloc(size: sizeof(*phy_data), GFP_KERNEL); |
433 | if (phy_data == NULL) |
434 | return -ENOMEM; |
435 | |
436 | rc = efx_mcdi_get_phy_cfg(efx, cfg: phy_data); |
437 | if (rc != 0) |
438 | goto fail; |
439 | |
440 | /* Read initial link advertisement */ |
441 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); |
442 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, inlen: 0, |
443 | outbuf, outlen: sizeof(outbuf), NULL); |
444 | if (rc) |
445 | goto fail; |
446 | |
447 | /* Fill out nic state */ |
448 | efx->phy_data = phy_data; |
449 | efx->phy_type = phy_data->type; |
450 | |
451 | efx->mdio_bus = phy_data->channel; |
452 | efx->mdio.prtad = phy_data->port; |
453 | efx->mdio.mmds = phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); |
454 | efx->mdio.mode_support = 0; |
455 | if (phy_data->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) |
456 | efx->mdio.mode_support |= MDIO_SUPPORTS_C22; |
457 | if (phy_data->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) |
458 | efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; |
459 | |
460 | caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); |
461 | if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) |
462 | mcdi_to_ethtool_linkset(media: phy_data->media, cap: caps, |
463 | linkset: efx->link_advertising); |
464 | else |
465 | phy_data->forced_cap = caps; |
466 | |
467 | /* Assert that we can map efx -> mcdi loopback modes */ |
468 | BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); |
469 | BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); |
470 | BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC); |
471 | BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII); |
472 | BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS); |
473 | BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI); |
474 | BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII); |
475 | BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII); |
476 | BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR); |
477 | BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI); |
478 | BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR); |
479 | BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR); |
480 | BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR); |
481 | BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR); |
482 | BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY); |
483 | BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS); |
484 | BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS); |
485 | BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD); |
486 | BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT); |
487 | BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS); |
488 | BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS); |
489 | BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR); |
490 | BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR); |
491 | BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS); |
492 | BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS); |
493 | BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR); |
494 | BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS); |
495 | |
496 | rc = efx_mcdi_loopback_modes(efx, loopback_modes: &efx->loopback_modes); |
497 | if (rc != 0) |
498 | goto fail; |
499 | /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, |
500 | * but by convention we don't |
501 | */ |
502 | efx->loopback_modes &= ~(1 << LOOPBACK_NONE); |
503 | |
504 | /* Set the initial link mode */ |
505 | efx_mcdi_phy_decode_link(efx, link_state: &efx->link_state, |
506 | MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), |
507 | MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), |
508 | MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); |
509 | |
510 | /* Record the initial FEC configuration (or nearest approximation |
511 | * representable in the ethtool configuration space) |
512 | */ |
513 | efx->fec_config = mcdi_fec_caps_to_ethtool(caps, |
514 | is_25g: efx->link_state.speed == 25000 || |
515 | efx->link_state.speed == 50000); |
516 | |
517 | /* Default to Autonegotiated flow control if the PHY supports it */ |
518 | efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; |
519 | if (phy_data->supported_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) |
520 | efx->wanted_fc |= EFX_FC_AUTO; |
521 | efx_link_set_wanted_fc(efx, efx->wanted_fc); |
522 | |
523 | return 0; |
524 | |
525 | fail: |
526 | kfree(objp: phy_data); |
527 | return rc; |
528 | } |
529 | |
530 | void efx_mcdi_phy_remove(struct efx_nic *efx) |
531 | { |
532 | struct efx_mcdi_phy_data *phy_data = efx->phy_data; |
533 | |
534 | efx->phy_data = NULL; |
535 | kfree(objp: phy_data); |
536 | } |
537 | |
538 | void efx_mcdi_phy_get_link_ksettings(struct efx_nic *efx, struct ethtool_link_ksettings *cmd) |
539 | { |
540 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
541 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_LEN); |
542 | int rc; |
543 | |
544 | cmd->base.speed = efx->link_state.speed; |
545 | cmd->base.duplex = efx->link_state.fd; |
546 | cmd->base.port = mcdi_to_ethtool_media(media: phy_cfg->media); |
547 | cmd->base.phy_address = phy_cfg->port; |
548 | cmd->base.autoneg = !!(efx->link_advertising[0] & ADVERTISED_Autoneg); |
549 | cmd->base.mdio_support = (efx->mdio.mode_support & |
550 | (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22)); |
551 | |
552 | mcdi_to_ethtool_linkset(media: phy_cfg->media, cap: phy_cfg->supported_cap, |
553 | linkset: cmd->link_modes.supported); |
554 | memcpy(cmd->link_modes.advertising, efx->link_advertising, |
555 | sizeof(__ETHTOOL_DECLARE_LINK_MODE_MASK())); |
556 | |
557 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); |
558 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, inlen: 0, |
559 | outbuf, outlen: sizeof(outbuf), NULL); |
560 | if (rc) |
561 | return; |
562 | mcdi_to_ethtool_linkset(media: phy_cfg->media, |
563 | MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP), |
564 | linkset: cmd->link_modes.lp_advertising); |
565 | } |
566 | |
567 | int efx_mcdi_phy_set_link_ksettings(struct efx_nic *efx, const struct ethtool_link_ksettings *cmd) |
568 | { |
569 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
570 | u32 caps; |
571 | int rc; |
572 | |
573 | if (cmd->base.autoneg) { |
574 | caps = (ethtool_linkset_to_mcdi_cap(linkset: cmd->link_modes.advertising) | |
575 | 1 << MC_CMD_PHY_CAP_AN_LBN); |
576 | } else if (cmd->base.duplex) { |
577 | switch (cmd->base.speed) { |
578 | case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break; |
579 | case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break; |
580 | case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break; |
581 | case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break; |
582 | case 40000: caps = 1 << MC_CMD_PHY_CAP_40000FDX_LBN; break; |
583 | case 100000: caps = 1 << MC_CMD_PHY_CAP_100000FDX_LBN; break; |
584 | case 25000: caps = 1 << MC_CMD_PHY_CAP_25000FDX_LBN; break; |
585 | case 50000: caps = 1 << MC_CMD_PHY_CAP_50000FDX_LBN; break; |
586 | default: return -EINVAL; |
587 | } |
588 | } else { |
589 | switch (cmd->base.speed) { |
590 | case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break; |
591 | case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break; |
592 | case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break; |
593 | default: return -EINVAL; |
594 | } |
595 | } |
596 | |
597 | caps |= ethtool_fec_caps_to_mcdi(supported_cap: phy_cfg->supported_cap, ethtool_cap: efx->fec_config); |
598 | |
599 | rc = efx_mcdi_set_link(efx, capabilities: caps, flags: efx_get_mcdi_phy_flags(efx), |
600 | loopback_mode: efx->loopback_mode, loopback_speed: 0); |
601 | if (rc) |
602 | return rc; |
603 | |
604 | if (cmd->base.autoneg) { |
605 | efx_link_set_advertising(efx, advertising: cmd->link_modes.advertising); |
606 | phy_cfg->forced_cap = 0; |
607 | } else { |
608 | efx_link_clear_advertising(efx); |
609 | phy_cfg->forced_cap = caps; |
610 | } |
611 | return 0; |
612 | } |
613 | |
614 | int efx_mcdi_phy_get_fecparam(struct efx_nic *efx, struct ethtool_fecparam *fec) |
615 | { |
616 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_LINK_OUT_V2_LEN); |
617 | u32 caps, active, speed; /* MCDI format */ |
618 | bool is_25g = false; |
619 | size_t outlen; |
620 | int rc; |
621 | |
622 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); |
623 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, inlen: 0, |
624 | outbuf, outlen: sizeof(outbuf), outlen_actual: &outlen); |
625 | if (rc) |
626 | return rc; |
627 | if (outlen < MC_CMD_GET_LINK_OUT_V2_LEN) |
628 | return -EOPNOTSUPP; |
629 | |
630 | /* behaviour for 25G/50G links depends on 25G BASER bit */ |
631 | speed = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_LINK_SPEED); |
632 | is_25g = speed == 25000 || speed == 50000; |
633 | |
634 | caps = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_CAP); |
635 | fec->fec = mcdi_fec_caps_to_ethtool(caps, is_25g); |
636 | /* BASER is never supported on 100G */ |
637 | if (speed == 100000) |
638 | fec->fec &= ~ETHTOOL_FEC_BASER; |
639 | |
640 | active = MCDI_DWORD(outbuf, GET_LINK_OUT_V2_FEC_TYPE); |
641 | switch (active) { |
642 | case MC_CMD_FEC_NONE: |
643 | fec->active_fec = ETHTOOL_FEC_OFF; |
644 | break; |
645 | case MC_CMD_FEC_BASER: |
646 | fec->active_fec = ETHTOOL_FEC_BASER; |
647 | break; |
648 | case MC_CMD_FEC_RS: |
649 | fec->active_fec = ETHTOOL_FEC_RS; |
650 | break; |
651 | default: |
652 | netif_warn(efx, hw, efx->net_dev, |
653 | "Firmware reports unrecognised FEC_TYPE %u\n" , |
654 | active); |
655 | /* We don't know what firmware has picked. AUTO is as good a |
656 | * "can't happen" value as any other. |
657 | */ |
658 | fec->active_fec = ETHTOOL_FEC_AUTO; |
659 | break; |
660 | } |
661 | |
662 | return 0; |
663 | } |
664 | |
665 | /* Basic validation to ensure that the caps we are going to attempt to set are |
666 | * in fact supported by the adapter. Note that 'no FEC' is always supported. |
667 | */ |
668 | static int ethtool_fec_supported(u32 supported_cap, u32 ethtool_cap) |
669 | { |
670 | if (ethtool_cap & ETHTOOL_FEC_OFF) |
671 | return 0; |
672 | |
673 | if (ethtool_cap && |
674 | !ethtool_fec_caps_to_mcdi(supported_cap, ethtool_cap)) |
675 | return -EINVAL; |
676 | return 0; |
677 | } |
678 | |
679 | int efx_mcdi_phy_set_fecparam(struct efx_nic *efx, const struct ethtool_fecparam *fec) |
680 | { |
681 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
682 | u32 caps; |
683 | int rc; |
684 | |
685 | rc = ethtool_fec_supported(supported_cap: phy_cfg->supported_cap, ethtool_cap: fec->fec); |
686 | if (rc) |
687 | return rc; |
688 | |
689 | /* Work out what efx_mcdi_phy_set_link_ksettings() would produce from |
690 | * saved advertising bits |
691 | */ |
692 | if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, efx->link_advertising)) |
693 | caps = (ethtool_linkset_to_mcdi_cap(linkset: efx->link_advertising) | |
694 | 1 << MC_CMD_PHY_CAP_AN_LBN); |
695 | else |
696 | caps = phy_cfg->forced_cap; |
697 | |
698 | caps |= ethtool_fec_caps_to_mcdi(supported_cap: phy_cfg->supported_cap, ethtool_cap: fec->fec); |
699 | rc = efx_mcdi_set_link(efx, capabilities: caps, flags: efx_get_mcdi_phy_flags(efx), |
700 | loopback_mode: efx->loopback_mode, loopback_speed: 0); |
701 | if (rc) |
702 | return rc; |
703 | |
704 | /* Record the new FEC setting for subsequent set_link calls */ |
705 | efx->fec_config = fec->fec; |
706 | return 0; |
707 | } |
708 | |
709 | int efx_mcdi_phy_test_alive(struct efx_nic *efx) |
710 | { |
711 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_STATE_OUT_LEN); |
712 | size_t outlen; |
713 | int rc; |
714 | |
715 | BUILD_BUG_ON(MC_CMD_GET_PHY_STATE_IN_LEN != 0); |
716 | |
717 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_STATE, NULL, inlen: 0, |
718 | outbuf, outlen: sizeof(outbuf), outlen_actual: &outlen); |
719 | if (rc) |
720 | return rc; |
721 | |
722 | if (outlen < MC_CMD_GET_PHY_STATE_OUT_LEN) |
723 | return -EIO; |
724 | if (MCDI_DWORD(outbuf, GET_PHY_STATE_OUT_STATE) != MC_CMD_PHY_STATE_OK) |
725 | return -EINVAL; |
726 | |
727 | return 0; |
728 | } |
729 | |
730 | int efx_mcdi_port_reconfigure(struct efx_nic *efx) |
731 | { |
732 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
733 | u32 caps = (efx->link_advertising[0] ? |
734 | ethtool_linkset_to_mcdi_cap(linkset: efx->link_advertising) : |
735 | phy_cfg->forced_cap); |
736 | |
737 | caps |= ethtool_fec_caps_to_mcdi(supported_cap: phy_cfg->supported_cap, ethtool_cap: efx->fec_config); |
738 | |
739 | return efx_mcdi_set_link(efx, capabilities: caps, flags: efx_get_mcdi_phy_flags(efx), |
740 | loopback_mode: efx->loopback_mode, loopback_speed: 0); |
741 | } |
742 | |
743 | static const char *const mcdi_sft9001_cable_diag_names[] = { |
744 | "cable.pairA.length" , |
745 | "cable.pairB.length" , |
746 | "cable.pairC.length" , |
747 | "cable.pairD.length" , |
748 | "cable.pairA.status" , |
749 | "cable.pairB.status" , |
750 | "cable.pairC.status" , |
751 | "cable.pairD.status" , |
752 | }; |
753 | |
754 | static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode, |
755 | int *results) |
756 | { |
757 | unsigned int retry, i, count = 0; |
758 | size_t outlen; |
759 | u32 status; |
760 | MCDI_DECLARE_BUF(inbuf, MC_CMD_START_BIST_IN_LEN); |
761 | MCDI_DECLARE_BUF(outbuf, MC_CMD_POLL_BIST_OUT_SFT9001_LEN); |
762 | u8 *ptr; |
763 | int rc; |
764 | |
765 | BUILD_BUG_ON(MC_CMD_START_BIST_OUT_LEN != 0); |
766 | MCDI_SET_DWORD(inbuf, START_BIST_IN_TYPE, bist_mode); |
767 | rc = efx_mcdi_rpc(efx, MC_CMD_START_BIST, |
768 | inbuf, MC_CMD_START_BIST_IN_LEN, NULL, outlen: 0, NULL); |
769 | if (rc) |
770 | goto out; |
771 | |
772 | /* Wait up to 10s for BIST to finish */ |
773 | for (retry = 0; retry < 100; ++retry) { |
774 | BUILD_BUG_ON(MC_CMD_POLL_BIST_IN_LEN != 0); |
775 | rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, inlen: 0, |
776 | outbuf, outlen: sizeof(outbuf), outlen_actual: &outlen); |
777 | if (rc) |
778 | goto out; |
779 | |
780 | status = MCDI_DWORD(outbuf, POLL_BIST_OUT_RESULT); |
781 | if (status != MC_CMD_POLL_BIST_RUNNING) |
782 | goto finished; |
783 | |
784 | msleep(msecs: 100); |
785 | } |
786 | |
787 | rc = -ETIMEDOUT; |
788 | goto out; |
789 | |
790 | finished: |
791 | results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1; |
792 | |
793 | /* SFT9001 specific cable diagnostics output */ |
794 | if (efx->phy_type == PHY_TYPE_SFT9001B && |
795 | (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT || |
796 | bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) { |
797 | ptr = MCDI_PTR(outbuf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A); |
798 | if (status == MC_CMD_POLL_BIST_PASSED && |
799 | outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) { |
800 | for (i = 0; i < 8; i++) { |
801 | results[count + i] = |
802 | EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i], |
803 | EFX_DWORD_0); |
804 | } |
805 | } |
806 | count += 8; |
807 | } |
808 | rc = count; |
809 | |
810 | out: |
811 | return rc; |
812 | } |
813 | |
814 | int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results, unsigned int flags) |
815 | { |
816 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
817 | u32 mode; |
818 | int rc; |
819 | |
820 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) { |
821 | rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results); |
822 | if (rc < 0) |
823 | return rc; |
824 | |
825 | results += rc; |
826 | } |
827 | |
828 | /* If we support both LONG and SHORT, then run each in response to |
829 | * break or not. Otherwise, run the one we support |
830 | */ |
831 | mode = 0; |
832 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN)) { |
833 | if ((flags & ETH_TEST_FL_OFFLINE) && |
834 | (phy_cfg->flags & |
835 | (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))) |
836 | mode = MC_CMD_PHY_BIST_CABLE_LONG; |
837 | else |
838 | mode = MC_CMD_PHY_BIST_CABLE_SHORT; |
839 | } else if (phy_cfg->flags & |
840 | (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN)) |
841 | mode = MC_CMD_PHY_BIST_CABLE_LONG; |
842 | |
843 | if (mode != 0) { |
844 | rc = efx_mcdi_bist(efx, bist_mode: mode, results); |
845 | if (rc < 0) |
846 | return rc; |
847 | results += rc; |
848 | } |
849 | |
850 | return 0; |
851 | } |
852 | |
853 | const char *efx_mcdi_phy_test_name(struct efx_nic *efx, unsigned int index) |
854 | { |
855 | struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; |
856 | |
857 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_LBN)) { |
858 | if (index == 0) |
859 | return "bist" ; |
860 | --index; |
861 | } |
862 | |
863 | if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_SHORT_LBN) | |
864 | (1 << MC_CMD_GET_PHY_CFG_OUT_BIST_CABLE_LONG_LBN))) { |
865 | if (index == 0) |
866 | return "cable" ; |
867 | --index; |
868 | |
869 | if (efx->phy_type == PHY_TYPE_SFT9001B) { |
870 | if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names)) |
871 | return mcdi_sft9001_cable_diag_names[index]; |
872 | index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names); |
873 | } |
874 | } |
875 | |
876 | return NULL; |
877 | } |
878 | |
879 | #define SFP_PAGE_SIZE 128 |
880 | #define SFF_DIAG_TYPE_OFFSET 92 |
881 | #define SFF_DIAG_ADDR_CHANGE BIT(2) |
882 | #define SFF_8079_NUM_PAGES 2 |
883 | #define SFF_8472_NUM_PAGES 4 |
884 | #define SFF_8436_NUM_PAGES 5 |
885 | #define SFF_DMT_LEVEL_OFFSET 94 |
886 | |
887 | /** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom |
888 | * @efx: NIC context |
889 | * @page: EEPROM page number |
890 | * @data: Destination data pointer |
891 | * @offset: Offset in page to copy from in to data |
892 | * @space: Space available in data |
893 | * |
894 | * Return: |
895 | * >=0 - amount of data copied |
896 | * <0 - error |
897 | */ |
898 | static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx, |
899 | unsigned int page, |
900 | u8 *data, ssize_t offset, |
901 | ssize_t space) |
902 | { |
903 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX); |
904 | MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN); |
905 | unsigned int payload_len; |
906 | unsigned int to_copy; |
907 | size_t outlen; |
908 | int rc; |
909 | |
910 | if (offset > SFP_PAGE_SIZE) |
911 | return -EINVAL; |
912 | |
913 | to_copy = min(space, SFP_PAGE_SIZE - offset); |
914 | |
915 | MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); |
916 | rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO, |
917 | inbuf, inlen: sizeof(inbuf), |
918 | outbuf, outlen: sizeof(outbuf), |
919 | outlen_actual: &outlen); |
920 | |
921 | if (rc) |
922 | return rc; |
923 | |
924 | if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + |
925 | SFP_PAGE_SIZE)) |
926 | return -EIO; |
927 | |
928 | payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN); |
929 | if (payload_len != SFP_PAGE_SIZE) |
930 | return -EIO; |
931 | |
932 | memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset, |
933 | to_copy); |
934 | |
935 | return to_copy; |
936 | } |
937 | |
938 | static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx, |
939 | unsigned int page, |
940 | u8 byte) |
941 | { |
942 | u8 data; |
943 | int rc; |
944 | |
945 | rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, data: &data, offset: byte, space: 1); |
946 | if (rc == 1) |
947 | return data; |
948 | |
949 | return rc; |
950 | } |
951 | |
952 | static int efx_mcdi_phy_diag_type(struct efx_nic *efx) |
953 | { |
954 | /* Page zero of the EEPROM includes the diagnostic type at byte 92. */ |
955 | return efx_mcdi_phy_get_module_eeprom_byte(efx, page: 0, |
956 | SFF_DIAG_TYPE_OFFSET); |
957 | } |
958 | |
959 | static int efx_mcdi_phy_sff_8472_level(struct efx_nic *efx) |
960 | { |
961 | /* Page zero of the EEPROM includes the DMT level at byte 94. */ |
962 | return efx_mcdi_phy_get_module_eeprom_byte(efx, page: 0, |
963 | SFF_DMT_LEVEL_OFFSET); |
964 | } |
965 | |
966 | static u32 efx_mcdi_phy_module_type(struct efx_nic *efx) |
967 | { |
968 | struct efx_mcdi_phy_data *phy_data = efx->phy_data; |
969 | |
970 | if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS) |
971 | return phy_data->media; |
972 | |
973 | /* A QSFP+ NIC may actually have an SFP+ module attached. |
974 | * The ID is page 0, byte 0. |
975 | * QSFP28 is of type SFF_8636, however, this is treated |
976 | * the same by ethtool, so we can also treat them the same. |
977 | */ |
978 | switch (efx_mcdi_phy_get_module_eeprom_byte(efx, page: 0, byte: 0)) { |
979 | case 0x3: /* SFP */ |
980 | return MC_CMD_MEDIA_SFP_PLUS; |
981 | case 0xc: /* QSFP */ |
982 | case 0xd: /* QSFP+ */ |
983 | case 0x11: /* QSFP28 */ |
984 | return MC_CMD_MEDIA_QSFP_PLUS; |
985 | default: |
986 | return 0; |
987 | } |
988 | } |
989 | |
990 | int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, struct ethtool_eeprom *ee, u8 *data) |
991 | { |
992 | int rc; |
993 | ssize_t space_remaining = ee->len; |
994 | unsigned int page_off; |
995 | bool ignore_missing; |
996 | int num_pages; |
997 | int page; |
998 | |
999 | switch (efx_mcdi_phy_module_type(efx)) { |
1000 | case MC_CMD_MEDIA_SFP_PLUS: |
1001 | num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ? |
1002 | SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES; |
1003 | page = 0; |
1004 | ignore_missing = false; |
1005 | break; |
1006 | case MC_CMD_MEDIA_QSFP_PLUS: |
1007 | num_pages = SFF_8436_NUM_PAGES; |
1008 | page = -1; /* We obtain the lower page by asking for -1. */ |
1009 | ignore_missing = true; /* Ignore missing pages after page 0. */ |
1010 | break; |
1011 | default: |
1012 | return -EOPNOTSUPP; |
1013 | } |
1014 | |
1015 | page_off = ee->offset % SFP_PAGE_SIZE; |
1016 | page += ee->offset / SFP_PAGE_SIZE; |
1017 | |
1018 | while (space_remaining && (page < num_pages)) { |
1019 | rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, |
1020 | data, offset: page_off, |
1021 | space: space_remaining); |
1022 | |
1023 | if (rc > 0) { |
1024 | space_remaining -= rc; |
1025 | data += rc; |
1026 | page_off = 0; |
1027 | page++; |
1028 | } else if (rc == 0) { |
1029 | space_remaining = 0; |
1030 | } else if (ignore_missing && (page > 0)) { |
1031 | int intended_size = SFP_PAGE_SIZE - page_off; |
1032 | |
1033 | space_remaining -= intended_size; |
1034 | if (space_remaining < 0) { |
1035 | space_remaining = 0; |
1036 | } else { |
1037 | memset(data, 0, intended_size); |
1038 | data += intended_size; |
1039 | page_off = 0; |
1040 | page++; |
1041 | rc = 0; |
1042 | } |
1043 | } else { |
1044 | return rc; |
1045 | } |
1046 | } |
1047 | |
1048 | return 0; |
1049 | } |
1050 | |
1051 | int efx_mcdi_phy_get_module_info(struct efx_nic *efx, struct ethtool_modinfo *modinfo) |
1052 | { |
1053 | int sff_8472_level; |
1054 | int diag_type; |
1055 | |
1056 | switch (efx_mcdi_phy_module_type(efx)) { |
1057 | case MC_CMD_MEDIA_SFP_PLUS: |
1058 | sff_8472_level = efx_mcdi_phy_sff_8472_level(efx); |
1059 | |
1060 | /* If we can't read the diagnostics level we have none. */ |
1061 | if (sff_8472_level < 0) |
1062 | return -EOPNOTSUPP; |
1063 | |
1064 | /* Check if this module requires the (unsupported) address |
1065 | * change operation. |
1066 | */ |
1067 | diag_type = efx_mcdi_phy_diag_type(efx); |
1068 | |
1069 | if (sff_8472_level == 0 || |
1070 | (diag_type & SFF_DIAG_ADDR_CHANGE)) { |
1071 | modinfo->type = ETH_MODULE_SFF_8079; |
1072 | modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; |
1073 | } else { |
1074 | modinfo->type = ETH_MODULE_SFF_8472; |
1075 | modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; |
1076 | } |
1077 | break; |
1078 | |
1079 | case MC_CMD_MEDIA_QSFP_PLUS: |
1080 | modinfo->type = ETH_MODULE_SFF_8436; |
1081 | modinfo->eeprom_len = ETH_MODULE_SFF_8436_MAX_LEN; |
1082 | break; |
1083 | |
1084 | default: |
1085 | return -EOPNOTSUPP; |
1086 | } |
1087 | |
1088 | return 0; |
1089 | } |
1090 | |
1091 | static unsigned int efx_calc_mac_mtu(struct efx_nic *efx) |
1092 | { |
1093 | return EFX_MAX_FRAME_LEN(efx->net_dev->mtu); |
1094 | } |
1095 | |
1096 | int efx_mcdi_set_mac(struct efx_nic *efx) |
1097 | { |
1098 | u32 fcntl; |
1099 | MCDI_DECLARE_BUF(cmdbytes, MC_CMD_SET_MAC_IN_LEN); |
1100 | |
1101 | BUILD_BUG_ON(MC_CMD_SET_MAC_OUT_LEN != 0); |
1102 | |
1103 | /* This has no effect on EF10 */ |
1104 | ether_addr_copy(MCDI_PTR(cmdbytes, SET_MAC_IN_ADDR), |
1105 | src: efx->net_dev->dev_addr); |
1106 | |
1107 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU, efx_calc_mac_mtu(efx)); |
1108 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0); |
1109 | MCDI_POPULATE_DWORD_1(cmdbytes, SET_MAC_IN_FLAGS, |
1110 | SET_MAC_IN_FLAG_INCLUDE_FCS, |
1111 | !!(efx->net_dev->features & NETIF_F_RXFCS)); |
1112 | |
1113 | switch (efx->wanted_fc) { |
1114 | case EFX_FC_RX | EFX_FC_TX: |
1115 | fcntl = MC_CMD_FCNTL_BIDIR; |
1116 | break; |
1117 | case EFX_FC_RX: |
1118 | fcntl = MC_CMD_FCNTL_RESPOND; |
1119 | break; |
1120 | default: |
1121 | fcntl = MC_CMD_FCNTL_OFF; |
1122 | break; |
1123 | } |
1124 | if (efx->wanted_fc & EFX_FC_AUTO) |
1125 | fcntl = MC_CMD_FCNTL_AUTO; |
1126 | if (efx->fc_disable) |
1127 | fcntl = MC_CMD_FCNTL_OFF; |
1128 | |
1129 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl); |
1130 | |
1131 | return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, inbuf: cmdbytes, inlen: sizeof(cmdbytes), |
1132 | NULL, outlen: 0, NULL); |
1133 | } |
1134 | |
1135 | int efx_mcdi_set_mtu(struct efx_nic *efx) |
1136 | { |
1137 | MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_MAC_EXT_IN_LEN); |
1138 | |
1139 | BUILD_BUG_ON(MC_CMD_SET_MAC_OUT_LEN != 0); |
1140 | |
1141 | MCDI_SET_DWORD(inbuf, SET_MAC_EXT_IN_MTU, efx_calc_mac_mtu(efx)); |
1142 | |
1143 | MCDI_POPULATE_DWORD_1(inbuf, SET_MAC_EXT_IN_CONTROL, |
1144 | SET_MAC_EXT_IN_CFG_MTU, 1); |
1145 | |
1146 | return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, inbuf, inlen: sizeof(inbuf), |
1147 | NULL, outlen: 0, NULL); |
1148 | } |
1149 | |
1150 | enum efx_stats_action { |
1151 | EFX_STATS_ENABLE, |
1152 | EFX_STATS_DISABLE, |
1153 | EFX_STATS_PULL, |
1154 | }; |
1155 | |
1156 | static int efx_mcdi_mac_stats(struct efx_nic *efx, |
1157 | enum efx_stats_action action, int clear) |
1158 | { |
1159 | MCDI_DECLARE_BUF(inbuf, MC_CMD_MAC_STATS_IN_LEN); |
1160 | int rc; |
1161 | int change = action == EFX_STATS_PULL ? 0 : 1; |
1162 | int enable = action == EFX_STATS_ENABLE ? 1 : 0; |
1163 | int period = action == EFX_STATS_ENABLE ? 1000 : 0; |
1164 | dma_addr_t dma_addr = efx->stats_buffer.dma_addr; |
1165 | u32 dma_len = action != EFX_STATS_DISABLE ? |
1166 | efx->num_mac_stats * sizeof(u64) : 0; |
1167 | |
1168 | BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_DMA_LEN != 0); |
1169 | |
1170 | MCDI_SET_QWORD(inbuf, MAC_STATS_IN_DMA_ADDR, dma_addr); |
1171 | MCDI_POPULATE_DWORD_7(inbuf, MAC_STATS_IN_CMD, |
1172 | MAC_STATS_IN_DMA, !!enable, |
1173 | MAC_STATS_IN_CLEAR, clear, |
1174 | MAC_STATS_IN_PERIODIC_CHANGE, change, |
1175 | MAC_STATS_IN_PERIODIC_ENABLE, enable, |
1176 | MAC_STATS_IN_PERIODIC_CLEAR, 0, |
1177 | MAC_STATS_IN_PERIODIC_NOEVENT, 1, |
1178 | MAC_STATS_IN_PERIOD_MS, period); |
1179 | MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); |
1180 | |
1181 | if (efx_nic_rev(efx) >= EFX_REV_HUNT_A0) |
1182 | MCDI_SET_DWORD(inbuf, MAC_STATS_IN_PORT_ID, efx->vport_id); |
1183 | |
1184 | rc = efx_mcdi_rpc_quiet(efx, MC_CMD_MAC_STATS, inbuf, inlen: sizeof(inbuf), |
1185 | NULL, outlen: 0, NULL); |
1186 | /* Expect ENOENT if DMA queues have not been set up */ |
1187 | if (rc && (rc != -ENOENT || atomic_read(v: &efx->active_queues))) |
1188 | efx_mcdi_display_error(efx, MC_CMD_MAC_STATS, inlen: sizeof(inbuf), |
1189 | NULL, outlen: 0, rc); |
1190 | return rc; |
1191 | } |
1192 | |
1193 | void efx_mcdi_mac_start_stats(struct efx_nic *efx) |
1194 | { |
1195 | __le64 *dma_stats = efx->stats_buffer.addr; |
1196 | |
1197 | dma_stats[efx->num_mac_stats - 1] = EFX_MC_STATS_GENERATION_INVALID; |
1198 | |
1199 | efx_mcdi_mac_stats(efx, action: EFX_STATS_ENABLE, clear: 0); |
1200 | } |
1201 | |
1202 | void efx_mcdi_mac_stop_stats(struct efx_nic *efx) |
1203 | { |
1204 | efx_mcdi_mac_stats(efx, action: EFX_STATS_DISABLE, clear: 0); |
1205 | } |
1206 | |
1207 | #define EFX_MAC_STATS_WAIT_US 100 |
1208 | #define EFX_MAC_STATS_WAIT_ATTEMPTS 10 |
1209 | |
1210 | void efx_mcdi_mac_pull_stats(struct efx_nic *efx) |
1211 | { |
1212 | __le64 *dma_stats = efx->stats_buffer.addr; |
1213 | int attempts = EFX_MAC_STATS_WAIT_ATTEMPTS; |
1214 | |
1215 | dma_stats[efx->num_mac_stats - 1] = EFX_MC_STATS_GENERATION_INVALID; |
1216 | efx_mcdi_mac_stats(efx, action: EFX_STATS_PULL, clear: 0); |
1217 | |
1218 | while (dma_stats[efx->num_mac_stats - 1] == |
1219 | EFX_MC_STATS_GENERATION_INVALID && |
1220 | attempts-- != 0) |
1221 | udelay(EFX_MAC_STATS_WAIT_US); |
1222 | } |
1223 | |
1224 | int efx_mcdi_mac_init_stats(struct efx_nic *efx) |
1225 | { |
1226 | int rc; |
1227 | |
1228 | if (!efx->num_mac_stats) |
1229 | return 0; |
1230 | |
1231 | /* Allocate buffer for stats */ |
1232 | rc = efx_nic_alloc_buffer(efx, buffer: &efx->stats_buffer, |
1233 | len: efx->num_mac_stats * sizeof(u64), GFP_KERNEL); |
1234 | if (rc) { |
1235 | netif_warn(efx, probe, efx->net_dev, |
1236 | "failed to allocate DMA buffer: %d\n" , rc); |
1237 | return rc; |
1238 | } |
1239 | |
1240 | netif_dbg(efx, probe, efx->net_dev, |
1241 | "stats buffer at %llx (virt %p phys %llx)\n" , |
1242 | (u64) efx->stats_buffer.dma_addr, |
1243 | efx->stats_buffer.addr, |
1244 | (u64) virt_to_phys(efx->stats_buffer.addr)); |
1245 | |
1246 | return 0; |
1247 | } |
1248 | |
1249 | void efx_mcdi_mac_fini_stats(struct efx_nic *efx) |
1250 | { |
1251 | efx_nic_free_buffer(efx, buffer: &efx->stats_buffer); |
1252 | } |
1253 | |
1254 | /* Get physical port number (EF10 only; on Siena it is same as PF number) */ |
1255 | int efx_mcdi_port_get_number(struct efx_nic *efx) |
1256 | { |
1257 | MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PORT_ASSIGNMENT_OUT_LEN); |
1258 | int rc; |
1259 | |
1260 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_PORT_ASSIGNMENT, NULL, inlen: 0, |
1261 | outbuf, outlen: sizeof(outbuf), NULL); |
1262 | if (rc) |
1263 | return rc; |
1264 | |
1265 | return MCDI_DWORD(outbuf, GET_PORT_ASSIGNMENT_OUT_PORT); |
1266 | } |
1267 | |
1268 | static unsigned int efx_mcdi_event_link_speed[] = { |
1269 | [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100, |
1270 | [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000, |
1271 | [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000, |
1272 | [MCDI_EVENT_LINKCHANGE_SPEED_40G] = 40000, |
1273 | [MCDI_EVENT_LINKCHANGE_SPEED_25G] = 25000, |
1274 | [MCDI_EVENT_LINKCHANGE_SPEED_50G] = 50000, |
1275 | [MCDI_EVENT_LINKCHANGE_SPEED_100G] = 100000, |
1276 | }; |
1277 | |
1278 | void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) |
1279 | { |
1280 | u32 flags, fcntl, speed, lpa; |
1281 | |
1282 | speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); |
1283 | EFX_WARN_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); |
1284 | speed = efx_mcdi_event_link_speed[speed]; |
1285 | |
1286 | flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); |
1287 | fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL); |
1288 | lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP); |
1289 | |
1290 | /* efx->link_state is only modified by efx_mcdi_phy_get_link(), |
1291 | * which is only run after flushing the event queues. Therefore, it |
1292 | * is safe to modify the link state outside of the mac_lock here. |
1293 | */ |
1294 | efx_mcdi_phy_decode_link(efx, link_state: &efx->link_state, speed, flags, fcntl); |
1295 | |
1296 | efx_mcdi_phy_check_fcntl(efx, lpa); |
1297 | |
1298 | efx_link_status_changed(efx); |
1299 | } |
1300 | |