1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008-2016 Solarflare Communications Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
31 */
32
33 #include <sys/cdefs.h>
34 #include "efx.h"
35 #include "efx_impl.h"
36
37 #if EFSYS_OPT_MCDI
38
39 /*
40 * There are three versions of the MCDI interface:
41 * - MCDIv0: Siena BootROM. Transport uses MCDIv1 headers.
42 * - MCDIv1: Siena firmware and Huntington BootROM.
43 * - MCDIv2: EF10 firmware (Huntington/Medford) and Medford BootROM.
44 * Transport uses MCDIv2 headers.
45 *
46 * MCDIv2 Header NOT_EPOCH flag
47 * ----------------------------
48 * A new epoch begins at initial startup or after an MC reboot, and defines when
49 * the MC should reject stale MCDI requests.
50 *
51 * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
52 * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
53 *
54 * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
55 * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
56 */
57
58 #if EFSYS_OPT_SIENA
59
60 static const efx_mcdi_ops_t __efx_mcdi_siena_ops = {
61 siena_mcdi_init, /* emco_init */
62 siena_mcdi_send_request, /* emco_send_request */
63 siena_mcdi_poll_reboot, /* emco_poll_reboot */
64 siena_mcdi_poll_response, /* emco_poll_response */
65 siena_mcdi_read_response, /* emco_read_response */
66 siena_mcdi_fini, /* emco_fini */
67 siena_mcdi_feature_supported, /* emco_feature_supported */
68 siena_mcdi_get_timeout, /* emco_get_timeout */
69 };
70
71 #endif /* EFSYS_OPT_SIENA */
72
73 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
74
75 static const efx_mcdi_ops_t __efx_mcdi_ef10_ops = {
76 ef10_mcdi_init, /* emco_init */
77 ef10_mcdi_send_request, /* emco_send_request */
78 ef10_mcdi_poll_reboot, /* emco_poll_reboot */
79 ef10_mcdi_poll_response, /* emco_poll_response */
80 ef10_mcdi_read_response, /* emco_read_response */
81 ef10_mcdi_fini, /* emco_fini */
82 ef10_mcdi_feature_supported, /* emco_feature_supported */
83 ef10_mcdi_get_timeout, /* emco_get_timeout */
84 };
85
86 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
87
88 __checkReturn efx_rc_t
efx_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * emtp)89 efx_mcdi_init(
90 __in efx_nic_t *enp,
91 __in const efx_mcdi_transport_t *emtp)
92 {
93 const efx_mcdi_ops_t *emcop;
94 efx_rc_t rc;
95
96 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
97 EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
98
99 switch (enp->en_family) {
100 #if EFSYS_OPT_SIENA
101 case EFX_FAMILY_SIENA:
102 emcop = &__efx_mcdi_siena_ops;
103 break;
104 #endif /* EFSYS_OPT_SIENA */
105
106 #if EFSYS_OPT_HUNTINGTON
107 case EFX_FAMILY_HUNTINGTON:
108 emcop = &__efx_mcdi_ef10_ops;
109 break;
110 #endif /* EFSYS_OPT_HUNTINGTON */
111
112 #if EFSYS_OPT_MEDFORD
113 case EFX_FAMILY_MEDFORD:
114 emcop = &__efx_mcdi_ef10_ops;
115 break;
116 #endif /* EFSYS_OPT_MEDFORD */
117
118 #if EFSYS_OPT_MEDFORD2
119 case EFX_FAMILY_MEDFORD2:
120 emcop = &__efx_mcdi_ef10_ops;
121 break;
122 #endif /* EFSYS_OPT_MEDFORD2 */
123
124 default:
125 EFSYS_ASSERT(0);
126 rc = ENOTSUP;
127 goto fail1;
128 }
129
130 if (enp->en_features & EFX_FEATURE_MCDI_DMA) {
131 /* MCDI requires a DMA buffer in host memory */
132 if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) {
133 rc = EINVAL;
134 goto fail2;
135 }
136 }
137 enp->en_mcdi.em_emtp = emtp;
138
139 if (emcop != NULL && emcop->emco_init != NULL) {
140 if ((rc = emcop->emco_init(enp, emtp)) != 0)
141 goto fail3;
142 }
143
144 enp->en_mcdi.em_emcop = emcop;
145 enp->en_mod_flags |= EFX_MOD_MCDI;
146
147 return (0);
148
149 fail3:
150 EFSYS_PROBE(fail3);
151 fail2:
152 EFSYS_PROBE(fail2);
153 fail1:
154 EFSYS_PROBE1(fail1, efx_rc_t, rc);
155
156 enp->en_mcdi.em_emcop = NULL;
157 enp->en_mcdi.em_emtp = NULL;
158 enp->en_mod_flags &= ~EFX_MOD_MCDI;
159
160 return (rc);
161 }
162
163 void
efx_mcdi_fini(__in efx_nic_t * enp)164 efx_mcdi_fini(
165 __in efx_nic_t *enp)
166 {
167 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
168 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
169
170 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
171 EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
172
173 if (emcop != NULL && emcop->emco_fini != NULL)
174 emcop->emco_fini(enp);
175
176 emip->emi_port = 0;
177 emip->emi_aborted = 0;
178
179 enp->en_mcdi.em_emcop = NULL;
180 enp->en_mod_flags &= ~EFX_MOD_MCDI;
181 }
182
183 void
efx_mcdi_new_epoch(__in efx_nic_t * enp)184 efx_mcdi_new_epoch(
185 __in efx_nic_t *enp)
186 {
187 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
188 efsys_lock_state_t state;
189
190 /* Start a new epoch (allow fresh MCDI requests to succeed) */
191 EFSYS_LOCK(enp->en_eslp, state);
192 emip->emi_new_epoch = B_TRUE;
193 EFSYS_UNLOCK(enp->en_eslp, state);
194 }
195
196 static void
efx_mcdi_send_request(__in efx_nic_t * enp,__in void * hdrp,__in size_t hdr_len,__in void * sdup,__in size_t sdu_len)197 efx_mcdi_send_request(
198 __in efx_nic_t *enp,
199 __in void *hdrp,
200 __in size_t hdr_len,
201 __in void *sdup,
202 __in size_t sdu_len)
203 {
204 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
205
206 emcop->emco_send_request(enp, hdrp, hdr_len, sdup, sdu_len);
207 }
208
209 static efx_rc_t
efx_mcdi_poll_reboot(__in efx_nic_t * enp)210 efx_mcdi_poll_reboot(
211 __in efx_nic_t *enp)
212 {
213 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
214 efx_rc_t rc;
215
216 rc = emcop->emco_poll_reboot(enp);
217 return (rc);
218 }
219
220 static boolean_t
efx_mcdi_poll_response(__in efx_nic_t * enp)221 efx_mcdi_poll_response(
222 __in efx_nic_t *enp)
223 {
224 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
225 boolean_t available;
226
227 available = emcop->emco_poll_response(enp);
228 return (available);
229 }
230
231 static void
efx_mcdi_read_response(__in efx_nic_t * enp,__out void * bufferp,__in size_t offset,__in size_t length)232 efx_mcdi_read_response(
233 __in efx_nic_t *enp,
234 __out void *bufferp,
235 __in size_t offset,
236 __in size_t length)
237 {
238 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
239
240 emcop->emco_read_response(enp, bufferp, offset, length);
241 }
242
243 void
efx_mcdi_request_start(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__in boolean_t ev_cpl)244 efx_mcdi_request_start(
245 __in efx_nic_t *enp,
246 __in efx_mcdi_req_t *emrp,
247 __in boolean_t ev_cpl)
248 {
249 #if EFSYS_OPT_MCDI_LOGGING
250 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
251 #endif
252 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
253 efx_dword_t hdr[2];
254 size_t hdr_len;
255 unsigned int max_version;
256 unsigned int seq;
257 unsigned int xflags;
258 boolean_t new_epoch;
259 efsys_lock_state_t state;
260
261 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
262 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
263 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
264
265 /*
266 * efx_mcdi_request_start() is naturally serialised against both
267 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
268 * by virtue of there only being one outstanding MCDI request.
269 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
270 * at any time, to timeout a pending mcdi request, That request may
271 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
272 * efx_mcdi_ev_death() may end up running in parallel with
273 * efx_mcdi_request_start(). This race is handled by ensuring that
274 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
275 * en_eslp lock.
276 */
277 EFSYS_LOCK(enp->en_eslp, state);
278 EFSYS_ASSERT(emip->emi_pending_req == NULL);
279 emip->emi_pending_req = emrp;
280 emip->emi_ev_cpl = ev_cpl;
281 emip->emi_poll_cnt = 0;
282 seq = emip->emi_seq++ & EFX_MASK32(MCDI_HEADER_SEQ);
283 new_epoch = emip->emi_new_epoch;
284 max_version = emip->emi_max_version;
285 EFSYS_UNLOCK(enp->en_eslp, state);
286
287 xflags = 0;
288 if (ev_cpl)
289 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
290
291 /*
292 * Huntington firmware supports MCDIv2, but the Huntington BootROM only
293 * supports MCDIv1. Use MCDIv1 headers for MCDIv1 commands where
294 * possible to support this.
295 */
296 if ((max_version >= 2) &&
297 ((emrp->emr_cmd > MC_CMD_CMD_SPACE_ESCAPE_7) ||
298 (emrp->emr_in_length > MCDI_CTL_SDU_LEN_MAX_V1) ||
299 (emrp->emr_out_length > MCDI_CTL_SDU_LEN_MAX_V1))) {
300 /* Construct MCDI v2 header */
301 hdr_len = sizeof (hdr);
302 EFX_POPULATE_DWORD_8(hdr[0],
303 MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
304 MCDI_HEADER_RESYNC, 1,
305 MCDI_HEADER_DATALEN, 0,
306 MCDI_HEADER_SEQ, seq,
307 MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
308 MCDI_HEADER_ERROR, 0,
309 MCDI_HEADER_RESPONSE, 0,
310 MCDI_HEADER_XFLAGS, xflags);
311
312 EFX_POPULATE_DWORD_2(hdr[1],
313 MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
314 MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
315 } else {
316 /* Construct MCDI v1 header */
317 hdr_len = sizeof (hdr[0]);
318 EFX_POPULATE_DWORD_8(hdr[0],
319 MCDI_HEADER_CODE, emrp->emr_cmd,
320 MCDI_HEADER_RESYNC, 1,
321 MCDI_HEADER_DATALEN, emrp->emr_in_length,
322 MCDI_HEADER_SEQ, seq,
323 MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
324 MCDI_HEADER_ERROR, 0,
325 MCDI_HEADER_RESPONSE, 0,
326 MCDI_HEADER_XFLAGS, xflags);
327 }
328
329 #if EFSYS_OPT_MCDI_LOGGING
330 if (emtp->emt_logger != NULL) {
331 emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
332 &hdr, hdr_len,
333 emrp->emr_in_buf, emrp->emr_in_length);
334 }
335 #endif /* EFSYS_OPT_MCDI_LOGGING */
336
337 efx_mcdi_send_request(enp, &hdr[0], hdr_len,
338 emrp->emr_in_buf, emrp->emr_in_length);
339 }
340
341 static void
efx_mcdi_read_response_header(__in efx_nic_t * enp,__inout efx_mcdi_req_t * emrp)342 efx_mcdi_read_response_header(
343 __in efx_nic_t *enp,
344 __inout efx_mcdi_req_t *emrp)
345 {
346 #if EFSYS_OPT_MCDI_LOGGING
347 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
348 #endif /* EFSYS_OPT_MCDI_LOGGING */
349 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
350 efx_dword_t hdr[2];
351 unsigned int hdr_len;
352 unsigned int data_len;
353 unsigned int seq;
354 unsigned int cmd;
355 unsigned int error;
356 efx_rc_t rc;
357
358 EFSYS_ASSERT(emrp != NULL);
359
360 efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
361 hdr_len = sizeof (hdr[0]);
362
363 cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
364 seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
365 error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);
366
367 if (cmd != MC_CMD_V2_EXTN) {
368 data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
369 } else {
370 efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
371 hdr_len += sizeof (hdr[1]);
372
373 cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
374 data_len =
375 EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
376 }
377
378 if (error && (data_len == 0)) {
379 /* The MC has rebooted since the request was sent. */
380 EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
381 efx_mcdi_poll_reboot(enp);
382 rc = EIO;
383 goto fail1;
384 }
385 if ((cmd != emrp->emr_cmd) ||
386 (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
387 /* Response is for a different request */
388 rc = EIO;
389 goto fail2;
390 }
391 if (error) {
392 efx_dword_t err[2];
393 unsigned int err_len = MIN(data_len, sizeof (err));
394 int err_code = MC_CMD_ERR_EPROTO;
395 int err_arg = 0;
396
397 /* Read error code (and arg num for MCDI v2 commands) */
398 efx_mcdi_read_response(enp, &err, hdr_len, err_len);
399
400 if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
401 err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
402 #ifdef WITH_MCDI_V2
403 if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
404 err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
405 #endif
406 emrp->emr_err_code = err_code;
407 emrp->emr_err_arg = err_arg;
408
409 #if EFSYS_OPT_MCDI_PROXY_AUTH
410 if ((err_code == MC_CMD_ERR_PROXY_PENDING) &&
411 (err_len == sizeof (err))) {
412 /*
413 * The MCDI request would normally fail with EPERM, but
414 * firmware has forwarded it to an authorization agent
415 * attached to a privileged PF.
416 *
417 * Save the authorization request handle. The client
418 * must wait for a PROXY_RESPONSE event, or timeout.
419 */
420 emrp->emr_proxy_handle = err_arg;
421 }
422 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
423
424 #if EFSYS_OPT_MCDI_LOGGING
425 if (emtp->emt_logger != NULL) {
426 emtp->emt_logger(emtp->emt_context,
427 EFX_LOG_MCDI_RESPONSE,
428 &hdr, hdr_len,
429 &err, err_len);
430 }
431 #endif /* EFSYS_OPT_MCDI_LOGGING */
432
433 if (!emrp->emr_quiet) {
434 EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
435 int, err_code, int, err_arg);
436 }
437
438 rc = efx_mcdi_request_errcode(err_code);
439 goto fail3;
440 }
441
442 emrp->emr_rc = 0;
443 emrp->emr_out_length_used = data_len;
444 #if EFSYS_OPT_MCDI_PROXY_AUTH
445 emrp->emr_proxy_handle = 0;
446 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
447 return;
448
449 fail3:
450 fail2:
451 fail1:
452 emrp->emr_rc = rc;
453 emrp->emr_out_length_used = 0;
454 }
455
456 static void
efx_mcdi_finish_response(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp)457 efx_mcdi_finish_response(
458 __in efx_nic_t *enp,
459 __in efx_mcdi_req_t *emrp)
460 {
461 #if EFSYS_OPT_MCDI_LOGGING
462 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
463 #endif /* EFSYS_OPT_MCDI_LOGGING */
464 efx_dword_t hdr[2];
465 unsigned int hdr_len;
466 size_t bytes;
467
468 if (emrp->emr_out_buf == NULL)
469 return;
470
471 /* Read the command header to detect MCDI response format */
472 hdr_len = sizeof (hdr[0]);
473 efx_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
474 if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
475 /*
476 * Read the actual payload length. The length given in the event
477 * is only correct for responses with the V1 format.
478 */
479 efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
480 hdr_len += sizeof (hdr[1]);
481
482 emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
483 MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
484 }
485
486 /* Copy payload out into caller supplied buffer */
487 bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
488 efx_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
489
490 #if EFSYS_OPT_MCDI_LOGGING
491 if (emtp->emt_logger != NULL) {
492 emtp->emt_logger(emtp->emt_context,
493 EFX_LOG_MCDI_RESPONSE,
494 &hdr, hdr_len,
495 emrp->emr_out_buf, bytes);
496 }
497 #endif /* EFSYS_OPT_MCDI_LOGGING */
498 }
499
500 __checkReturn boolean_t
efx_mcdi_request_poll(__in efx_nic_t * enp)501 efx_mcdi_request_poll(
502 __in efx_nic_t *enp)
503 {
504 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
505 efx_mcdi_req_t *emrp;
506 efsys_lock_state_t state;
507 efx_rc_t rc;
508
509 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
510 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
511 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
512
513 /* Serialise against post-watchdog efx_mcdi_ev* */
514 EFSYS_LOCK(enp->en_eslp, state);
515
516 EFSYS_ASSERT(emip->emi_pending_req != NULL);
517 EFSYS_ASSERT(!emip->emi_ev_cpl);
518 emrp = emip->emi_pending_req;
519
520 /* Check if hardware is unavailable */
521 if (efx_nic_hw_unavailable(enp)) {
522 EFSYS_UNLOCK(enp->en_eslp, state);
523 return (B_FALSE);
524 }
525
526 /* Check for reboot atomically w.r.t efx_mcdi_request_start */
527 if (emip->emi_poll_cnt++ == 0) {
528 if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
529 emip->emi_pending_req = NULL;
530 EFSYS_UNLOCK(enp->en_eslp, state);
531
532 /* Reboot/Assertion */
533 if (rc == EIO || rc == EINTR)
534 efx_mcdi_raise_exception(enp, emrp, rc);
535
536 goto fail1;
537 }
538 }
539
540 /* Check if a response is available */
541 if (efx_mcdi_poll_response(enp) == B_FALSE) {
542 EFSYS_UNLOCK(enp->en_eslp, state);
543 return (B_FALSE);
544 }
545
546 /* Read the response header */
547 efx_mcdi_read_response_header(enp, emrp);
548
549 /* Request complete */
550 emip->emi_pending_req = NULL;
551
552 /* Ensure stale MCDI requests fail after an MC reboot. */
553 emip->emi_new_epoch = B_FALSE;
554
555 EFSYS_UNLOCK(enp->en_eslp, state);
556
557 if ((rc = emrp->emr_rc) != 0)
558 goto fail2;
559
560 efx_mcdi_finish_response(enp, emrp);
561 return (B_TRUE);
562
563 fail2:
564 if (!emrp->emr_quiet)
565 EFSYS_PROBE(fail2);
566 fail1:
567 if (!emrp->emr_quiet)
568 EFSYS_PROBE1(fail1, efx_rc_t, rc);
569
570 return (B_TRUE);
571 }
572
573 __checkReturn boolean_t
efx_mcdi_request_abort(__in efx_nic_t * enp)574 efx_mcdi_request_abort(
575 __in efx_nic_t *enp)
576 {
577 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
578 efx_mcdi_req_t *emrp;
579 boolean_t aborted;
580 efsys_lock_state_t state;
581
582 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
583 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
584 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
585
586 /*
587 * efx_mcdi_ev_* may have already completed this event, and be
588 * spinning/blocked on the upper layer lock. So it *is* legitimate
589 * to for emi_pending_req to be NULL. If there is a pending event
590 * completed request, then provide a "credit" to allow
591 * efx_mcdi_ev_cpl() to accept a single spurious completion.
592 */
593 EFSYS_LOCK(enp->en_eslp, state);
594 emrp = emip->emi_pending_req;
595 aborted = (emrp != NULL);
596 if (aborted) {
597 emip->emi_pending_req = NULL;
598
599 /* Error the request */
600 emrp->emr_out_length_used = 0;
601 emrp->emr_rc = ETIMEDOUT;
602
603 /* Provide a credit for seqno/emr_pending_req mismatches */
604 if (emip->emi_ev_cpl)
605 ++emip->emi_aborted;
606
607 /*
608 * The upper layer has called us, so we don't
609 * need to complete the request.
610 */
611 }
612 EFSYS_UNLOCK(enp->en_eslp, state);
613
614 return (aborted);
615 }
616
617 void
efx_mcdi_get_timeout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * timeoutp)618 efx_mcdi_get_timeout(
619 __in efx_nic_t *enp,
620 __in efx_mcdi_req_t *emrp,
621 __out uint32_t *timeoutp)
622 {
623 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
624
625 emcop->emco_get_timeout(enp, emrp, timeoutp);
626 }
627
628 __checkReturn efx_rc_t
efx_mcdi_request_errcode(__in unsigned int err)629 efx_mcdi_request_errcode(
630 __in unsigned int err)
631 {
632
633 switch (err) {
634 /* MCDI v1 */
635 case MC_CMD_ERR_EPERM:
636 return (EACCES);
637 case MC_CMD_ERR_ENOENT:
638 return (ENOENT);
639 case MC_CMD_ERR_EINTR:
640 return (EINTR);
641 case MC_CMD_ERR_EACCES:
642 return (EACCES);
643 case MC_CMD_ERR_EBUSY:
644 return (EBUSY);
645 case MC_CMD_ERR_EINVAL:
646 return (EINVAL);
647 case MC_CMD_ERR_EDEADLK:
648 return (EDEADLK);
649 case MC_CMD_ERR_ENOSYS:
650 return (ENOTSUP);
651 case MC_CMD_ERR_ETIME:
652 return (ETIMEDOUT);
653 case MC_CMD_ERR_ENOTSUP:
654 return (ENOTSUP);
655 case MC_CMD_ERR_EALREADY:
656 return (EALREADY);
657
658 /* MCDI v2 */
659 case MC_CMD_ERR_EEXIST:
660 return (EEXIST);
661 #ifdef MC_CMD_ERR_EAGAIN
662 case MC_CMD_ERR_EAGAIN:
663 return (EAGAIN);
664 #endif
665 #ifdef MC_CMD_ERR_ENOSPC
666 case MC_CMD_ERR_ENOSPC:
667 return (ENOSPC);
668 #endif
669 case MC_CMD_ERR_ERANGE:
670 return (ERANGE);
671
672 case MC_CMD_ERR_ALLOC_FAIL:
673 return (ENOMEM);
674 case MC_CMD_ERR_NO_VADAPTOR:
675 return (ENOENT);
676 case MC_CMD_ERR_NO_EVB_PORT:
677 return (ENOENT);
678 case MC_CMD_ERR_NO_VSWITCH:
679 return (ENODEV);
680 case MC_CMD_ERR_VLAN_LIMIT:
681 return (EINVAL);
682 case MC_CMD_ERR_BAD_PCI_FUNC:
683 return (ENODEV);
684 case MC_CMD_ERR_BAD_VLAN_MODE:
685 return (EINVAL);
686 case MC_CMD_ERR_BAD_VSWITCH_TYPE:
687 return (EINVAL);
688 case MC_CMD_ERR_BAD_VPORT_TYPE:
689 return (EINVAL);
690 case MC_CMD_ERR_MAC_EXIST:
691 return (EEXIST);
692
693 case MC_CMD_ERR_PROXY_PENDING:
694 return (EAGAIN);
695
696 default:
697 EFSYS_PROBE1(mc_pcol_error, int, err);
698 return (EIO);
699 }
700 }
701
702 void
efx_mcdi_raise_exception(__in efx_nic_t * enp,__in_opt efx_mcdi_req_t * emrp,__in int rc)703 efx_mcdi_raise_exception(
704 __in efx_nic_t *enp,
705 __in_opt efx_mcdi_req_t *emrp,
706 __in int rc)
707 {
708 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
709 efx_mcdi_exception_t exception;
710
711 /* Reboot or Assertion failure only */
712 EFSYS_ASSERT(rc == EIO || rc == EINTR);
713
714 /*
715 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
716 * then the EIO is not worthy of an exception.
717 */
718 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
719 return;
720
721 exception = (rc == EIO)
722 ? EFX_MCDI_EXCEPTION_MC_REBOOT
723 : EFX_MCDI_EXCEPTION_MC_BADASSERT;
724
725 emtp->emt_exception(emtp->emt_context, exception);
726 }
727
728 void
efx_mcdi_execute(__in efx_nic_t * enp,__inout efx_mcdi_req_t * emrp)729 efx_mcdi_execute(
730 __in efx_nic_t *enp,
731 __inout efx_mcdi_req_t *emrp)
732 {
733 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
734
735 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
736 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
737
738 emrp->emr_quiet = B_FALSE;
739 emtp->emt_execute(emtp->emt_context, emrp);
740 }
741
742 void
efx_mcdi_execute_quiet(__in efx_nic_t * enp,__inout efx_mcdi_req_t * emrp)743 efx_mcdi_execute_quiet(
744 __in efx_nic_t *enp,
745 __inout efx_mcdi_req_t *emrp)
746 {
747 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
748
749 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
750 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
751
752 emrp->emr_quiet = B_TRUE;
753 emtp->emt_execute(emtp->emt_context, emrp);
754 }
755
756 void
efx_mcdi_ev_cpl(__in efx_nic_t * enp,__in unsigned int seq,__in unsigned int outlen,__in int errcode)757 efx_mcdi_ev_cpl(
758 __in efx_nic_t *enp,
759 __in unsigned int seq,
760 __in unsigned int outlen,
761 __in int errcode)
762 {
763 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
764 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
765 efx_mcdi_req_t *emrp;
766 efsys_lock_state_t state;
767
768 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
769 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
770
771 /*
772 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
773 * when we're completing an aborted request.
774 */
775 EFSYS_LOCK(enp->en_eslp, state);
776 if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
777 (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
778 EFSYS_ASSERT(emip->emi_aborted > 0);
779 if (emip->emi_aborted > 0)
780 --emip->emi_aborted;
781 EFSYS_UNLOCK(enp->en_eslp, state);
782 return;
783 }
784
785 emrp = emip->emi_pending_req;
786 emip->emi_pending_req = NULL;
787 EFSYS_UNLOCK(enp->en_eslp, state);
788
789 if (emip->emi_max_version >= 2) {
790 /* MCDIv2 response details do not fit into an event. */
791 efx_mcdi_read_response_header(enp, emrp);
792 } else {
793 if (errcode != 0) {
794 if (!emrp->emr_quiet) {
795 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
796 int, errcode);
797 }
798 emrp->emr_out_length_used = 0;
799 emrp->emr_rc = efx_mcdi_request_errcode(errcode);
800 } else {
801 emrp->emr_out_length_used = outlen;
802 emrp->emr_rc = 0;
803 }
804 }
805 if (emrp->emr_rc == 0)
806 efx_mcdi_finish_response(enp, emrp);
807
808 emtp->emt_ev_cpl(emtp->emt_context);
809 }
810
811 #if EFSYS_OPT_MCDI_PROXY_AUTH
812
813 __checkReturn efx_rc_t
efx_mcdi_get_proxy_handle(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * handlep)814 efx_mcdi_get_proxy_handle(
815 __in efx_nic_t *enp,
816 __in efx_mcdi_req_t *emrp,
817 __out uint32_t *handlep)
818 {
819 efx_rc_t rc;
820
821 _NOTE(ARGUNUSED(enp))
822
823 /*
824 * Return proxy handle from MCDI request that returned with error
825 * MC_MCD_ERR_PROXY_PENDING. This handle is used to wait for a matching
826 * PROXY_RESPONSE event.
827 */
828 if ((emrp == NULL) || (handlep == NULL)) {
829 rc = EINVAL;
830 goto fail1;
831 }
832 if ((emrp->emr_rc != 0) &&
833 (emrp->emr_err_code == MC_CMD_ERR_PROXY_PENDING)) {
834 *handlep = emrp->emr_proxy_handle;
835 rc = 0;
836 } else {
837 *handlep = 0;
838 rc = ENOENT;
839 }
840 return (rc);
841
842 fail1:
843 EFSYS_PROBE1(fail1, efx_rc_t, rc);
844 return (rc);
845 }
846
847 void
efx_mcdi_ev_proxy_response(__in efx_nic_t * enp,__in unsigned int handle,__in unsigned int status)848 efx_mcdi_ev_proxy_response(
849 __in efx_nic_t *enp,
850 __in unsigned int handle,
851 __in unsigned int status)
852 {
853 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
854 efx_rc_t rc;
855
856 /*
857 * Handle results of an authorization request for a privileged MCDI
858 * command. If authorization was granted then we must re-issue the
859 * original MCDI request. If authorization failed or timed out,
860 * then the original MCDI request should be completed with the
861 * result code from this event.
862 */
863 rc = (status == 0) ? 0 : efx_mcdi_request_errcode(status);
864
865 emtp->emt_ev_proxy_response(emtp->emt_context, handle, rc);
866 }
867 #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
868
869 void
efx_mcdi_ev_death(__in efx_nic_t * enp,__in int rc)870 efx_mcdi_ev_death(
871 __in efx_nic_t *enp,
872 __in int rc)
873 {
874 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
875 const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
876 efx_mcdi_req_t *emrp = NULL;
877 boolean_t ev_cpl;
878 efsys_lock_state_t state;
879
880 /*
881 * The MCDI request (if there is one) has been terminated, either
882 * by a BADASSERT or REBOOT event.
883 *
884 * If there is an outstanding event-completed MCDI operation, then we
885 * will never receive the completion event (because both MCDI
886 * completions and BADASSERT events are sent to the same evq). So
887 * complete this MCDI op.
888 *
889 * This function might run in parallel with efx_mcdi_request_poll()
890 * for poll completed mcdi requests, and also with
891 * efx_mcdi_request_start() for post-watchdog completions.
892 */
893 EFSYS_LOCK(enp->en_eslp, state);
894 emrp = emip->emi_pending_req;
895 ev_cpl = emip->emi_ev_cpl;
896 if (emrp != NULL && emip->emi_ev_cpl) {
897 emip->emi_pending_req = NULL;
898
899 emrp->emr_out_length_used = 0;
900 emrp->emr_rc = rc;
901 ++emip->emi_aborted;
902 }
903
904 /*
905 * Since we're running in parallel with a request, consume the
906 * status word before dropping the lock.
907 */
908 if (rc == EIO || rc == EINTR) {
909 EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
910 (void) efx_mcdi_poll_reboot(enp);
911 emip->emi_new_epoch = B_TRUE;
912 }
913
914 EFSYS_UNLOCK(enp->en_eslp, state);
915
916 efx_mcdi_raise_exception(enp, emrp, rc);
917
918 if (emrp != NULL && ev_cpl)
919 emtp->emt_ev_cpl(emtp->emt_context);
920 }
921
922 __checkReturn efx_rc_t
923 efx_mcdi_version(
924 __in efx_nic_t *enp,
925 __out_ecount_opt(4) uint16_t versionp[4],
926 __out_opt uint32_t *buildp,
927 __out_opt efx_mcdi_boot_t *statusp)
928 {
929 efx_mcdi_req_t req;
930 EFX_MCDI_DECLARE_BUF(payload,
931 MAX(MC_CMD_GET_VERSION_IN_LEN, MC_CMD_GET_BOOT_STATUS_IN_LEN),
932 MAX(MC_CMD_GET_VERSION_OUT_LEN,
933 MC_CMD_GET_BOOT_STATUS_OUT_LEN));
934 efx_word_t *ver_words;
935 uint16_t version[4];
936 uint32_t build;
937 efx_mcdi_boot_t status;
938 efx_rc_t rc;
939
940 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
941
942 req.emr_cmd = MC_CMD_GET_VERSION;
943 req.emr_in_buf = payload;
944 req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
945 req.emr_out_buf = payload;
946 req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
947
948 efx_mcdi_execute(enp, &req);
949
950 if (req.emr_rc != 0) {
951 rc = req.emr_rc;
952 goto fail1;
953 }
954
955 /* bootrom support */
956 if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
957 version[0] = version[1] = version[2] = version[3] = 0;
958 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
959
960 goto version;
961 }
962
963 if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
964 rc = EMSGSIZE;
965 goto fail2;
966 }
967
968 ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
969 version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
970 version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
971 version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
972 version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
973 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
974
975 version:
976 /* The bootrom doesn't understand BOOT_STATUS */
977 if (MC_FW_VERSION_IS_BOOTLOADER(build)) {
978 status = EFX_MCDI_BOOT_ROM;
979 goto out;
980 }
981
982 (void) memset(payload, 0, sizeof (payload));
983 req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
984 req.emr_in_buf = payload;
985 req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
986 req.emr_out_buf = payload;
987 req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
988
989 efx_mcdi_execute_quiet(enp, &req);
990
991 if (req.emr_rc == EACCES) {
992 /* Unprivileged functions cannot access BOOT_STATUS */
993 status = EFX_MCDI_BOOT_PRIMARY;
994 version[0] = version[1] = version[2] = version[3] = 0;
995 build = 0;
996 goto out;
997 }
998
999 if (req.emr_rc != 0) {
1000 rc = req.emr_rc;
1001 goto fail3;
1002 }
1003
1004 if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
1005 rc = EMSGSIZE;
1006 goto fail4;
1007 }
1008
1009 if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
1010 GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
1011 status = EFX_MCDI_BOOT_PRIMARY;
1012 else
1013 status = EFX_MCDI_BOOT_SECONDARY;
1014
1015 out:
1016 if (versionp != NULL)
1017 memcpy(versionp, version, sizeof (version));
1018 if (buildp != NULL)
1019 *buildp = build;
1020 if (statusp != NULL)
1021 *statusp = status;
1022
1023 return (0);
1024
1025 fail4:
1026 EFSYS_PROBE(fail4);
1027 fail3:
1028 EFSYS_PROBE(fail3);
1029 fail2:
1030 EFSYS_PROBE(fail2);
1031 fail1:
1032 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1033
1034 return (rc);
1035 }
1036
1037 __checkReturn efx_rc_t
efx_mcdi_get_capabilities(__in efx_nic_t * enp,__out_opt uint32_t * flagsp,__out_opt uint16_t * rx_dpcpu_fw_idp,__out_opt uint16_t * tx_dpcpu_fw_idp,__out_opt uint32_t * flags2p,__out_opt uint32_t * tso2ncp)1038 efx_mcdi_get_capabilities(
1039 __in efx_nic_t *enp,
1040 __out_opt uint32_t *flagsp,
1041 __out_opt uint16_t *rx_dpcpu_fw_idp,
1042 __out_opt uint16_t *tx_dpcpu_fw_idp,
1043 __out_opt uint32_t *flags2p,
1044 __out_opt uint32_t *tso2ncp)
1045 {
1046 efx_mcdi_req_t req;
1047 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_CAPABILITIES_IN_LEN,
1048 MC_CMD_GET_CAPABILITIES_V2_OUT_LEN);
1049 boolean_t v2_capable;
1050 efx_rc_t rc;
1051
1052 req.emr_cmd = MC_CMD_GET_CAPABILITIES;
1053 req.emr_in_buf = payload;
1054 req.emr_in_length = MC_CMD_GET_CAPABILITIES_IN_LEN;
1055 req.emr_out_buf = payload;
1056 req.emr_out_length = MC_CMD_GET_CAPABILITIES_V2_OUT_LEN;
1057
1058 efx_mcdi_execute_quiet(enp, &req);
1059
1060 if (req.emr_rc != 0) {
1061 rc = req.emr_rc;
1062 goto fail1;
1063 }
1064
1065 if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
1066 rc = EMSGSIZE;
1067 goto fail2;
1068 }
1069
1070 if (flagsp != NULL)
1071 *flagsp = MCDI_OUT_DWORD(req, GET_CAPABILITIES_OUT_FLAGS1);
1072
1073 if (rx_dpcpu_fw_idp != NULL)
1074 *rx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1075 GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID);
1076
1077 if (tx_dpcpu_fw_idp != NULL)
1078 *tx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
1079 GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
1080
1081 if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
1082 v2_capable = B_FALSE;
1083 else
1084 v2_capable = B_TRUE;
1085
1086 if (flags2p != NULL) {
1087 *flags2p = (v2_capable) ?
1088 MCDI_OUT_DWORD(req, GET_CAPABILITIES_V2_OUT_FLAGS2) :
1089 0;
1090 }
1091
1092 if (tso2ncp != NULL) {
1093 *tso2ncp = (v2_capable) ?
1094 MCDI_OUT_WORD(req,
1095 GET_CAPABILITIES_V2_OUT_TX_TSO_V2_N_CONTEXTS) :
1096 0;
1097 }
1098
1099 return (0);
1100
1101 fail2:
1102 EFSYS_PROBE(fail2);
1103 fail1:
1104 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1105
1106 return (rc);
1107 }
1108
1109 static __checkReturn efx_rc_t
efx_mcdi_do_reboot(__in efx_nic_t * enp,__in boolean_t after_assertion)1110 efx_mcdi_do_reboot(
1111 __in efx_nic_t *enp,
1112 __in boolean_t after_assertion)
1113 {
1114 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_REBOOT_IN_LEN,
1115 MC_CMD_REBOOT_OUT_LEN);
1116 efx_mcdi_req_t req;
1117 efx_rc_t rc;
1118
1119 /*
1120 * We could require the caller to have caused en_mod_flags=0 to
1121 * call this function. This doesn't help the other port though,
1122 * who's about to get the MC ripped out from underneath them.
1123 * Since they have to cope with the subsequent fallout of MCDI
1124 * failures, we should as well.
1125 */
1126 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1127
1128 req.emr_cmd = MC_CMD_REBOOT;
1129 req.emr_in_buf = payload;
1130 req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
1131 req.emr_out_buf = payload;
1132 req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
1133
1134 MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
1135 (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
1136
1137 efx_mcdi_execute_quiet(enp, &req);
1138
1139 if (req.emr_rc == EACCES) {
1140 /* Unprivileged functions cannot reboot the MC. */
1141 goto out;
1142 }
1143
1144 /* A successful reboot request returns EIO. */
1145 if (req.emr_rc != 0 && req.emr_rc != EIO) {
1146 rc = req.emr_rc;
1147 goto fail1;
1148 }
1149
1150 out:
1151 return (0);
1152
1153 fail1:
1154 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1155
1156 return (rc);
1157 }
1158
1159 __checkReturn efx_rc_t
efx_mcdi_reboot(__in efx_nic_t * enp)1160 efx_mcdi_reboot(
1161 __in efx_nic_t *enp)
1162 {
1163 return (efx_mcdi_do_reboot(enp, B_FALSE));
1164 }
1165
1166 __checkReturn efx_rc_t
efx_mcdi_exit_assertion_handler(__in efx_nic_t * enp)1167 efx_mcdi_exit_assertion_handler(
1168 __in efx_nic_t *enp)
1169 {
1170 return (efx_mcdi_do_reboot(enp, B_TRUE));
1171 }
1172
1173 __checkReturn efx_rc_t
efx_mcdi_read_assertion(__in efx_nic_t * enp)1174 efx_mcdi_read_assertion(
1175 __in efx_nic_t *enp)
1176 {
1177 efx_mcdi_req_t req;
1178 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_ASSERTS_IN_LEN,
1179 MC_CMD_GET_ASSERTS_OUT_LEN);
1180 #ifdef KDTRACE_HOOKS
1181 const char *reason;
1182 #else
1183 const char *reason __unused;
1184 #endif
1185 unsigned int flags;
1186 unsigned int index;
1187 unsigned int ofst;
1188 int retry;
1189 efx_rc_t rc;
1190
1191 /*
1192 * Before we attempt to chat to the MC, we should verify that the MC
1193 * isn't in its assertion handler, either due to a previous reboot,
1194 * or because we're reinitializing due to an eec_exception().
1195 *
1196 * Use GET_ASSERTS to read any assertion state that may be present.
1197 * Retry this command twice. Once because a boot-time assertion failure
1198 * might cause the 1st MCDI request to fail. And once again because
1199 * we might race with efx_mcdi_exit_assertion_handler() running on
1200 * partner port(s) on the same NIC.
1201 */
1202 retry = 2;
1203 do {
1204 (void) memset(payload, 0, sizeof (payload));
1205 req.emr_cmd = MC_CMD_GET_ASSERTS;
1206 req.emr_in_buf = payload;
1207 req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
1208 req.emr_out_buf = payload;
1209 req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
1210
1211 MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
1212 efx_mcdi_execute_quiet(enp, &req);
1213
1214 } while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
1215
1216 if (req.emr_rc != 0) {
1217 if (req.emr_rc == EACCES) {
1218 /* Unprivileged functions cannot clear assertions. */
1219 goto out;
1220 }
1221 rc = req.emr_rc;
1222 goto fail1;
1223 }
1224
1225 if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
1226 rc = EMSGSIZE;
1227 goto fail2;
1228 }
1229
1230 /* Print out any assertion state recorded */
1231 flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
1232 if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
1233 return (0);
1234
1235 reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
1236 ? "system-level assertion"
1237 : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
1238 ? "thread-level assertion"
1239 : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
1240 ? "watchdog reset"
1241 : (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
1242 ? "illegal address trap"
1243 : "unknown assertion";
1244 EFSYS_PROBE3(mcpu_assertion,
1245 const char *, reason, unsigned int,
1246 MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
1247 unsigned int,
1248 MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
1249
1250 /* Print out the registers (r1 ... r31) */
1251 ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
1252 for (index = 1;
1253 index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
1254 index++) {
1255 EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
1256 EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
1257 EFX_DWORD_0));
1258 ofst += sizeof (efx_dword_t);
1259 }
1260 EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
1261
1262 out:
1263 return (0);
1264
1265 fail2:
1266 EFSYS_PROBE(fail2);
1267 fail1:
1268 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1269
1270 return (rc);
1271 }
1272
1273 /*
1274 * Internal routines for specific MCDI requests.
1275 */
1276
1277 __checkReturn efx_rc_t
efx_mcdi_drv_attach(__in efx_nic_t * enp,__in boolean_t attach)1278 efx_mcdi_drv_attach(
1279 __in efx_nic_t *enp,
1280 __in boolean_t attach)
1281 {
1282 efx_mcdi_req_t req;
1283 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_DRV_ATTACH_IN_LEN,
1284 MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
1285 efx_rc_t rc;
1286
1287 req.emr_cmd = MC_CMD_DRV_ATTACH;
1288 req.emr_in_buf = payload;
1289 req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
1290 req.emr_out_buf = payload;
1291 req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
1292
1293 /*
1294 * Typically, client drivers use DONT_CARE for the datapath firmware
1295 * type to ensure that the driver can attach to an unprivileged
1296 * function. The datapath firmware type to use is controlled by the
1297 * 'sfboot' utility.
1298 * If a client driver wishes to attach with a specific datapath firmware
1299 * type, that can be passed in second argument of efx_nic_probe API. One
1300 * such example is the ESXi native driver that attempts attaching with
1301 * FULL_FEATURED datapath firmware type first and fall backs to
1302 * DONT_CARE datapath firmware type if MC_CMD_DRV_ATTACH fails.
1303 */
1304 MCDI_IN_POPULATE_DWORD_2(req, DRV_ATTACH_IN_NEW_STATE,
1305 DRV_ATTACH_IN_ATTACH, attach ? 1 : 0,
1306 DRV_ATTACH_IN_SUBVARIANT_AWARE, EFSYS_OPT_FW_SUBVARIANT_AWARE);
1307 MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
1308 MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, enp->efv);
1309
1310 efx_mcdi_execute(enp, &req);
1311
1312 if (req.emr_rc != 0) {
1313 rc = req.emr_rc;
1314 goto fail1;
1315 }
1316
1317 if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
1318 rc = EMSGSIZE;
1319 goto fail2;
1320 }
1321
1322 return (0);
1323
1324 fail2:
1325 EFSYS_PROBE(fail2);
1326 fail1:
1327 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1328
1329 return (rc);
1330 }
1331
1332 __checkReturn efx_rc_t
1333 efx_mcdi_get_board_cfg(
1334 __in efx_nic_t *enp,
1335 __out_opt uint32_t *board_typep,
1336 __out_opt efx_dword_t *capabilitiesp,
1337 __out_ecount_opt(6) uint8_t mac_addrp[6])
1338 {
1339 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1340 efx_mcdi_req_t req;
1341 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN,
1342 MC_CMD_GET_BOARD_CFG_OUT_LENMIN);
1343 efx_rc_t rc;
1344
1345 req.emr_cmd = MC_CMD_GET_BOARD_CFG;
1346 req.emr_in_buf = payload;
1347 req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
1348 req.emr_out_buf = payload;
1349 req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
1350
1351 efx_mcdi_execute(enp, &req);
1352
1353 if (req.emr_rc != 0) {
1354 rc = req.emr_rc;
1355 goto fail1;
1356 }
1357
1358 if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
1359 rc = EMSGSIZE;
1360 goto fail2;
1361 }
1362
1363 if (mac_addrp != NULL) {
1364 uint8_t *addrp;
1365
1366 if (emip->emi_port == 1) {
1367 addrp = MCDI_OUT2(req, uint8_t,
1368 GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
1369 } else if (emip->emi_port == 2) {
1370 addrp = MCDI_OUT2(req, uint8_t,
1371 GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
1372 } else {
1373 rc = EINVAL;
1374 goto fail3;
1375 }
1376
1377 EFX_MAC_ADDR_COPY(mac_addrp, addrp);
1378 }
1379
1380 if (capabilitiesp != NULL) {
1381 if (emip->emi_port == 1) {
1382 *capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1383 GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
1384 } else if (emip->emi_port == 2) {
1385 *capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1386 GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
1387 } else {
1388 rc = EINVAL;
1389 goto fail4;
1390 }
1391 }
1392
1393 if (board_typep != NULL) {
1394 *board_typep = MCDI_OUT_DWORD(req,
1395 GET_BOARD_CFG_OUT_BOARD_TYPE);
1396 }
1397
1398 return (0);
1399
1400 fail4:
1401 EFSYS_PROBE(fail4);
1402 fail3:
1403 EFSYS_PROBE(fail3);
1404 fail2:
1405 EFSYS_PROBE(fail2);
1406 fail1:
1407 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1408
1409 return (rc);
1410 }
1411
1412 __checkReturn efx_rc_t
efx_mcdi_get_resource_limits(__in efx_nic_t * enp,__out_opt uint32_t * nevqp,__out_opt uint32_t * nrxqp,__out_opt uint32_t * ntxqp)1413 efx_mcdi_get_resource_limits(
1414 __in efx_nic_t *enp,
1415 __out_opt uint32_t *nevqp,
1416 __out_opt uint32_t *nrxqp,
1417 __out_opt uint32_t *ntxqp)
1418 {
1419 efx_mcdi_req_t req;
1420 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
1421 MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN);
1422 efx_rc_t rc;
1423
1424 req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
1425 req.emr_in_buf = payload;
1426 req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
1427 req.emr_out_buf = payload;
1428 req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
1429
1430 efx_mcdi_execute(enp, &req);
1431
1432 if (req.emr_rc != 0) {
1433 rc = req.emr_rc;
1434 goto fail1;
1435 }
1436
1437 if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
1438 rc = EMSGSIZE;
1439 goto fail2;
1440 }
1441
1442 if (nevqp != NULL)
1443 *nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
1444 if (nrxqp != NULL)
1445 *nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
1446 if (ntxqp != NULL)
1447 *ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
1448
1449 return (0);
1450
1451 fail2:
1452 EFSYS_PROBE(fail2);
1453 fail1:
1454 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1455
1456 return (rc);
1457 }
1458
1459 __checkReturn efx_rc_t
efx_mcdi_get_phy_cfg(__in efx_nic_t * enp)1460 efx_mcdi_get_phy_cfg(
1461 __in efx_nic_t *enp)
1462 {
1463 efx_port_t *epp = &(enp->en_port);
1464 efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1465 efx_mcdi_req_t req;
1466 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_CFG_IN_LEN,
1467 MC_CMD_GET_PHY_CFG_OUT_LEN);
1468 #if EFSYS_OPT_NAMES
1469 const char *namep;
1470 size_t namelen;
1471 #endif
1472 uint32_t phy_media_type;
1473 efx_rc_t rc;
1474
1475 req.emr_cmd = MC_CMD_GET_PHY_CFG;
1476 req.emr_in_buf = payload;
1477 req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
1478 req.emr_out_buf = payload;
1479 req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
1480
1481 efx_mcdi_execute(enp, &req);
1482
1483 if (req.emr_rc != 0) {
1484 rc = req.emr_rc;
1485 goto fail1;
1486 }
1487
1488 if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
1489 rc = EMSGSIZE;
1490 goto fail2;
1491 }
1492
1493 encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
1494 #if EFSYS_OPT_NAMES
1495 namep = MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME);
1496 namelen = MIN(sizeof (encp->enc_phy_name) - 1,
1497 strnlen(namep, MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
1498 (void) memset(encp->enc_phy_name, 0,
1499 sizeof (encp->enc_phy_name));
1500 memcpy(encp->enc_phy_name, namep, namelen);
1501 #endif /* EFSYS_OPT_NAMES */
1502 (void) memset(encp->enc_phy_revision, 0,
1503 sizeof (encp->enc_phy_revision));
1504 memcpy(encp->enc_phy_revision,
1505 MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
1506 MIN(sizeof (encp->enc_phy_revision) - 1,
1507 MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
1508 #if EFSYS_OPT_PHY_LED_CONTROL
1509 encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
1510 (1 << EFX_PHY_LED_OFF) |
1511 (1 << EFX_PHY_LED_ON));
1512 #endif /* EFSYS_OPT_PHY_LED_CONTROL */
1513
1514 /* Get the media type of the fixed port, if recognised. */
1515 EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
1516 EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
1517 EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
1518 EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
1519 EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
1520 EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
1521 EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
1522 phy_media_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
1523 epp->ep_fixed_port_type = (efx_phy_media_type_t) phy_media_type;
1524 if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
1525 epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
1526
1527 epp->ep_phy_cap_mask =
1528 MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
1529 #if EFSYS_OPT_PHY_FLAGS
1530 encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
1531 #endif /* EFSYS_OPT_PHY_FLAGS */
1532
1533 encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
1534
1535 /* Populate internal state */
1536 encp->enc_mcdi_mdio_channel =
1537 (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
1538
1539 #if EFSYS_OPT_PHY_STATS
1540 encp->enc_mcdi_phy_stat_mask =
1541 MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
1542 #endif /* EFSYS_OPT_PHY_STATS */
1543
1544 #if EFSYS_OPT_BIST
1545 encp->enc_bist_mask = 0;
1546 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1547 GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
1548 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
1549 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1550 GET_PHY_CFG_OUT_BIST_CABLE_LONG))
1551 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
1552 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1553 GET_PHY_CFG_OUT_BIST))
1554 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
1555 #endif /* EFSYS_OPT_BIST */
1556
1557 return (0);
1558
1559 fail2:
1560 EFSYS_PROBE(fail2);
1561 fail1:
1562 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1563
1564 return (rc);
1565 }
1566
1567 __checkReturn efx_rc_t
efx_mcdi_firmware_update_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1568 efx_mcdi_firmware_update_supported(
1569 __in efx_nic_t *enp,
1570 __out boolean_t *supportedp)
1571 {
1572 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1573 efx_rc_t rc;
1574
1575 if (emcop != NULL) {
1576 if ((rc = emcop->emco_feature_supported(enp,
1577 EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
1578 goto fail1;
1579 } else {
1580 /* Earlier devices always supported updates */
1581 *supportedp = B_TRUE;
1582 }
1583
1584 return (0);
1585
1586 fail1:
1587 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1588
1589 return (rc);
1590 }
1591
1592 __checkReturn efx_rc_t
efx_mcdi_macaddr_change_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1593 efx_mcdi_macaddr_change_supported(
1594 __in efx_nic_t *enp,
1595 __out boolean_t *supportedp)
1596 {
1597 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1598 efx_rc_t rc;
1599
1600 if (emcop != NULL) {
1601 if ((rc = emcop->emco_feature_supported(enp,
1602 EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
1603 goto fail1;
1604 } else {
1605 /* Earlier devices always supported MAC changes */
1606 *supportedp = B_TRUE;
1607 }
1608
1609 return (0);
1610
1611 fail1:
1612 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1613
1614 return (rc);
1615 }
1616
1617 __checkReturn efx_rc_t
efx_mcdi_link_control_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1618 efx_mcdi_link_control_supported(
1619 __in efx_nic_t *enp,
1620 __out boolean_t *supportedp)
1621 {
1622 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1623 efx_rc_t rc;
1624
1625 if (emcop != NULL) {
1626 if ((rc = emcop->emco_feature_supported(enp,
1627 EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
1628 goto fail1;
1629 } else {
1630 /* Earlier devices always supported link control */
1631 *supportedp = B_TRUE;
1632 }
1633
1634 return (0);
1635
1636 fail1:
1637 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1638
1639 return (rc);
1640 }
1641
1642 __checkReturn efx_rc_t
efx_mcdi_mac_spoofing_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1643 efx_mcdi_mac_spoofing_supported(
1644 __in efx_nic_t *enp,
1645 __out boolean_t *supportedp)
1646 {
1647 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1648 efx_rc_t rc;
1649
1650 if (emcop != NULL) {
1651 if ((rc = emcop->emco_feature_supported(enp,
1652 EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
1653 goto fail1;
1654 } else {
1655 /* Earlier devices always supported MAC spoofing */
1656 *supportedp = B_TRUE;
1657 }
1658
1659 return (0);
1660
1661 fail1:
1662 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1663
1664 return (rc);
1665 }
1666
1667 #if EFSYS_OPT_BIST
1668
1669 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1670 /*
1671 * Enter bist offline mode. This is a fw mode which puts the NIC into a state
1672 * where memory BIST tests can be run and not much else can interfere or happen.
1673 * A reboot is required to exit this mode.
1674 */
1675 __checkReturn efx_rc_t
efx_mcdi_bist_enable_offline(__in efx_nic_t * enp)1676 efx_mcdi_bist_enable_offline(
1677 __in efx_nic_t *enp)
1678 {
1679 efx_mcdi_req_t req;
1680 efx_rc_t rc;
1681
1682 EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
1683 EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
1684
1685 req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
1686 req.emr_in_buf = NULL;
1687 req.emr_in_length = 0;
1688 req.emr_out_buf = NULL;
1689 req.emr_out_length = 0;
1690
1691 efx_mcdi_execute(enp, &req);
1692
1693 if (req.emr_rc != 0) {
1694 rc = req.emr_rc;
1695 goto fail1;
1696 }
1697
1698 return (0);
1699
1700 fail1:
1701 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1702
1703 return (rc);
1704 }
1705 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
1706
1707 __checkReturn efx_rc_t
efx_mcdi_bist_start(__in efx_nic_t * enp,__in efx_bist_type_t type)1708 efx_mcdi_bist_start(
1709 __in efx_nic_t *enp,
1710 __in efx_bist_type_t type)
1711 {
1712 efx_mcdi_req_t req;
1713 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_START_BIST_IN_LEN,
1714 MC_CMD_START_BIST_OUT_LEN);
1715 efx_rc_t rc;
1716
1717 req.emr_cmd = MC_CMD_START_BIST;
1718 req.emr_in_buf = payload;
1719 req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
1720 req.emr_out_buf = payload;
1721 req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
1722
1723 switch (type) {
1724 case EFX_BIST_TYPE_PHY_NORMAL:
1725 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
1726 break;
1727 case EFX_BIST_TYPE_PHY_CABLE_SHORT:
1728 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1729 MC_CMD_PHY_BIST_CABLE_SHORT);
1730 break;
1731 case EFX_BIST_TYPE_PHY_CABLE_LONG:
1732 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1733 MC_CMD_PHY_BIST_CABLE_LONG);
1734 break;
1735 case EFX_BIST_TYPE_MC_MEM:
1736 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1737 MC_CMD_MC_MEM_BIST);
1738 break;
1739 case EFX_BIST_TYPE_SAT_MEM:
1740 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1741 MC_CMD_PORT_MEM_BIST);
1742 break;
1743 case EFX_BIST_TYPE_REG:
1744 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1745 MC_CMD_REG_BIST);
1746 break;
1747 default:
1748 EFSYS_ASSERT(0);
1749 }
1750
1751 efx_mcdi_execute(enp, &req);
1752
1753 if (req.emr_rc != 0) {
1754 rc = req.emr_rc;
1755 goto fail1;
1756 }
1757
1758 return (0);
1759
1760 fail1:
1761 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1762
1763 return (rc);
1764 }
1765
1766 #endif /* EFSYS_OPT_BIST */
1767
1768 /* Enable logging of some events (e.g. link state changes) */
1769 __checkReturn efx_rc_t
efx_mcdi_log_ctrl(__in efx_nic_t * enp)1770 efx_mcdi_log_ctrl(
1771 __in efx_nic_t *enp)
1772 {
1773 efx_mcdi_req_t req;
1774 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_LOG_CTRL_IN_LEN,
1775 MC_CMD_LOG_CTRL_OUT_LEN);
1776 efx_rc_t rc;
1777
1778 req.emr_cmd = MC_CMD_LOG_CTRL;
1779 req.emr_in_buf = payload;
1780 req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
1781 req.emr_out_buf = payload;
1782 req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
1783
1784 MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
1785 MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
1786 MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
1787
1788 efx_mcdi_execute(enp, &req);
1789
1790 if (req.emr_rc != 0) {
1791 rc = req.emr_rc;
1792 goto fail1;
1793 }
1794
1795 return (0);
1796
1797 fail1:
1798 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1799
1800 return (rc);
1801 }
1802
1803 #if EFSYS_OPT_MAC_STATS
1804
1805 typedef enum efx_stats_action_e {
1806 EFX_STATS_CLEAR,
1807 EFX_STATS_UPLOAD,
1808 EFX_STATS_ENABLE_NOEVENTS,
1809 EFX_STATS_ENABLE_EVENTS,
1810 EFX_STATS_DISABLE,
1811 } efx_stats_action_t;
1812
1813 static __checkReturn efx_rc_t
efx_mcdi_mac_stats(__in efx_nic_t * enp,__in_opt efsys_mem_t * esmp,__in efx_stats_action_t action,__in uint16_t period_ms)1814 efx_mcdi_mac_stats(
1815 __in efx_nic_t *enp,
1816 __in_opt efsys_mem_t *esmp,
1817 __in efx_stats_action_t action,
1818 __in uint16_t period_ms)
1819 {
1820 efx_mcdi_req_t req;
1821 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAC_STATS_IN_LEN,
1822 MC_CMD_MAC_STATS_V2_OUT_DMA_LEN);
1823 int clear = (action == EFX_STATS_CLEAR);
1824 int upload = (action == EFX_STATS_UPLOAD);
1825 int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
1826 int events = (action == EFX_STATS_ENABLE_EVENTS);
1827 int disable = (action == EFX_STATS_DISABLE);
1828 efx_rc_t rc;
1829
1830 req.emr_cmd = MC_CMD_MAC_STATS;
1831 req.emr_in_buf = payload;
1832 req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
1833 req.emr_out_buf = payload;
1834 req.emr_out_length = MC_CMD_MAC_STATS_V2_OUT_DMA_LEN;
1835
1836 MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
1837 MAC_STATS_IN_DMA, upload,
1838 MAC_STATS_IN_CLEAR, clear,
1839 MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
1840 MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
1841 MAC_STATS_IN_PERIODIC_NOEVENT, !events,
1842 MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
1843
1844 if (enable || events || upload) {
1845 const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
1846 uint32_t bytes;
1847
1848 /* Periodic stats or stats upload require a DMA buffer */
1849 if (esmp == NULL) {
1850 rc = EINVAL;
1851 goto fail1;
1852 }
1853
1854 if (encp->enc_mac_stats_nstats < MC_CMD_MAC_NSTATS) {
1855 /* MAC stats count too small for legacy MAC stats */
1856 rc = ENOSPC;
1857 goto fail2;
1858 }
1859
1860 bytes = encp->enc_mac_stats_nstats * sizeof (efx_qword_t);
1861
1862 if (EFSYS_MEM_SIZE(esmp) < bytes) {
1863 /* DMA buffer too small */
1864 rc = ENOSPC;
1865 goto fail3;
1866 }
1867
1868 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
1869 EFSYS_MEM_ADDR(esmp) & 0xffffffff);
1870 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
1871 EFSYS_MEM_ADDR(esmp) >> 32);
1872 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
1873 }
1874
1875 /*
1876 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
1877 * as this may fail (and leave periodic DMA enabled) if the
1878 * vadapter has already been deleted.
1879 */
1880 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
1881 (disable ? EVB_PORT_ID_NULL : enp->en_vport_id));
1882
1883 efx_mcdi_execute(enp, &req);
1884
1885 if (req.emr_rc != 0) {
1886 /* EF10: Expect ENOENT if no DMA queues are initialised */
1887 if ((req.emr_rc != ENOENT) ||
1888 (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
1889 rc = req.emr_rc;
1890 goto fail4;
1891 }
1892 }
1893
1894 return (0);
1895
1896 fail4:
1897 EFSYS_PROBE(fail4);
1898 fail3:
1899 EFSYS_PROBE(fail3);
1900 fail2:
1901 EFSYS_PROBE(fail2);
1902 fail1:
1903 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1904
1905 return (rc);
1906 }
1907
1908 __checkReturn efx_rc_t
efx_mcdi_mac_stats_clear(__in efx_nic_t * enp)1909 efx_mcdi_mac_stats_clear(
1910 __in efx_nic_t *enp)
1911 {
1912 efx_rc_t rc;
1913
1914 if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR, 0)) != 0)
1915 goto fail1;
1916
1917 return (0);
1918
1919 fail1:
1920 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1921
1922 return (rc);
1923 }
1924
1925 __checkReturn efx_rc_t
efx_mcdi_mac_stats_upload(__in efx_nic_t * enp,__in efsys_mem_t * esmp)1926 efx_mcdi_mac_stats_upload(
1927 __in efx_nic_t *enp,
1928 __in efsys_mem_t *esmp)
1929 {
1930 efx_rc_t rc;
1931
1932 /*
1933 * The MC DMAs aggregate statistics for our convenience, so we can
1934 * avoid having to pull the statistics buffer into the cache to
1935 * maintain cumulative statistics.
1936 */
1937 if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD, 0)) != 0)
1938 goto fail1;
1939
1940 return (0);
1941
1942 fail1:
1943 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1944
1945 return (rc);
1946 }
1947
1948 __checkReturn efx_rc_t
efx_mcdi_mac_stats_periodic(__in efx_nic_t * enp,__in efsys_mem_t * esmp,__in uint16_t period_ms,__in boolean_t events)1949 efx_mcdi_mac_stats_periodic(
1950 __in efx_nic_t *enp,
1951 __in efsys_mem_t *esmp,
1952 __in uint16_t period_ms,
1953 __in boolean_t events)
1954 {
1955 efx_rc_t rc;
1956
1957 /*
1958 * The MC DMAs aggregate statistics for our convenience, so we can
1959 * avoid having to pull the statistics buffer into the cache to
1960 * maintain cumulative statistics.
1961 * Huntington uses a fixed 1sec period.
1962 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
1963 */
1964 if (period_ms == 0)
1965 rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE, 0);
1966 else if (events)
1967 rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS,
1968 period_ms);
1969 else
1970 rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS,
1971 period_ms);
1972
1973 if (rc != 0)
1974 goto fail1;
1975
1976 return (0);
1977
1978 fail1:
1979 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1980
1981 return (rc);
1982 }
1983
1984 #endif /* EFSYS_OPT_MAC_STATS */
1985
1986 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1987
1988 /*
1989 * This function returns the pf and vf number of a function. If it is a pf the
1990 * vf number is 0xffff. The vf number is the index of the vf on that
1991 * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
1992 * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
1993 */
1994 __checkReturn efx_rc_t
efx_mcdi_get_function_info(__in efx_nic_t * enp,__out uint32_t * pfp,__out_opt uint32_t * vfp)1995 efx_mcdi_get_function_info(
1996 __in efx_nic_t *enp,
1997 __out uint32_t *pfp,
1998 __out_opt uint32_t *vfp)
1999 {
2000 efx_mcdi_req_t req;
2001 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
2002 MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
2003 efx_rc_t rc;
2004
2005 req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
2006 req.emr_in_buf = payload;
2007 req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
2008 req.emr_out_buf = payload;
2009 req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
2010
2011 efx_mcdi_execute(enp, &req);
2012
2013 if (req.emr_rc != 0) {
2014 rc = req.emr_rc;
2015 goto fail1;
2016 }
2017
2018 if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
2019 rc = EMSGSIZE;
2020 goto fail2;
2021 }
2022
2023 *pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
2024 if (vfp != NULL)
2025 *vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
2026
2027 return (0);
2028
2029 fail2:
2030 EFSYS_PROBE(fail2);
2031 fail1:
2032 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2033
2034 return (rc);
2035 }
2036
2037 __checkReturn efx_rc_t
efx_mcdi_privilege_mask(__in efx_nic_t * enp,__in uint32_t pf,__in uint32_t vf,__out uint32_t * maskp)2038 efx_mcdi_privilege_mask(
2039 __in efx_nic_t *enp,
2040 __in uint32_t pf,
2041 __in uint32_t vf,
2042 __out uint32_t *maskp)
2043 {
2044 efx_mcdi_req_t req;
2045 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_PRIVILEGE_MASK_IN_LEN,
2046 MC_CMD_PRIVILEGE_MASK_OUT_LEN);
2047 efx_rc_t rc;
2048
2049 req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
2050 req.emr_in_buf = payload;
2051 req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
2052 req.emr_out_buf = payload;
2053 req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
2054
2055 MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
2056 PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
2057 PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
2058
2059 efx_mcdi_execute(enp, &req);
2060
2061 if (req.emr_rc != 0) {
2062 rc = req.emr_rc;
2063 goto fail1;
2064 }
2065
2066 if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
2067 rc = EMSGSIZE;
2068 goto fail2;
2069 }
2070
2071 *maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
2072
2073 return (0);
2074
2075 fail2:
2076 EFSYS_PROBE(fail2);
2077 fail1:
2078 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2079
2080 return (rc);
2081 }
2082
2083 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
2084
2085 __checkReturn efx_rc_t
efx_mcdi_set_workaround(__in efx_nic_t * enp,__in uint32_t type,__in boolean_t enabled,__out_opt uint32_t * flagsp)2086 efx_mcdi_set_workaround(
2087 __in efx_nic_t *enp,
2088 __in uint32_t type,
2089 __in boolean_t enabled,
2090 __out_opt uint32_t *flagsp)
2091 {
2092 efx_mcdi_req_t req;
2093 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN,
2094 MC_CMD_WORKAROUND_EXT_OUT_LEN);
2095 efx_rc_t rc;
2096
2097 req.emr_cmd = MC_CMD_WORKAROUND;
2098 req.emr_in_buf = payload;
2099 req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
2100 req.emr_out_buf = payload;
2101 req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
2102
2103 MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
2104 MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
2105
2106 efx_mcdi_execute_quiet(enp, &req);
2107
2108 if (req.emr_rc != 0) {
2109 rc = req.emr_rc;
2110 goto fail1;
2111 }
2112
2113 if (flagsp != NULL) {
2114 if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
2115 *flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
2116 else
2117 *flagsp = 0;
2118 }
2119
2120 return (0);
2121
2122 fail1:
2123 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2124
2125 return (rc);
2126 }
2127
2128 __checkReturn efx_rc_t
efx_mcdi_get_workarounds(__in efx_nic_t * enp,__out_opt uint32_t * implementedp,__out_opt uint32_t * enabledp)2129 efx_mcdi_get_workarounds(
2130 __in efx_nic_t *enp,
2131 __out_opt uint32_t *implementedp,
2132 __out_opt uint32_t *enabledp)
2133 {
2134 efx_mcdi_req_t req;
2135 EFX_MCDI_DECLARE_BUF(payload, 0, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
2136 efx_rc_t rc;
2137
2138 req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
2139 req.emr_in_buf = NULL;
2140 req.emr_in_length = 0;
2141 req.emr_out_buf = payload;
2142 req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
2143
2144 efx_mcdi_execute(enp, &req);
2145
2146 if (req.emr_rc != 0) {
2147 rc = req.emr_rc;
2148 goto fail1;
2149 }
2150
2151 if (implementedp != NULL) {
2152 *implementedp =
2153 MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
2154 }
2155
2156 if (enabledp != NULL) {
2157 *enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
2158 }
2159
2160 return (0);
2161
2162 fail1:
2163 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2164
2165 return (rc);
2166 }
2167
2168 /*
2169 * Size of media information page in accordance with SFF-8472 and SFF-8436.
2170 * It is used in MCDI interface as well.
2171 */
2172 #define EFX_PHY_MEDIA_INFO_PAGE_SIZE 0x80
2173
2174 /*
2175 * Transceiver identifiers from SFF-8024 Table 4-1.
2176 */
2177 #define EFX_SFF_TRANSCEIVER_ID_SFP 0x03 /* SFP/SFP+/SFP28 */
2178 #define EFX_SFF_TRANSCEIVER_ID_QSFP 0x0c /* QSFP */
2179 #define EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS 0x0d /* QSFP+ or later */
2180 #define EFX_SFF_TRANSCEIVER_ID_QSFP28 0x11 /* QSFP28 or later */
2181
2182 static __checkReturn efx_rc_t
efx_mcdi_get_phy_media_info(__in efx_nic_t * enp,__in uint32_t mcdi_page,__in uint8_t offset,__in uint8_t len,__out_bcount (len)uint8_t * data)2183 efx_mcdi_get_phy_media_info(
2184 __in efx_nic_t *enp,
2185 __in uint32_t mcdi_page,
2186 __in uint8_t offset,
2187 __in uint8_t len,
2188 __out_bcount(len) uint8_t *data)
2189 {
2190 efx_mcdi_req_t req;
2191 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
2192 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
2193 EFX_PHY_MEDIA_INFO_PAGE_SIZE));
2194 efx_rc_t rc;
2195
2196 EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2197
2198 req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
2199 req.emr_in_buf = payload;
2200 req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
2201 req.emr_out_buf = payload;
2202 req.emr_out_length =
2203 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2204
2205 MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
2206
2207 efx_mcdi_execute(enp, &req);
2208
2209 if (req.emr_rc != 0) {
2210 rc = req.emr_rc;
2211 goto fail1;
2212 }
2213
2214 if (req.emr_out_length_used !=
2215 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
2216 rc = EMSGSIZE;
2217 goto fail2;
2218 }
2219
2220 if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
2221 EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2222 rc = EIO;
2223 goto fail3;
2224 }
2225
2226 memcpy(data,
2227 MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
2228 len);
2229
2230 return (0);
2231
2232 fail3:
2233 EFSYS_PROBE(fail3);
2234 fail2:
2235 EFSYS_PROBE(fail2);
2236 fail1:
2237 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2238
2239 return (rc);
2240 }
2241
2242 __checkReturn efx_rc_t
efx_mcdi_phy_module_get_info(__in efx_nic_t * enp,__in uint8_t dev_addr,__in size_t offset,__in size_t len,__out_bcount (len)uint8_t * data)2243 efx_mcdi_phy_module_get_info(
2244 __in efx_nic_t *enp,
2245 __in uint8_t dev_addr,
2246 __in size_t offset,
2247 __in size_t len,
2248 __out_bcount(len) uint8_t *data)
2249 {
2250 efx_port_t *epp = &(enp->en_port);
2251 efx_rc_t rc;
2252 uint32_t mcdi_lower_page;
2253 uint32_t mcdi_upper_page;
2254 uint8_t id;
2255
2256 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2257
2258 /*
2259 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
2260 * Offset plus length interface allows to access page 0 only.
2261 * I.e. non-zero upper pages are not accessible.
2262 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
2263 * QSFP+ Memory Map for details on how information is structured
2264 * and accessible.
2265 */
2266 switch (epp->ep_fixed_port_type) {
2267 case EFX_PHY_MEDIA_SFP_PLUS:
2268 case EFX_PHY_MEDIA_QSFP_PLUS:
2269 /* Port type supports modules */
2270 break;
2271 default:
2272 rc = ENOTSUP;
2273 goto fail1;
2274 }
2275
2276 /*
2277 * For all supported port types, MCDI page 0 offset 0 holds the
2278 * transceiver identifier. Probe to determine the data layout.
2279 * Definitions from SFF-8024 Table 4-1.
2280 */
2281 rc = efx_mcdi_get_phy_media_info(enp, 0, 0, sizeof (id), &id);
2282 if (rc != 0)
2283 goto fail2;
2284
2285 switch (id) {
2286 case EFX_SFF_TRANSCEIVER_ID_SFP:
2287 /*
2288 * In accordance with SFF-8472 Diagnostic Monitoring
2289 * Interface for Optical Transceivers section 4 Memory
2290 * Organization two 2-wire addresses are defined.
2291 */
2292 switch (dev_addr) {
2293 /* Base information */
2294 case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
2295 /*
2296 * MCDI page 0 should be used to access lower
2297 * page 0 (0x00 - 0x7f) at the device address 0xA0.
2298 */
2299 mcdi_lower_page = 0;
2300 /*
2301 * MCDI page 1 should be used to access upper
2302 * page 0 (0x80 - 0xff) at the device address 0xA0.
2303 */
2304 mcdi_upper_page = 1;
2305 break;
2306 /* Diagnostics */
2307 case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
2308 /*
2309 * MCDI page 2 should be used to access lower
2310 * page 0 (0x00 - 0x7f) at the device address 0xA2.
2311 */
2312 mcdi_lower_page = 2;
2313 /*
2314 * MCDI page 3 should be used to access upper
2315 * page 0 (0x80 - 0xff) at the device address 0xA2.
2316 */
2317 mcdi_upper_page = 3;
2318 break;
2319 default:
2320 rc = ENOTSUP;
2321 goto fail3;
2322 }
2323 break;
2324 case EFX_SFF_TRANSCEIVER_ID_QSFP:
2325 case EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS:
2326 case EFX_SFF_TRANSCEIVER_ID_QSFP28:
2327 switch (dev_addr) {
2328 case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
2329 /*
2330 * MCDI page -1 should be used to access lower page 0
2331 * (0x00 - 0x7f).
2332 */
2333 mcdi_lower_page = (uint32_t)-1;
2334 /*
2335 * MCDI page 0 should be used to access upper page 0
2336 * (0x80h - 0xff).
2337 */
2338 mcdi_upper_page = 0;
2339 break;
2340 default:
2341 rc = ENOTSUP;
2342 goto fail3;
2343 }
2344 break;
2345 default:
2346 rc = ENOTSUP;
2347 goto fail3;
2348 }
2349
2350 EFX_STATIC_ASSERT(EFX_PHY_MEDIA_INFO_PAGE_SIZE <= 0xFF);
2351
2352 if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2353 size_t read_len =
2354 MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
2355
2356 rc = efx_mcdi_get_phy_media_info(enp,
2357 mcdi_lower_page, (uint8_t)offset, (uint8_t)read_len, data);
2358 if (rc != 0)
2359 goto fail4;
2360
2361 data += read_len;
2362 len -= read_len;
2363
2364 offset = 0;
2365 } else {
2366 offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
2367 }
2368
2369 if (len > 0) {
2370 EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2371 EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2372
2373 rc = efx_mcdi_get_phy_media_info(enp,
2374 mcdi_upper_page, (uint8_t)offset, (uint8_t)len, data);
2375 if (rc != 0)
2376 goto fail5;
2377 }
2378
2379 return (0);
2380
2381 fail5:
2382 EFSYS_PROBE(fail5);
2383 fail4:
2384 EFSYS_PROBE(fail4);
2385 fail3:
2386 EFSYS_PROBE(fail3);
2387 fail2:
2388 EFSYS_PROBE(fail2);
2389 fail1:
2390 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2391
2392 return (rc);
2393 }
2394
2395 #endif /* EFSYS_OPT_MCDI */
2396