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