]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * | |
f67539c2 TL |
3 | * Copyright(c) 2019-2020 Xilinx, Inc. |
4 | * Copyright(c) 2012-2019 Solarflare Communications Inc. | |
11fdf7f2 TL |
5 | */ |
6 | ||
7 | #include "efx.h" | |
8 | #include "efx_impl.h" | |
9 | ||
9f95a23c | 10 | #if EFX_OPTS_EF10() |
11fdf7f2 TL |
11 | |
12 | static void | |
13 | mcdi_phy_decode_cap( | |
14 | __in uint32_t mcdi_cap, | |
15 | __out uint32_t *maskp) | |
16 | { | |
17 | uint32_t mask; | |
18 | ||
19 | #define CHECK_CAP(_cap) \ | |
20 | EFX_STATIC_ASSERT(EFX_PHY_CAP_##_cap == MC_CMD_PHY_CAP_##_cap##_LBN) | |
21 | ||
22 | CHECK_CAP(10HDX); | |
23 | CHECK_CAP(10FDX); | |
24 | CHECK_CAP(100HDX); | |
25 | CHECK_CAP(100FDX); | |
26 | CHECK_CAP(1000HDX); | |
27 | CHECK_CAP(1000FDX); | |
28 | CHECK_CAP(10000FDX); | |
29 | CHECK_CAP(25000FDX); | |
30 | CHECK_CAP(40000FDX); | |
31 | CHECK_CAP(50000FDX); | |
32 | CHECK_CAP(100000FDX); | |
33 | CHECK_CAP(PAUSE); | |
34 | CHECK_CAP(ASYM); | |
35 | CHECK_CAP(AN); | |
36 | CHECK_CAP(DDM); | |
37 | CHECK_CAP(BASER_FEC); | |
38 | CHECK_CAP(BASER_FEC_REQUESTED); | |
39 | CHECK_CAP(RS_FEC); | |
40 | CHECK_CAP(RS_FEC_REQUESTED); | |
41 | CHECK_CAP(25G_BASER_FEC); | |
42 | CHECK_CAP(25G_BASER_FEC_REQUESTED); | |
43 | #undef CHECK_CAP | |
44 | ||
45 | mask = 0; | |
46 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) | |
47 | mask |= (1 << EFX_PHY_CAP_10HDX); | |
48 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) | |
49 | mask |= (1 << EFX_PHY_CAP_10FDX); | |
50 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) | |
51 | mask |= (1 << EFX_PHY_CAP_100HDX); | |
52 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) | |
53 | mask |= (1 << EFX_PHY_CAP_100FDX); | |
54 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) | |
55 | mask |= (1 << EFX_PHY_CAP_1000HDX); | |
56 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) | |
57 | mask |= (1 << EFX_PHY_CAP_1000FDX); | |
58 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) | |
59 | mask |= (1 << EFX_PHY_CAP_10000FDX); | |
60 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_25000FDX_LBN)) | |
61 | mask |= (1 << EFX_PHY_CAP_25000FDX); | |
62 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN)) | |
63 | mask |= (1 << EFX_PHY_CAP_40000FDX); | |
64 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_50000FDX_LBN)) | |
65 | mask |= (1 << EFX_PHY_CAP_50000FDX); | |
66 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100000FDX_LBN)) | |
67 | mask |= (1 << EFX_PHY_CAP_100000FDX); | |
68 | ||
69 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) | |
70 | mask |= (1 << EFX_PHY_CAP_PAUSE); | |
71 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) | |
72 | mask |= (1 << EFX_PHY_CAP_ASYM); | |
73 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) | |
74 | mask |= (1 << EFX_PHY_CAP_AN); | |
75 | ||
76 | /* FEC caps (supported on Medford2 and later) */ | |
77 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_LBN)) | |
78 | mask |= (1 << EFX_PHY_CAP_BASER_FEC); | |
79 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_BASER_FEC_REQUESTED_LBN)) | |
80 | mask |= (1 << EFX_PHY_CAP_BASER_FEC_REQUESTED); | |
81 | ||
82 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_RS_FEC_LBN)) | |
83 | mask |= (1 << EFX_PHY_CAP_RS_FEC); | |
84 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_RS_FEC_REQUESTED_LBN)) | |
85 | mask |= (1 << EFX_PHY_CAP_RS_FEC_REQUESTED); | |
86 | ||
87 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_LBN)) | |
88 | mask |= (1 << EFX_PHY_CAP_25G_BASER_FEC); | |
89 | if (mcdi_cap & (1 << MC_CMD_PHY_CAP_25G_BASER_FEC_REQUESTED_LBN)) | |
90 | mask |= (1 << EFX_PHY_CAP_25G_BASER_FEC_REQUESTED); | |
91 | ||
92 | *maskp = mask; | |
93 | } | |
94 | ||
95 | static void | |
96 | mcdi_phy_decode_link_mode( | |
97 | __in efx_nic_t *enp, | |
98 | __in uint32_t link_flags, | |
99 | __in unsigned int speed, | |
100 | __in unsigned int fcntl, | |
9f95a23c | 101 | __in uint32_t fec, |
11fdf7f2 | 102 | __out efx_link_mode_t *link_modep, |
9f95a23c TL |
103 | __out unsigned int *fcntlp, |
104 | __out efx_phy_fec_type_t *fecp) | |
11fdf7f2 TL |
105 | { |
106 | boolean_t fd = !!(link_flags & | |
107 | (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN)); | |
108 | boolean_t up = !!(link_flags & | |
109 | (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN)); | |
110 | ||
111 | _NOTE(ARGUNUSED(enp)) | |
112 | ||
113 | if (!up) | |
114 | *link_modep = EFX_LINK_DOWN; | |
115 | else if (speed == 100000 && fd) | |
116 | *link_modep = EFX_LINK_100000FDX; | |
117 | else if (speed == 50000 && fd) | |
118 | *link_modep = EFX_LINK_50000FDX; | |
119 | else if (speed == 40000 && fd) | |
120 | *link_modep = EFX_LINK_40000FDX; | |
121 | else if (speed == 25000 && fd) | |
122 | *link_modep = EFX_LINK_25000FDX; | |
123 | else if (speed == 10000 && fd) | |
124 | *link_modep = EFX_LINK_10000FDX; | |
125 | else if (speed == 1000) | |
126 | *link_modep = fd ? EFX_LINK_1000FDX : EFX_LINK_1000HDX; | |
127 | else if (speed == 100) | |
128 | *link_modep = fd ? EFX_LINK_100FDX : EFX_LINK_100HDX; | |
129 | else if (speed == 10) | |
130 | *link_modep = fd ? EFX_LINK_10FDX : EFX_LINK_10HDX; | |
131 | else | |
132 | *link_modep = EFX_LINK_UNKNOWN; | |
133 | ||
134 | if (fcntl == MC_CMD_FCNTL_OFF) | |
135 | *fcntlp = 0; | |
136 | else if (fcntl == MC_CMD_FCNTL_RESPOND) | |
137 | *fcntlp = EFX_FCNTL_RESPOND; | |
138 | else if (fcntl == MC_CMD_FCNTL_GENERATE) | |
139 | *fcntlp = EFX_FCNTL_GENERATE; | |
140 | else if (fcntl == MC_CMD_FCNTL_BIDIR) | |
141 | *fcntlp = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE; | |
142 | else { | |
143 | EFSYS_PROBE1(mc_pcol_error, int, fcntl); | |
144 | *fcntlp = 0; | |
145 | } | |
9f95a23c TL |
146 | |
147 | switch (fec) { | |
148 | case MC_CMD_FEC_NONE: | |
149 | *fecp = EFX_PHY_FEC_NONE; | |
150 | break; | |
151 | case MC_CMD_FEC_BASER: | |
152 | *fecp = EFX_PHY_FEC_BASER; | |
153 | break; | |
154 | case MC_CMD_FEC_RS: | |
155 | *fecp = EFX_PHY_FEC_RS; | |
156 | break; | |
157 | default: | |
158 | EFSYS_PROBE1(mc_pcol_error, int, fec); | |
159 | *fecp = EFX_PHY_FEC_NONE; | |
160 | break; | |
161 | } | |
11fdf7f2 TL |
162 | } |
163 | ||
164 | ||
165 | void | |
166 | ef10_phy_link_ev( | |
167 | __in efx_nic_t *enp, | |
168 | __in efx_qword_t *eqp, | |
169 | __out efx_link_mode_t *link_modep) | |
170 | { | |
171 | efx_port_t *epp = &(enp->en_port); | |
172 | unsigned int link_flags; | |
173 | unsigned int speed; | |
174 | unsigned int fcntl; | |
9f95a23c | 175 | efx_phy_fec_type_t fec = MC_CMD_FEC_NONE; |
11fdf7f2 TL |
176 | efx_link_mode_t link_mode; |
177 | uint32_t lp_cap_mask; | |
178 | ||
179 | /* | |
180 | * Convert the LINKCHANGE speed enumeration into mbit/s, in the | |
181 | * same way as GET_LINK encodes the speed | |
182 | */ | |
183 | switch (MCDI_EV_FIELD(eqp, LINKCHANGE_SPEED)) { | |
184 | case MCDI_EVENT_LINKCHANGE_SPEED_100M: | |
185 | speed = 100; | |
186 | break; | |
187 | case MCDI_EVENT_LINKCHANGE_SPEED_1G: | |
188 | speed = 1000; | |
189 | break; | |
190 | case MCDI_EVENT_LINKCHANGE_SPEED_10G: | |
191 | speed = 10000; | |
192 | break; | |
193 | case MCDI_EVENT_LINKCHANGE_SPEED_25G: | |
194 | speed = 25000; | |
195 | break; | |
196 | case MCDI_EVENT_LINKCHANGE_SPEED_40G: | |
197 | speed = 40000; | |
198 | break; | |
199 | case MCDI_EVENT_LINKCHANGE_SPEED_50G: | |
200 | speed = 50000; | |
201 | break; | |
202 | case MCDI_EVENT_LINKCHANGE_SPEED_100G: | |
203 | speed = 100000; | |
204 | break; | |
205 | default: | |
206 | speed = 0; | |
207 | break; | |
208 | } | |
209 | ||
210 | link_flags = MCDI_EV_FIELD(eqp, LINKCHANGE_LINK_FLAGS); | |
211 | mcdi_phy_decode_link_mode(enp, link_flags, speed, | |
212 | MCDI_EV_FIELD(eqp, LINKCHANGE_FCNTL), | |
9f95a23c TL |
213 | MC_CMD_FEC_NONE, &link_mode, |
214 | &fcntl, &fec); | |
11fdf7f2 TL |
215 | mcdi_phy_decode_cap(MCDI_EV_FIELD(eqp, LINKCHANGE_LP_CAP), |
216 | &lp_cap_mask); | |
217 | ||
218 | /* | |
219 | * It's safe to update ep_lp_cap_mask without the driver's port lock | |
220 | * because presumably any concurrently running efx_port_poll() is | |
221 | * only going to arrive at the same value. | |
222 | * | |
223 | * ep_fcntl has two meanings. It's either the link common fcntl | |
224 | * (if the PHY supports AN), or it's the forced link state. If | |
225 | * the former, it's safe to update the value for the same reason as | |
226 | * for ep_lp_cap_mask. If the latter, then just ignore the value, | |
227 | * because we can race with efx_mac_fcntl_set(). | |
228 | */ | |
229 | epp->ep_lp_cap_mask = lp_cap_mask; | |
230 | epp->ep_fcntl = fcntl; | |
231 | ||
232 | *link_modep = link_mode; | |
233 | } | |
234 | ||
235 | __checkReturn efx_rc_t | |
236 | ef10_phy_power( | |
237 | __in efx_nic_t *enp, | |
238 | __in boolean_t power) | |
239 | { | |
240 | efx_rc_t rc; | |
241 | ||
242 | if (!power) | |
243 | return (0); | |
244 | ||
245 | /* Check if the PHY is a zombie */ | |
246 | if ((rc = ef10_phy_verify(enp)) != 0) | |
247 | goto fail1; | |
248 | ||
249 | enp->en_reset_flags |= EFX_RESET_PHY; | |
250 | ||
251 | return (0); | |
252 | ||
253 | fail1: | |
254 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
255 | ||
256 | return (rc); | |
257 | } | |
258 | ||
259 | __checkReturn efx_rc_t | |
260 | ef10_phy_get_link( | |
261 | __in efx_nic_t *enp, | |
262 | __out ef10_link_state_t *elsp) | |
263 | { | |
264 | efx_mcdi_req_t req; | |
9f95a23c TL |
265 | uint32_t fec; |
266 | EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_LINK_IN_LEN, | |
267 | MC_CMD_GET_LINK_OUT_V2_LEN); | |
11fdf7f2 TL |
268 | efx_rc_t rc; |
269 | ||
11fdf7f2 TL |
270 | req.emr_cmd = MC_CMD_GET_LINK; |
271 | req.emr_in_buf = payload; | |
272 | req.emr_in_length = MC_CMD_GET_LINK_IN_LEN; | |
273 | req.emr_out_buf = payload; | |
9f95a23c | 274 | req.emr_out_length = MC_CMD_GET_LINK_OUT_V2_LEN; |
11fdf7f2 TL |
275 | |
276 | efx_mcdi_execute(enp, &req); | |
277 | ||
278 | if (req.emr_rc != 0) { | |
279 | rc = req.emr_rc; | |
280 | goto fail1; | |
281 | } | |
282 | ||
283 | if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) { | |
284 | rc = EMSGSIZE; | |
285 | goto fail2; | |
286 | } | |
287 | ||
288 | mcdi_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_CAP), | |
9f95a23c | 289 | &elsp->epls.epls_adv_cap_mask); |
11fdf7f2 | 290 | mcdi_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_LP_CAP), |
9f95a23c TL |
291 | &elsp->epls.epls_lp_cap_mask); |
292 | ||
293 | if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_V2_LEN) | |
294 | fec = MC_CMD_FEC_NONE; | |
295 | else | |
296 | fec = MCDI_OUT_DWORD(req, GET_LINK_OUT_V2_FEC_TYPE); | |
11fdf7f2 TL |
297 | |
298 | mcdi_phy_decode_link_mode(enp, MCDI_OUT_DWORD(req, GET_LINK_OUT_FLAGS), | |
299 | MCDI_OUT_DWORD(req, GET_LINK_OUT_LINK_SPEED), | |
300 | MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL), | |
9f95a23c TL |
301 | fec, &elsp->epls.epls_link_mode, |
302 | &elsp->epls.epls_fcntl, &elsp->epls.epls_fec); | |
303 | ||
304 | if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_V2_LEN) { | |
305 | elsp->epls.epls_ld_cap_mask = 0; | |
306 | } else { | |
307 | mcdi_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_V2_LD_CAP), | |
308 | &elsp->epls.epls_ld_cap_mask); | |
309 | } | |
310 | ||
11fdf7f2 TL |
311 | |
312 | #if EFSYS_OPT_LOOPBACK | |
313 | /* | |
314 | * MC_CMD_LOOPBACK and EFX_LOOPBACK names are equivalent, so use the | |
315 | * MCDI value directly. Agreement is checked in efx_loopback_mask(). | |
316 | */ | |
317 | elsp->els_loopback = MCDI_OUT_DWORD(req, GET_LINK_OUT_LOOPBACK_MODE); | |
318 | #endif /* EFSYS_OPT_LOOPBACK */ | |
319 | ||
320 | elsp->els_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0; | |
321 | ||
322 | return (0); | |
323 | ||
324 | fail2: | |
325 | EFSYS_PROBE(fail2); | |
326 | fail1: | |
327 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
328 | ||
329 | return (rc); | |
330 | } | |
331 | ||
332 | __checkReturn efx_rc_t | |
333 | ef10_phy_reconfigure( | |
334 | __in efx_nic_t *enp) | |
335 | { | |
336 | efx_port_t *epp = &(enp->en_port); | |
337 | efx_mcdi_req_t req; | |
9f95a23c TL |
338 | EFX_MCDI_DECLARE_BUF(payload, MC_CMD_SET_LINK_IN_LEN, |
339 | MC_CMD_SET_LINK_OUT_LEN); | |
11fdf7f2 TL |
340 | uint32_t cap_mask; |
341 | #if EFSYS_OPT_PHY_LED_CONTROL | |
342 | unsigned int led_mode; | |
343 | #endif | |
344 | unsigned int speed; | |
345 | boolean_t supported; | |
346 | efx_rc_t rc; | |
347 | ||
348 | if ((rc = efx_mcdi_link_control_supported(enp, &supported)) != 0) | |
349 | goto fail1; | |
350 | if (supported == B_FALSE) | |
351 | goto out; | |
352 | ||
11fdf7f2 TL |
353 | req.emr_cmd = MC_CMD_SET_LINK; |
354 | req.emr_in_buf = payload; | |
355 | req.emr_in_length = MC_CMD_SET_LINK_IN_LEN; | |
356 | req.emr_out_buf = payload; | |
357 | req.emr_out_length = MC_CMD_SET_LINK_OUT_LEN; | |
358 | ||
359 | cap_mask = epp->ep_adv_cap_mask; | |
360 | MCDI_IN_POPULATE_DWORD_10(req, SET_LINK_IN_CAP, | |
361 | PHY_CAP_10HDX, (cap_mask >> EFX_PHY_CAP_10HDX) & 0x1, | |
362 | PHY_CAP_10FDX, (cap_mask >> EFX_PHY_CAP_10FDX) & 0x1, | |
363 | PHY_CAP_100HDX, (cap_mask >> EFX_PHY_CAP_100HDX) & 0x1, | |
364 | PHY_CAP_100FDX, (cap_mask >> EFX_PHY_CAP_100FDX) & 0x1, | |
365 | PHY_CAP_1000HDX, (cap_mask >> EFX_PHY_CAP_1000HDX) & 0x1, | |
366 | PHY_CAP_1000FDX, (cap_mask >> EFX_PHY_CAP_1000FDX) & 0x1, | |
367 | PHY_CAP_10000FDX, (cap_mask >> EFX_PHY_CAP_10000FDX) & 0x1, | |
368 | PHY_CAP_PAUSE, (cap_mask >> EFX_PHY_CAP_PAUSE) & 0x1, | |
369 | PHY_CAP_ASYM, (cap_mask >> EFX_PHY_CAP_ASYM) & 0x1, | |
370 | PHY_CAP_AN, (cap_mask >> EFX_PHY_CAP_AN) & 0x1); | |
371 | /* Too many fields for for POPULATE macros, so insert this afterwards */ | |
372 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
373 | PHY_CAP_25000FDX, (cap_mask >> EFX_PHY_CAP_25000FDX) & 0x1); | |
374 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
375 | PHY_CAP_40000FDX, (cap_mask >> EFX_PHY_CAP_40000FDX) & 0x1); | |
376 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
377 | PHY_CAP_50000FDX, (cap_mask >> EFX_PHY_CAP_50000FDX) & 0x1); | |
378 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
379 | PHY_CAP_100000FDX, (cap_mask >> EFX_PHY_CAP_100000FDX) & 0x1); | |
380 | ||
381 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
382 | PHY_CAP_BASER_FEC, (cap_mask >> EFX_PHY_CAP_BASER_FEC) & 0x1); | |
383 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
384 | PHY_CAP_BASER_FEC_REQUESTED, | |
385 | (cap_mask >> EFX_PHY_CAP_BASER_FEC_REQUESTED) & 0x1); | |
386 | ||
387 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
388 | PHY_CAP_RS_FEC, (cap_mask >> EFX_PHY_CAP_RS_FEC) & 0x1); | |
389 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
390 | PHY_CAP_RS_FEC_REQUESTED, | |
391 | (cap_mask >> EFX_PHY_CAP_RS_FEC_REQUESTED) & 0x1); | |
392 | ||
393 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
394 | PHY_CAP_25G_BASER_FEC, | |
395 | (cap_mask >> EFX_PHY_CAP_25G_BASER_FEC) & 0x1); | |
396 | MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP, | |
397 | PHY_CAP_25G_BASER_FEC_REQUESTED, | |
398 | (cap_mask >> EFX_PHY_CAP_25G_BASER_FEC_REQUESTED) & 0x1); | |
399 | ||
400 | #if EFSYS_OPT_LOOPBACK | |
401 | MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, | |
402 | epp->ep_loopback_type); | |
403 | switch (epp->ep_loopback_link_mode) { | |
404 | case EFX_LINK_100FDX: | |
405 | speed = 100; | |
406 | break; | |
407 | case EFX_LINK_1000FDX: | |
408 | speed = 1000; | |
409 | break; | |
410 | case EFX_LINK_10000FDX: | |
411 | speed = 10000; | |
412 | break; | |
413 | case EFX_LINK_25000FDX: | |
414 | speed = 25000; | |
415 | break; | |
416 | case EFX_LINK_40000FDX: | |
417 | speed = 40000; | |
418 | break; | |
419 | case EFX_LINK_50000FDX: | |
420 | speed = 50000; | |
421 | break; | |
422 | case EFX_LINK_100000FDX: | |
423 | speed = 100000; | |
424 | break; | |
425 | default: | |
426 | speed = 0; | |
427 | } | |
428 | #else | |
429 | MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE); | |
430 | speed = 0; | |
431 | #endif /* EFSYS_OPT_LOOPBACK */ | |
432 | MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed); | |
433 | ||
434 | #if EFSYS_OPT_PHY_FLAGS | |
435 | MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, epp->ep_phy_flags); | |
436 | #else | |
437 | MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, 0); | |
438 | #endif /* EFSYS_OPT_PHY_FLAGS */ | |
439 | ||
440 | efx_mcdi_execute(enp, &req); | |
441 | ||
442 | if (req.emr_rc != 0) { | |
443 | rc = req.emr_rc; | |
444 | goto fail2; | |
445 | } | |
446 | ||
447 | /* And set the blink mode */ | |
448 | (void) memset(payload, 0, sizeof (payload)); | |
449 | req.emr_cmd = MC_CMD_SET_ID_LED; | |
450 | req.emr_in_buf = payload; | |
451 | req.emr_in_length = MC_CMD_SET_ID_LED_IN_LEN; | |
452 | req.emr_out_buf = payload; | |
453 | req.emr_out_length = MC_CMD_SET_ID_LED_OUT_LEN; | |
454 | ||
455 | #if EFSYS_OPT_PHY_LED_CONTROL | |
456 | switch (epp->ep_phy_led_mode) { | |
457 | case EFX_PHY_LED_DEFAULT: | |
458 | led_mode = MC_CMD_LED_DEFAULT; | |
459 | break; | |
460 | case EFX_PHY_LED_OFF: | |
461 | led_mode = MC_CMD_LED_OFF; | |
462 | break; | |
463 | case EFX_PHY_LED_ON: | |
464 | led_mode = MC_CMD_LED_ON; | |
465 | break; | |
466 | default: | |
467 | EFSYS_ASSERT(0); | |
468 | led_mode = MC_CMD_LED_DEFAULT; | |
469 | } | |
470 | ||
471 | MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, led_mode); | |
472 | #else | |
473 | MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, MC_CMD_LED_DEFAULT); | |
474 | #endif /* EFSYS_OPT_PHY_LED_CONTROL */ | |
475 | ||
476 | efx_mcdi_execute(enp, &req); | |
477 | ||
478 | if (req.emr_rc != 0) { | |
479 | rc = req.emr_rc; | |
480 | goto fail3; | |
481 | } | |
482 | out: | |
483 | return (0); | |
484 | ||
485 | fail3: | |
486 | EFSYS_PROBE(fail3); | |
487 | fail2: | |
488 | EFSYS_PROBE(fail2); | |
489 | fail1: | |
490 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
491 | ||
492 | return (rc); | |
493 | } | |
494 | ||
495 | __checkReturn efx_rc_t | |
496 | ef10_phy_verify( | |
497 | __in efx_nic_t *enp) | |
498 | { | |
499 | efx_mcdi_req_t req; | |
9f95a23c TL |
500 | EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_STATE_IN_LEN, |
501 | MC_CMD_GET_PHY_STATE_OUT_LEN); | |
11fdf7f2 TL |
502 | uint32_t state; |
503 | efx_rc_t rc; | |
504 | ||
11fdf7f2 TL |
505 | req.emr_cmd = MC_CMD_GET_PHY_STATE; |
506 | req.emr_in_buf = payload; | |
507 | req.emr_in_length = MC_CMD_GET_PHY_STATE_IN_LEN; | |
508 | req.emr_out_buf = payload; | |
509 | req.emr_out_length = MC_CMD_GET_PHY_STATE_OUT_LEN; | |
510 | ||
511 | efx_mcdi_execute(enp, &req); | |
512 | ||
513 | if (req.emr_rc != 0) { | |
514 | rc = req.emr_rc; | |
515 | goto fail1; | |
516 | } | |
517 | ||
518 | if (req.emr_out_length_used < MC_CMD_GET_PHY_STATE_OUT_LEN) { | |
519 | rc = EMSGSIZE; | |
520 | goto fail2; | |
521 | } | |
522 | ||
523 | state = MCDI_OUT_DWORD(req, GET_PHY_STATE_OUT_STATE); | |
524 | if (state != MC_CMD_PHY_STATE_OK) { | |
525 | if (state != MC_CMD_PHY_STATE_ZOMBIE) | |
526 | EFSYS_PROBE1(mc_pcol_error, int, state); | |
527 | rc = ENOTACTIVE; | |
528 | goto fail3; | |
529 | } | |
530 | ||
531 | return (0); | |
532 | ||
533 | fail3: | |
534 | EFSYS_PROBE(fail3); | |
535 | fail2: | |
536 | EFSYS_PROBE(fail2); | |
537 | fail1: | |
538 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
539 | ||
540 | return (rc); | |
541 | } | |
542 | ||
543 | __checkReturn efx_rc_t | |
544 | ef10_phy_oui_get( | |
545 | __in efx_nic_t *enp, | |
546 | __out uint32_t *ouip) | |
547 | { | |
548 | _NOTE(ARGUNUSED(enp, ouip)) | |
549 | ||
550 | return (ENOTSUP); | |
551 | } | |
552 | ||
9f95a23c TL |
553 | __checkReturn efx_rc_t |
554 | ef10_phy_link_state_get( | |
555 | __in efx_nic_t *enp, | |
556 | __out efx_phy_link_state_t *eplsp) | |
557 | { | |
558 | efx_rc_t rc; | |
559 | ef10_link_state_t els; | |
560 | ||
561 | /* Obtain the active link state */ | |
562 | if ((rc = ef10_phy_get_link(enp, &els)) != 0) | |
563 | goto fail1; | |
564 | ||
565 | *eplsp = els.epls; | |
566 | ||
567 | return (0); | |
568 | ||
569 | fail1: | |
570 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
571 | ||
572 | return (rc); | |
573 | } | |
574 | ||
575 | ||
11fdf7f2 TL |
576 | #if EFSYS_OPT_PHY_STATS |
577 | ||
578 | __checkReturn efx_rc_t | |
579 | ef10_phy_stats_update( | |
580 | __in efx_nic_t *enp, | |
581 | __in efsys_mem_t *esmp, | |
582 | __inout_ecount(EFX_PHY_NSTATS) uint32_t *stat) | |
583 | { | |
584 | /* TBD: no stats support in firmware yet */ | |
585 | _NOTE(ARGUNUSED(enp, esmp)) | |
586 | memset(stat, 0, EFX_PHY_NSTATS * sizeof (*stat)); | |
587 | ||
588 | return (0); | |
589 | } | |
590 | ||
591 | #endif /* EFSYS_OPT_PHY_STATS */ | |
592 | ||
593 | #if EFSYS_OPT_BIST | |
594 | ||
595 | __checkReturn efx_rc_t | |
596 | ef10_bist_enable_offline( | |
597 | __in efx_nic_t *enp) | |
598 | { | |
599 | efx_rc_t rc; | |
600 | ||
601 | if ((rc = efx_mcdi_bist_enable_offline(enp)) != 0) | |
602 | goto fail1; | |
603 | ||
604 | return (0); | |
605 | ||
606 | fail1: | |
607 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
608 | ||
609 | return (rc); | |
610 | } | |
611 | ||
612 | __checkReturn efx_rc_t | |
613 | ef10_bist_start( | |
614 | __in efx_nic_t *enp, | |
615 | __in efx_bist_type_t type) | |
616 | { | |
617 | efx_rc_t rc; | |
618 | ||
619 | if ((rc = efx_mcdi_bist_start(enp, type)) != 0) | |
620 | goto fail1; | |
621 | ||
622 | return (0); | |
623 | ||
624 | fail1: | |
625 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
626 | ||
627 | return (rc); | |
628 | } | |
629 | ||
630 | __checkReturn efx_rc_t | |
631 | ef10_bist_poll( | |
632 | __in efx_nic_t *enp, | |
633 | __in efx_bist_type_t type, | |
634 | __out efx_bist_result_t *resultp, | |
635 | __out_opt __drv_when(count > 0, __notnull) | |
636 | uint32_t *value_maskp, | |
637 | __out_ecount_opt(count) __drv_when(count > 0, __notnull) | |
638 | unsigned long *valuesp, | |
639 | __in size_t count) | |
640 | { | |
9f95a23c TL |
641 | /* |
642 | * MCDI_CTL_SDU_LEN_MAX_V1 is large enough cover all BIST results, | |
643 | * whilst not wasting stack. | |
644 | */ | |
645 | EFX_MCDI_DECLARE_BUF(payload, MC_CMD_POLL_BIST_IN_LEN, | |
646 | MCDI_CTL_SDU_LEN_MAX_V1); | |
11fdf7f2 TL |
647 | efx_nic_cfg_t *encp = &(enp->en_nic_cfg); |
648 | efx_mcdi_req_t req; | |
11fdf7f2 TL |
649 | uint32_t value_mask = 0; |
650 | uint32_t result; | |
651 | efx_rc_t rc; | |
652 | ||
9f95a23c TL |
653 | EFX_STATIC_ASSERT(MC_CMD_POLL_BIST_OUT_LEN <= |
654 | MCDI_CTL_SDU_LEN_MAX_V1); | |
655 | EFX_STATIC_ASSERT(MC_CMD_POLL_BIST_OUT_SFT9001_LEN <= | |
656 | MCDI_CTL_SDU_LEN_MAX_V1); | |
657 | EFX_STATIC_ASSERT(MC_CMD_POLL_BIST_OUT_MRSFP_LEN <= | |
658 | MCDI_CTL_SDU_LEN_MAX_V1); | |
659 | EFX_STATIC_ASSERT(MC_CMD_POLL_BIST_OUT_MEM_LEN <= | |
660 | MCDI_CTL_SDU_LEN_MAX_V1); | |
661 | ||
11fdf7f2 TL |
662 | _NOTE(ARGUNUSED(type)) |
663 | ||
11fdf7f2 TL |
664 | req.emr_cmd = MC_CMD_POLL_BIST; |
665 | req.emr_in_buf = payload; | |
666 | req.emr_in_length = MC_CMD_POLL_BIST_IN_LEN; | |
667 | req.emr_out_buf = payload; | |
9f95a23c | 668 | req.emr_out_length = MCDI_CTL_SDU_LEN_MAX_V1; |
11fdf7f2 TL |
669 | |
670 | efx_mcdi_execute(enp, &req); | |
671 | ||
672 | if (req.emr_rc != 0) { | |
673 | rc = req.emr_rc; | |
674 | goto fail1; | |
675 | } | |
676 | ||
677 | if (req.emr_out_length_used < MC_CMD_POLL_BIST_OUT_RESULT_OFST + 4) { | |
678 | rc = EMSGSIZE; | |
679 | goto fail2; | |
680 | } | |
681 | ||
682 | if (count > 0) | |
683 | (void) memset(valuesp, '\0', count * sizeof (unsigned long)); | |
684 | ||
685 | result = MCDI_OUT_DWORD(req, POLL_BIST_OUT_RESULT); | |
686 | ||
687 | if (result == MC_CMD_POLL_BIST_FAILED && | |
688 | req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MEM_LEN && | |
689 | count > EFX_BIST_MEM_ECC_FATAL) { | |
690 | if (valuesp != NULL) { | |
691 | valuesp[EFX_BIST_MEM_TEST] = | |
692 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_TEST); | |
693 | valuesp[EFX_BIST_MEM_ADDR] = | |
694 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ADDR); | |
695 | valuesp[EFX_BIST_MEM_BUS] = | |
696 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_BUS); | |
697 | valuesp[EFX_BIST_MEM_EXPECT] = | |
698 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_EXPECT); | |
699 | valuesp[EFX_BIST_MEM_ACTUAL] = | |
700 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ACTUAL); | |
701 | valuesp[EFX_BIST_MEM_ECC] = | |
702 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC); | |
703 | valuesp[EFX_BIST_MEM_ECC_PARITY] = | |
704 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_PARITY); | |
705 | valuesp[EFX_BIST_MEM_ECC_FATAL] = | |
706 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_FATAL); | |
707 | } | |
708 | value_mask |= (1 << EFX_BIST_MEM_TEST) | | |
709 | (1 << EFX_BIST_MEM_ADDR) | | |
710 | (1 << EFX_BIST_MEM_BUS) | | |
711 | (1 << EFX_BIST_MEM_EXPECT) | | |
712 | (1 << EFX_BIST_MEM_ACTUAL) | | |
713 | (1 << EFX_BIST_MEM_ECC) | | |
714 | (1 << EFX_BIST_MEM_ECC_PARITY) | | |
715 | (1 << EFX_BIST_MEM_ECC_FATAL); | |
716 | } else if (result == MC_CMD_POLL_BIST_FAILED && | |
717 | encp->enc_phy_type == EFX_PHY_XFI_FARMI && | |
718 | req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MRSFP_LEN && | |
719 | count > EFX_BIST_FAULT_CODE) { | |
720 | if (valuesp != NULL) | |
721 | valuesp[EFX_BIST_FAULT_CODE] = | |
722 | MCDI_OUT_DWORD(req, POLL_BIST_OUT_MRSFP_TEST); | |
723 | value_mask |= 1 << EFX_BIST_FAULT_CODE; | |
724 | } | |
725 | ||
726 | if (value_maskp != NULL) | |
727 | *value_maskp = value_mask; | |
728 | ||
729 | EFSYS_ASSERT(resultp != NULL); | |
730 | if (result == MC_CMD_POLL_BIST_RUNNING) | |
731 | *resultp = EFX_BIST_RESULT_RUNNING; | |
732 | else if (result == MC_CMD_POLL_BIST_PASSED) | |
733 | *resultp = EFX_BIST_RESULT_PASSED; | |
734 | else | |
735 | *resultp = EFX_BIST_RESULT_FAILED; | |
736 | ||
737 | return (0); | |
738 | ||
739 | fail2: | |
740 | EFSYS_PROBE(fail2); | |
741 | fail1: | |
742 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
743 | ||
744 | return (rc); | |
745 | } | |
746 | ||
747 | void | |
748 | ef10_bist_stop( | |
749 | __in efx_nic_t *enp, | |
750 | __in efx_bist_type_t type) | |
751 | { | |
752 | /* There is no way to stop BIST on EF10. */ | |
753 | _NOTE(ARGUNUSED(enp, type)) | |
754 | } | |
755 | ||
756 | #endif /* EFSYS_OPT_BIST */ | |
757 | ||
9f95a23c | 758 | #endif /* EFX_OPTS_EF10() */ |