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 const char *reason;
1181 unsigned int flags;
1182 unsigned int index;
1183 unsigned int ofst;
1184 int retry;
1185 efx_rc_t rc;
1186
1187 /*
1188 * Before we attempt to chat to the MC, we should verify that the MC
1189 * isn't in its assertion handler, either due to a previous reboot,
1190 * or because we're reinitializing due to an eec_exception().
1191 *
1192 * Use GET_ASSERTS to read any assertion state that may be present.
1193 * Retry this command twice. Once because a boot-time assertion failure
1194 * might cause the 1st MCDI request to fail. And once again because
1195 * we might race with efx_mcdi_exit_assertion_handler() running on
1196 * partner port(s) on the same NIC.
1197 */
1198 retry = 2;
1199 do {
1200 (void) memset(payload, 0, sizeof (payload));
1201 req.emr_cmd = MC_CMD_GET_ASSERTS;
1202 req.emr_in_buf = payload;
1203 req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
1204 req.emr_out_buf = payload;
1205 req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
1206
1207 MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
1208 efx_mcdi_execute_quiet(enp, &req);
1209
1210 } while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
1211
1212 if (req.emr_rc != 0) {
1213 if (req.emr_rc == EACCES) {
1214 /* Unprivileged functions cannot clear assertions. */
1215 goto out;
1216 }
1217 rc = req.emr_rc;
1218 goto fail1;
1219 }
1220
1221 if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
1222 rc = EMSGSIZE;
1223 goto fail2;
1224 }
1225
1226 /* Print out any assertion state recorded */
1227 flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
1228 if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
1229 return (0);
1230
1231 reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
1232 ? "system-level assertion"
1233 : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
1234 ? "thread-level assertion"
1235 : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
1236 ? "watchdog reset"
1237 : (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
1238 ? "illegal address trap"
1239 : "unknown assertion";
1240 EFSYS_PROBE3(mcpu_assertion,
1241 const char *, reason, unsigned int,
1242 MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
1243 unsigned int,
1244 MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
1245
1246 /* Print out the registers (r1 ... r31) */
1247 ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
1248 for (index = 1;
1249 index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
1250 index++) {
1251 EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
1252 EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
1253 EFX_DWORD_0));
1254 ofst += sizeof (efx_dword_t);
1255 }
1256 EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
1257
1258 out:
1259 return (0);
1260
1261 fail2:
1262 EFSYS_PROBE(fail2);
1263 fail1:
1264 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1265
1266 return (rc);
1267 }
1268
1269 /*
1270 * Internal routines for specific MCDI requests.
1271 */
1272
1273 __checkReturn efx_rc_t
efx_mcdi_drv_attach(__in efx_nic_t * enp,__in boolean_t attach)1274 efx_mcdi_drv_attach(
1275 __in efx_nic_t *enp,
1276 __in boolean_t attach)
1277 {
1278 efx_mcdi_req_t req;
1279 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_DRV_ATTACH_IN_LEN,
1280 MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
1281 efx_rc_t rc;
1282
1283 req.emr_cmd = MC_CMD_DRV_ATTACH;
1284 req.emr_in_buf = payload;
1285 req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
1286 req.emr_out_buf = payload;
1287 req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
1288
1289 /*
1290 * Typically, client drivers use DONT_CARE for the datapath firmware
1291 * type to ensure that the driver can attach to an unprivileged
1292 * function. The datapath firmware type to use is controlled by the
1293 * 'sfboot' utility.
1294 * If a client driver wishes to attach with a specific datapath firmware
1295 * type, that can be passed in second argument of efx_nic_probe API. One
1296 * such example is the ESXi native driver that attempts attaching with
1297 * FULL_FEATURED datapath firmware type first and fall backs to
1298 * DONT_CARE datapath firmware type if MC_CMD_DRV_ATTACH fails.
1299 */
1300 MCDI_IN_POPULATE_DWORD_2(req, DRV_ATTACH_IN_NEW_STATE,
1301 DRV_ATTACH_IN_ATTACH, attach ? 1 : 0,
1302 DRV_ATTACH_IN_SUBVARIANT_AWARE, EFSYS_OPT_FW_SUBVARIANT_AWARE);
1303 MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
1304 MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, enp->efv);
1305
1306 efx_mcdi_execute(enp, &req);
1307
1308 if (req.emr_rc != 0) {
1309 rc = req.emr_rc;
1310 goto fail1;
1311 }
1312
1313 if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
1314 rc = EMSGSIZE;
1315 goto fail2;
1316 }
1317
1318 return (0);
1319
1320 fail2:
1321 EFSYS_PROBE(fail2);
1322 fail1:
1323 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1324
1325 return (rc);
1326 }
1327
1328 __checkReturn efx_rc_t
1329 efx_mcdi_get_board_cfg(
1330 __in efx_nic_t *enp,
1331 __out_opt uint32_t *board_typep,
1332 __out_opt efx_dword_t *capabilitiesp,
1333 __out_ecount_opt(6) uint8_t mac_addrp[6])
1334 {
1335 efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1336 efx_mcdi_req_t req;
1337 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN,
1338 MC_CMD_GET_BOARD_CFG_OUT_LENMIN);
1339 efx_rc_t rc;
1340
1341 req.emr_cmd = MC_CMD_GET_BOARD_CFG;
1342 req.emr_in_buf = payload;
1343 req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
1344 req.emr_out_buf = payload;
1345 req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
1346
1347 efx_mcdi_execute(enp, &req);
1348
1349 if (req.emr_rc != 0) {
1350 rc = req.emr_rc;
1351 goto fail1;
1352 }
1353
1354 if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
1355 rc = EMSGSIZE;
1356 goto fail2;
1357 }
1358
1359 if (mac_addrp != NULL) {
1360 uint8_t *addrp;
1361
1362 if (emip->emi_port == 1) {
1363 addrp = MCDI_OUT2(req, uint8_t,
1364 GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
1365 } else if (emip->emi_port == 2) {
1366 addrp = MCDI_OUT2(req, uint8_t,
1367 GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
1368 } else {
1369 rc = EINVAL;
1370 goto fail3;
1371 }
1372
1373 EFX_MAC_ADDR_COPY(mac_addrp, addrp);
1374 }
1375
1376 if (capabilitiesp != NULL) {
1377 if (emip->emi_port == 1) {
1378 *capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1379 GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
1380 } else if (emip->emi_port == 2) {
1381 *capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1382 GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
1383 } else {
1384 rc = EINVAL;
1385 goto fail4;
1386 }
1387 }
1388
1389 if (board_typep != NULL) {
1390 *board_typep = MCDI_OUT_DWORD(req,
1391 GET_BOARD_CFG_OUT_BOARD_TYPE);
1392 }
1393
1394 return (0);
1395
1396 fail4:
1397 EFSYS_PROBE(fail4);
1398 fail3:
1399 EFSYS_PROBE(fail3);
1400 fail2:
1401 EFSYS_PROBE(fail2);
1402 fail1:
1403 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1404
1405 return (rc);
1406 }
1407
1408 __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)1409 efx_mcdi_get_resource_limits(
1410 __in efx_nic_t *enp,
1411 __out_opt uint32_t *nevqp,
1412 __out_opt uint32_t *nrxqp,
1413 __out_opt uint32_t *ntxqp)
1414 {
1415 efx_mcdi_req_t req;
1416 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
1417 MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN);
1418 efx_rc_t rc;
1419
1420 req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
1421 req.emr_in_buf = payload;
1422 req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
1423 req.emr_out_buf = payload;
1424 req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
1425
1426 efx_mcdi_execute(enp, &req);
1427
1428 if (req.emr_rc != 0) {
1429 rc = req.emr_rc;
1430 goto fail1;
1431 }
1432
1433 if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
1434 rc = EMSGSIZE;
1435 goto fail2;
1436 }
1437
1438 if (nevqp != NULL)
1439 *nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
1440 if (nrxqp != NULL)
1441 *nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
1442 if (ntxqp != NULL)
1443 *ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
1444
1445 return (0);
1446
1447 fail2:
1448 EFSYS_PROBE(fail2);
1449 fail1:
1450 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1451
1452 return (rc);
1453 }
1454
1455 __checkReturn efx_rc_t
efx_mcdi_get_phy_cfg(__in efx_nic_t * enp)1456 efx_mcdi_get_phy_cfg(
1457 __in efx_nic_t *enp)
1458 {
1459 efx_port_t *epp = &(enp->en_port);
1460 efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1461 efx_mcdi_req_t req;
1462 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_CFG_IN_LEN,
1463 MC_CMD_GET_PHY_CFG_OUT_LEN);
1464 #if EFSYS_OPT_NAMES
1465 const char *namep;
1466 size_t namelen;
1467 #endif
1468 uint32_t phy_media_type;
1469 efx_rc_t rc;
1470
1471 req.emr_cmd = MC_CMD_GET_PHY_CFG;
1472 req.emr_in_buf = payload;
1473 req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
1474 req.emr_out_buf = payload;
1475 req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
1476
1477 efx_mcdi_execute(enp, &req);
1478
1479 if (req.emr_rc != 0) {
1480 rc = req.emr_rc;
1481 goto fail1;
1482 }
1483
1484 if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
1485 rc = EMSGSIZE;
1486 goto fail2;
1487 }
1488
1489 encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
1490 #if EFSYS_OPT_NAMES
1491 namep = MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME);
1492 namelen = MIN(sizeof (encp->enc_phy_name) - 1,
1493 strnlen(namep, MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
1494 (void) memset(encp->enc_phy_name, 0,
1495 sizeof (encp->enc_phy_name));
1496 memcpy(encp->enc_phy_name, namep, namelen);
1497 #endif /* EFSYS_OPT_NAMES */
1498 (void) memset(encp->enc_phy_revision, 0,
1499 sizeof (encp->enc_phy_revision));
1500 memcpy(encp->enc_phy_revision,
1501 MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
1502 MIN(sizeof (encp->enc_phy_revision) - 1,
1503 MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
1504 #if EFSYS_OPT_PHY_LED_CONTROL
1505 encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
1506 (1 << EFX_PHY_LED_OFF) |
1507 (1 << EFX_PHY_LED_ON));
1508 #endif /* EFSYS_OPT_PHY_LED_CONTROL */
1509
1510 /* Get the media type of the fixed port, if recognised. */
1511 EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
1512 EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
1513 EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
1514 EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
1515 EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
1516 EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
1517 EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
1518 phy_media_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
1519 epp->ep_fixed_port_type = (efx_phy_media_type_t) phy_media_type;
1520 if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
1521 epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
1522
1523 epp->ep_phy_cap_mask =
1524 MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
1525 #if EFSYS_OPT_PHY_FLAGS
1526 encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
1527 #endif /* EFSYS_OPT_PHY_FLAGS */
1528
1529 encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
1530
1531 /* Populate internal state */
1532 encp->enc_mcdi_mdio_channel =
1533 (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
1534
1535 #if EFSYS_OPT_PHY_STATS
1536 encp->enc_mcdi_phy_stat_mask =
1537 MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
1538 #endif /* EFSYS_OPT_PHY_STATS */
1539
1540 #if EFSYS_OPT_BIST
1541 encp->enc_bist_mask = 0;
1542 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1543 GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
1544 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
1545 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1546 GET_PHY_CFG_OUT_BIST_CABLE_LONG))
1547 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
1548 if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1549 GET_PHY_CFG_OUT_BIST))
1550 encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
1551 #endif /* EFSYS_OPT_BIST */
1552
1553 return (0);
1554
1555 fail2:
1556 EFSYS_PROBE(fail2);
1557 fail1:
1558 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1559
1560 return (rc);
1561 }
1562
1563 __checkReturn efx_rc_t
efx_mcdi_firmware_update_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1564 efx_mcdi_firmware_update_supported(
1565 __in efx_nic_t *enp,
1566 __out boolean_t *supportedp)
1567 {
1568 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1569 efx_rc_t rc;
1570
1571 if (emcop != NULL) {
1572 if ((rc = emcop->emco_feature_supported(enp,
1573 EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
1574 goto fail1;
1575 } else {
1576 /* Earlier devices always supported updates */
1577 *supportedp = B_TRUE;
1578 }
1579
1580 return (0);
1581
1582 fail1:
1583 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1584
1585 return (rc);
1586 }
1587
1588 __checkReturn efx_rc_t
efx_mcdi_macaddr_change_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1589 efx_mcdi_macaddr_change_supported(
1590 __in efx_nic_t *enp,
1591 __out boolean_t *supportedp)
1592 {
1593 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1594 efx_rc_t rc;
1595
1596 if (emcop != NULL) {
1597 if ((rc = emcop->emco_feature_supported(enp,
1598 EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
1599 goto fail1;
1600 } else {
1601 /* Earlier devices always supported MAC changes */
1602 *supportedp = B_TRUE;
1603 }
1604
1605 return (0);
1606
1607 fail1:
1608 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1609
1610 return (rc);
1611 }
1612
1613 __checkReturn efx_rc_t
efx_mcdi_link_control_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1614 efx_mcdi_link_control_supported(
1615 __in efx_nic_t *enp,
1616 __out boolean_t *supportedp)
1617 {
1618 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1619 efx_rc_t rc;
1620
1621 if (emcop != NULL) {
1622 if ((rc = emcop->emco_feature_supported(enp,
1623 EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
1624 goto fail1;
1625 } else {
1626 /* Earlier devices always supported link control */
1627 *supportedp = B_TRUE;
1628 }
1629
1630 return (0);
1631
1632 fail1:
1633 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1634
1635 return (rc);
1636 }
1637
1638 __checkReturn efx_rc_t
efx_mcdi_mac_spoofing_supported(__in efx_nic_t * enp,__out boolean_t * supportedp)1639 efx_mcdi_mac_spoofing_supported(
1640 __in efx_nic_t *enp,
1641 __out boolean_t *supportedp)
1642 {
1643 const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1644 efx_rc_t rc;
1645
1646 if (emcop != NULL) {
1647 if ((rc = emcop->emco_feature_supported(enp,
1648 EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
1649 goto fail1;
1650 } else {
1651 /* Earlier devices always supported MAC spoofing */
1652 *supportedp = B_TRUE;
1653 }
1654
1655 return (0);
1656
1657 fail1:
1658 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1659
1660 return (rc);
1661 }
1662
1663 #if EFSYS_OPT_BIST
1664
1665 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1666 /*
1667 * Enter bist offline mode. This is a fw mode which puts the NIC into a state
1668 * where memory BIST tests can be run and not much else can interfere or happen.
1669 * A reboot is required to exit this mode.
1670 */
1671 __checkReturn efx_rc_t
efx_mcdi_bist_enable_offline(__in efx_nic_t * enp)1672 efx_mcdi_bist_enable_offline(
1673 __in efx_nic_t *enp)
1674 {
1675 efx_mcdi_req_t req;
1676 efx_rc_t rc;
1677
1678 EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
1679 EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
1680
1681 req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
1682 req.emr_in_buf = NULL;
1683 req.emr_in_length = 0;
1684 req.emr_out_buf = NULL;
1685 req.emr_out_length = 0;
1686
1687 efx_mcdi_execute(enp, &req);
1688
1689 if (req.emr_rc != 0) {
1690 rc = req.emr_rc;
1691 goto fail1;
1692 }
1693
1694 return (0);
1695
1696 fail1:
1697 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1698
1699 return (rc);
1700 }
1701 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
1702
1703 __checkReturn efx_rc_t
efx_mcdi_bist_start(__in efx_nic_t * enp,__in efx_bist_type_t type)1704 efx_mcdi_bist_start(
1705 __in efx_nic_t *enp,
1706 __in efx_bist_type_t type)
1707 {
1708 efx_mcdi_req_t req;
1709 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_START_BIST_IN_LEN,
1710 MC_CMD_START_BIST_OUT_LEN);
1711 efx_rc_t rc;
1712
1713 req.emr_cmd = MC_CMD_START_BIST;
1714 req.emr_in_buf = payload;
1715 req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
1716 req.emr_out_buf = payload;
1717 req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
1718
1719 switch (type) {
1720 case EFX_BIST_TYPE_PHY_NORMAL:
1721 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
1722 break;
1723 case EFX_BIST_TYPE_PHY_CABLE_SHORT:
1724 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1725 MC_CMD_PHY_BIST_CABLE_SHORT);
1726 break;
1727 case EFX_BIST_TYPE_PHY_CABLE_LONG:
1728 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1729 MC_CMD_PHY_BIST_CABLE_LONG);
1730 break;
1731 case EFX_BIST_TYPE_MC_MEM:
1732 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1733 MC_CMD_MC_MEM_BIST);
1734 break;
1735 case EFX_BIST_TYPE_SAT_MEM:
1736 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1737 MC_CMD_PORT_MEM_BIST);
1738 break;
1739 case EFX_BIST_TYPE_REG:
1740 MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1741 MC_CMD_REG_BIST);
1742 break;
1743 default:
1744 EFSYS_ASSERT(0);
1745 }
1746
1747 efx_mcdi_execute(enp, &req);
1748
1749 if (req.emr_rc != 0) {
1750 rc = req.emr_rc;
1751 goto fail1;
1752 }
1753
1754 return (0);
1755
1756 fail1:
1757 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1758
1759 return (rc);
1760 }
1761
1762 #endif /* EFSYS_OPT_BIST */
1763
1764 /* Enable logging of some events (e.g. link state changes) */
1765 __checkReturn efx_rc_t
efx_mcdi_log_ctrl(__in efx_nic_t * enp)1766 efx_mcdi_log_ctrl(
1767 __in efx_nic_t *enp)
1768 {
1769 efx_mcdi_req_t req;
1770 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_LOG_CTRL_IN_LEN,
1771 MC_CMD_LOG_CTRL_OUT_LEN);
1772 efx_rc_t rc;
1773
1774 req.emr_cmd = MC_CMD_LOG_CTRL;
1775 req.emr_in_buf = payload;
1776 req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
1777 req.emr_out_buf = payload;
1778 req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
1779
1780 MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
1781 MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
1782 MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
1783
1784 efx_mcdi_execute(enp, &req);
1785
1786 if (req.emr_rc != 0) {
1787 rc = req.emr_rc;
1788 goto fail1;
1789 }
1790
1791 return (0);
1792
1793 fail1:
1794 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1795
1796 return (rc);
1797 }
1798
1799 #if EFSYS_OPT_MAC_STATS
1800
1801 typedef enum efx_stats_action_e {
1802 EFX_STATS_CLEAR,
1803 EFX_STATS_UPLOAD,
1804 EFX_STATS_ENABLE_NOEVENTS,
1805 EFX_STATS_ENABLE_EVENTS,
1806 EFX_STATS_DISABLE,
1807 } efx_stats_action_t;
1808
1809 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)1810 efx_mcdi_mac_stats(
1811 __in efx_nic_t *enp,
1812 __in_opt efsys_mem_t *esmp,
1813 __in efx_stats_action_t action,
1814 __in uint16_t period_ms)
1815 {
1816 efx_mcdi_req_t req;
1817 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAC_STATS_IN_LEN,
1818 MC_CMD_MAC_STATS_V2_OUT_DMA_LEN);
1819 int clear = (action == EFX_STATS_CLEAR);
1820 int upload = (action == EFX_STATS_UPLOAD);
1821 int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
1822 int events = (action == EFX_STATS_ENABLE_EVENTS);
1823 int disable = (action == EFX_STATS_DISABLE);
1824 efx_rc_t rc;
1825
1826 req.emr_cmd = MC_CMD_MAC_STATS;
1827 req.emr_in_buf = payload;
1828 req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
1829 req.emr_out_buf = payload;
1830 req.emr_out_length = MC_CMD_MAC_STATS_V2_OUT_DMA_LEN;
1831
1832 MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
1833 MAC_STATS_IN_DMA, upload,
1834 MAC_STATS_IN_CLEAR, clear,
1835 MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
1836 MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
1837 MAC_STATS_IN_PERIODIC_NOEVENT, !events,
1838 MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
1839
1840 if (enable || events || upload) {
1841 const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
1842 uint32_t bytes;
1843
1844 /* Periodic stats or stats upload require a DMA buffer */
1845 if (esmp == NULL) {
1846 rc = EINVAL;
1847 goto fail1;
1848 }
1849
1850 if (encp->enc_mac_stats_nstats < MC_CMD_MAC_NSTATS) {
1851 /* MAC stats count too small for legacy MAC stats */
1852 rc = ENOSPC;
1853 goto fail2;
1854 }
1855
1856 bytes = encp->enc_mac_stats_nstats * sizeof (efx_qword_t);
1857
1858 if (EFSYS_MEM_SIZE(esmp) < bytes) {
1859 /* DMA buffer too small */
1860 rc = ENOSPC;
1861 goto fail3;
1862 }
1863
1864 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
1865 EFSYS_MEM_ADDR(esmp) & 0xffffffff);
1866 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
1867 EFSYS_MEM_ADDR(esmp) >> 32);
1868 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
1869 }
1870
1871 /*
1872 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
1873 * as this may fail (and leave periodic DMA enabled) if the
1874 * vadapter has already been deleted.
1875 */
1876 MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
1877 (disable ? EVB_PORT_ID_NULL : enp->en_vport_id));
1878
1879 efx_mcdi_execute(enp, &req);
1880
1881 if (req.emr_rc != 0) {
1882 /* EF10: Expect ENOENT if no DMA queues are initialised */
1883 if ((req.emr_rc != ENOENT) ||
1884 (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
1885 rc = req.emr_rc;
1886 goto fail4;
1887 }
1888 }
1889
1890 return (0);
1891
1892 fail4:
1893 EFSYS_PROBE(fail4);
1894 fail3:
1895 EFSYS_PROBE(fail3);
1896 fail2:
1897 EFSYS_PROBE(fail2);
1898 fail1:
1899 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1900
1901 return (rc);
1902 }
1903
1904 __checkReturn efx_rc_t
efx_mcdi_mac_stats_clear(__in efx_nic_t * enp)1905 efx_mcdi_mac_stats_clear(
1906 __in efx_nic_t *enp)
1907 {
1908 efx_rc_t rc;
1909
1910 if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR, 0)) != 0)
1911 goto fail1;
1912
1913 return (0);
1914
1915 fail1:
1916 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1917
1918 return (rc);
1919 }
1920
1921 __checkReturn efx_rc_t
efx_mcdi_mac_stats_upload(__in efx_nic_t * enp,__in efsys_mem_t * esmp)1922 efx_mcdi_mac_stats_upload(
1923 __in efx_nic_t *enp,
1924 __in efsys_mem_t *esmp)
1925 {
1926 efx_rc_t rc;
1927
1928 /*
1929 * The MC DMAs aggregate statistics for our convenience, so we can
1930 * avoid having to pull the statistics buffer into the cache to
1931 * maintain cumulative statistics.
1932 */
1933 if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD, 0)) != 0)
1934 goto fail1;
1935
1936 return (0);
1937
1938 fail1:
1939 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1940
1941 return (rc);
1942 }
1943
1944 __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)1945 efx_mcdi_mac_stats_periodic(
1946 __in efx_nic_t *enp,
1947 __in efsys_mem_t *esmp,
1948 __in uint16_t period_ms,
1949 __in boolean_t events)
1950 {
1951 efx_rc_t rc;
1952
1953 /*
1954 * The MC DMAs aggregate statistics for our convenience, so we can
1955 * avoid having to pull the statistics buffer into the cache to
1956 * maintain cumulative statistics.
1957 * Huntington uses a fixed 1sec period.
1958 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
1959 */
1960 if (period_ms == 0)
1961 rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE, 0);
1962 else if (events)
1963 rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS,
1964 period_ms);
1965 else
1966 rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS,
1967 period_ms);
1968
1969 if (rc != 0)
1970 goto fail1;
1971
1972 return (0);
1973
1974 fail1:
1975 EFSYS_PROBE1(fail1, efx_rc_t, rc);
1976
1977 return (rc);
1978 }
1979
1980 #endif /* EFSYS_OPT_MAC_STATS */
1981
1982 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
1983
1984 /*
1985 * This function returns the pf and vf number of a function. If it is a pf the
1986 * vf number is 0xffff. The vf number is the index of the vf on that
1987 * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
1988 * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
1989 */
1990 __checkReturn efx_rc_t
efx_mcdi_get_function_info(__in efx_nic_t * enp,__out uint32_t * pfp,__out_opt uint32_t * vfp)1991 efx_mcdi_get_function_info(
1992 __in efx_nic_t *enp,
1993 __out uint32_t *pfp,
1994 __out_opt uint32_t *vfp)
1995 {
1996 efx_mcdi_req_t req;
1997 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
1998 MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
1999 efx_rc_t rc;
2000
2001 req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
2002 req.emr_in_buf = payload;
2003 req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
2004 req.emr_out_buf = payload;
2005 req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
2006
2007 efx_mcdi_execute(enp, &req);
2008
2009 if (req.emr_rc != 0) {
2010 rc = req.emr_rc;
2011 goto fail1;
2012 }
2013
2014 if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
2015 rc = EMSGSIZE;
2016 goto fail2;
2017 }
2018
2019 *pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
2020 if (vfp != NULL)
2021 *vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
2022
2023 return (0);
2024
2025 fail2:
2026 EFSYS_PROBE(fail2);
2027 fail1:
2028 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2029
2030 return (rc);
2031 }
2032
2033 __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)2034 efx_mcdi_privilege_mask(
2035 __in efx_nic_t *enp,
2036 __in uint32_t pf,
2037 __in uint32_t vf,
2038 __out uint32_t *maskp)
2039 {
2040 efx_mcdi_req_t req;
2041 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_PRIVILEGE_MASK_IN_LEN,
2042 MC_CMD_PRIVILEGE_MASK_OUT_LEN);
2043 efx_rc_t rc;
2044
2045 req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
2046 req.emr_in_buf = payload;
2047 req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
2048 req.emr_out_buf = payload;
2049 req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
2050
2051 MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
2052 PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
2053 PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
2054
2055 efx_mcdi_execute(enp, &req);
2056
2057 if (req.emr_rc != 0) {
2058 rc = req.emr_rc;
2059 goto fail1;
2060 }
2061
2062 if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
2063 rc = EMSGSIZE;
2064 goto fail2;
2065 }
2066
2067 *maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
2068
2069 return (0);
2070
2071 fail2:
2072 EFSYS_PROBE(fail2);
2073 fail1:
2074 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2075
2076 return (rc);
2077 }
2078
2079 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
2080
2081 __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)2082 efx_mcdi_set_workaround(
2083 __in efx_nic_t *enp,
2084 __in uint32_t type,
2085 __in boolean_t enabled,
2086 __out_opt uint32_t *flagsp)
2087 {
2088 efx_mcdi_req_t req;
2089 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN,
2090 MC_CMD_WORKAROUND_EXT_OUT_LEN);
2091 efx_rc_t rc;
2092
2093 req.emr_cmd = MC_CMD_WORKAROUND;
2094 req.emr_in_buf = payload;
2095 req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
2096 req.emr_out_buf = payload;
2097 req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
2098
2099 MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
2100 MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
2101
2102 efx_mcdi_execute_quiet(enp, &req);
2103
2104 if (req.emr_rc != 0) {
2105 rc = req.emr_rc;
2106 goto fail1;
2107 }
2108
2109 if (flagsp != NULL) {
2110 if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
2111 *flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
2112 else
2113 *flagsp = 0;
2114 }
2115
2116 return (0);
2117
2118 fail1:
2119 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2120
2121 return (rc);
2122 }
2123
2124 __checkReturn efx_rc_t
efx_mcdi_get_workarounds(__in efx_nic_t * enp,__out_opt uint32_t * implementedp,__out_opt uint32_t * enabledp)2125 efx_mcdi_get_workarounds(
2126 __in efx_nic_t *enp,
2127 __out_opt uint32_t *implementedp,
2128 __out_opt uint32_t *enabledp)
2129 {
2130 efx_mcdi_req_t req;
2131 EFX_MCDI_DECLARE_BUF(payload, 0, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
2132 efx_rc_t rc;
2133
2134 req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
2135 req.emr_in_buf = NULL;
2136 req.emr_in_length = 0;
2137 req.emr_out_buf = payload;
2138 req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
2139
2140 efx_mcdi_execute(enp, &req);
2141
2142 if (req.emr_rc != 0) {
2143 rc = req.emr_rc;
2144 goto fail1;
2145 }
2146
2147 if (implementedp != NULL) {
2148 *implementedp =
2149 MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
2150 }
2151
2152 if (enabledp != NULL) {
2153 *enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
2154 }
2155
2156 return (0);
2157
2158 fail1:
2159 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2160
2161 return (rc);
2162 }
2163
2164 /*
2165 * Size of media information page in accordance with SFF-8472 and SFF-8436.
2166 * It is used in MCDI interface as well.
2167 */
2168 #define EFX_PHY_MEDIA_INFO_PAGE_SIZE 0x80
2169
2170 /*
2171 * Transceiver identifiers from SFF-8024 Table 4-1.
2172 */
2173 #define EFX_SFF_TRANSCEIVER_ID_SFP 0x03 /* SFP/SFP+/SFP28 */
2174 #define EFX_SFF_TRANSCEIVER_ID_QSFP 0x0c /* QSFP */
2175 #define EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS 0x0d /* QSFP+ or later */
2176 #define EFX_SFF_TRANSCEIVER_ID_QSFP28 0x11 /* QSFP28 or later */
2177
2178 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)2179 efx_mcdi_get_phy_media_info(
2180 __in efx_nic_t *enp,
2181 __in uint32_t mcdi_page,
2182 __in uint8_t offset,
2183 __in uint8_t len,
2184 __out_bcount(len) uint8_t *data)
2185 {
2186 efx_mcdi_req_t req;
2187 EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
2188 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
2189 EFX_PHY_MEDIA_INFO_PAGE_SIZE));
2190 efx_rc_t rc;
2191
2192 EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2193
2194 req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
2195 req.emr_in_buf = payload;
2196 req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
2197 req.emr_out_buf = payload;
2198 req.emr_out_length =
2199 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2200
2201 MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
2202
2203 efx_mcdi_execute(enp, &req);
2204
2205 if (req.emr_rc != 0) {
2206 rc = req.emr_rc;
2207 goto fail1;
2208 }
2209
2210 if (req.emr_out_length_used !=
2211 MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
2212 rc = EMSGSIZE;
2213 goto fail2;
2214 }
2215
2216 if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
2217 EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2218 rc = EIO;
2219 goto fail3;
2220 }
2221
2222 memcpy(data,
2223 MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
2224 len);
2225
2226 return (0);
2227
2228 fail3:
2229 EFSYS_PROBE(fail3);
2230 fail2:
2231 EFSYS_PROBE(fail2);
2232 fail1:
2233 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2234
2235 return (rc);
2236 }
2237
2238 __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)2239 efx_mcdi_phy_module_get_info(
2240 __in efx_nic_t *enp,
2241 __in uint8_t dev_addr,
2242 __in size_t offset,
2243 __in size_t len,
2244 __out_bcount(len) uint8_t *data)
2245 {
2246 efx_port_t *epp = &(enp->en_port);
2247 efx_rc_t rc;
2248 uint32_t mcdi_lower_page;
2249 uint32_t mcdi_upper_page;
2250 uint8_t id;
2251
2252 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2253
2254 /*
2255 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
2256 * Offset plus length interface allows to access page 0 only.
2257 * I.e. non-zero upper pages are not accessible.
2258 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
2259 * QSFP+ Memory Map for details on how information is structured
2260 * and accessible.
2261 */
2262 switch (epp->ep_fixed_port_type) {
2263 case EFX_PHY_MEDIA_SFP_PLUS:
2264 case EFX_PHY_MEDIA_QSFP_PLUS:
2265 /* Port type supports modules */
2266 break;
2267 default:
2268 rc = ENOTSUP;
2269 goto fail1;
2270 }
2271
2272 /*
2273 * For all supported port types, MCDI page 0 offset 0 holds the
2274 * transceiver identifier. Probe to determine the data layout.
2275 * Definitions from SFF-8024 Table 4-1.
2276 */
2277 rc = efx_mcdi_get_phy_media_info(enp, 0, 0, sizeof (id), &id);
2278 if (rc != 0)
2279 goto fail2;
2280
2281 switch (id) {
2282 case EFX_SFF_TRANSCEIVER_ID_SFP:
2283 /*
2284 * In accordance with SFF-8472 Diagnostic Monitoring
2285 * Interface for Optical Transceivers section 4 Memory
2286 * Organization two 2-wire addresses are defined.
2287 */
2288 switch (dev_addr) {
2289 /* Base information */
2290 case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
2291 /*
2292 * MCDI page 0 should be used to access lower
2293 * page 0 (0x00 - 0x7f) at the device address 0xA0.
2294 */
2295 mcdi_lower_page = 0;
2296 /*
2297 * MCDI page 1 should be used to access upper
2298 * page 0 (0x80 - 0xff) at the device address 0xA0.
2299 */
2300 mcdi_upper_page = 1;
2301 break;
2302 /* Diagnostics */
2303 case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
2304 /*
2305 * MCDI page 2 should be used to access lower
2306 * page 0 (0x00 - 0x7f) at the device address 0xA2.
2307 */
2308 mcdi_lower_page = 2;
2309 /*
2310 * MCDI page 3 should be used to access upper
2311 * page 0 (0x80 - 0xff) at the device address 0xA2.
2312 */
2313 mcdi_upper_page = 3;
2314 break;
2315 default:
2316 rc = ENOTSUP;
2317 goto fail3;
2318 }
2319 break;
2320 case EFX_SFF_TRANSCEIVER_ID_QSFP:
2321 case EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS:
2322 case EFX_SFF_TRANSCEIVER_ID_QSFP28:
2323 switch (dev_addr) {
2324 case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
2325 /*
2326 * MCDI page -1 should be used to access lower page 0
2327 * (0x00 - 0x7f).
2328 */
2329 mcdi_lower_page = (uint32_t)-1;
2330 /*
2331 * MCDI page 0 should be used to access upper page 0
2332 * (0x80h - 0xff).
2333 */
2334 mcdi_upper_page = 0;
2335 break;
2336 default:
2337 rc = ENOTSUP;
2338 goto fail3;
2339 }
2340 break;
2341 default:
2342 rc = ENOTSUP;
2343 goto fail3;
2344 }
2345
2346 EFX_STATIC_ASSERT(EFX_PHY_MEDIA_INFO_PAGE_SIZE <= 0xFF);
2347
2348 if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2349 size_t read_len =
2350 MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
2351
2352 rc = efx_mcdi_get_phy_media_info(enp,
2353 mcdi_lower_page, (uint8_t)offset, (uint8_t)read_len, data);
2354 if (rc != 0)
2355 goto fail4;
2356
2357 data += read_len;
2358 len -= read_len;
2359
2360 offset = 0;
2361 } else {
2362 offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
2363 }
2364
2365 if (len > 0) {
2366 EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2367 EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2368
2369 rc = efx_mcdi_get_phy_media_info(enp,
2370 mcdi_upper_page, (uint8_t)offset, (uint8_t)len, data);
2371 if (rc != 0)
2372 goto fail5;
2373 }
2374
2375 return (0);
2376
2377 fail5:
2378 EFSYS_PROBE(fail5);
2379 fail4:
2380 EFSYS_PROBE(fail4);
2381 fail3:
2382 EFSYS_PROBE(fail3);
2383 fail2:
2384 EFSYS_PROBE(fail2);
2385 fail1:
2386 EFSYS_PROBE1(fail1, efx_rc_t, rc);
2387
2388 return (rc);
2389 }
2390
2391 #endif /* EFSYS_OPT_MCDI */
2392