1 /*-
2 * Copyright 2008-2009 Solarflare Communications Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include "efsys.h"
30 #include "efx.h"
31 #include "efx_types.h"
32 #include "efx_regs.h"
33 #include "efx_regs_mcdi.h"
34 #include "efx_impl.h"
35
36 #if EFSYS_OPT_MCDI
37
38 /*
39 * A reboot/assertion causes the MCDI status word to be set after the
40 * command word is set or a REBOOT event is sent. If we notice a reboot
41 * via these mechanisms then wait 10ms for the status word to be set.
42 */
43 #define MCDI_STATUS_SLEEP_US 10000
44
45 void
efx_mcdi_request_start(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__in boolean_t ev_cpl)46 efx_mcdi_request_start(
47 __in efx_nic_t *enp,
48 __in efx_mcdi_req_t *emrp,
49 __in boolean_t ev_cpl)
50 {
51 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
52 efx_dword_t dword;
53 unsigned int seq;
54 unsigned int xflags;
55 unsigned int pdur;
56 unsigned int dbr;
57 unsigned int pos;
58 int state;
59
60 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
61 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
62 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
63
64 switch (emip->emi_port) {
65 case 1:
66 pdur = MC_SMEM_P0_PDU_OFST >> 2;
67 dbr = MC_SMEM_P0_DOORBELL_OFST >> 2;
68 break;
69 case 2:
70 pdur = MC_SMEM_P1_PDU_OFST >> 2;
71 dbr = MC_SMEM_P1_DOORBELL_OFST >> 2;
72 break;
73 default:
74 EFSYS_ASSERT(0);
75 pdur = dbr = 0;
76 };
77
78 /*
79 * efx_mcdi_request_start() is naturally serialised against both
80 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
81 * by virtue of there only being one outstanding MCDI request.
82 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
83 * at any time, to timeout a pending mcdi request, That request may
84 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
85 * efx_mcdi_ev_death() may end up running in parallel with
86 * efx_mcdi_request_start(). This race is handled by ensuring that
87 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
88 * en_eslp lock.
89 */
90 EFSYS_LOCK(enp->en_eslp, state);
91 EFSYS_ASSERT(emip->emi_pending_req == NULL);
92 emip->emi_pending_req = emrp;
93 emip->emi_ev_cpl = ev_cpl;
94 emip->emi_poll_cnt = 0;
95 seq = emip->emi_seq++ & 0xf;
96 EFSYS_UNLOCK(enp->en_eslp, state);
97
98 xflags = 0;
99 if (ev_cpl)
100 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
101
102 /* Construct the header in shared memory */
103 EFX_POPULATE_DWORD_6(dword,
104 MCDI_HEADER_CODE, emrp->emr_cmd,
105 MCDI_HEADER_RESYNC, 1,
106 MCDI_HEADER_DATALEN, emrp->emr_in_length,
107 MCDI_HEADER_SEQ, seq,
108 MCDI_HEADER_RESPONSE, 0,
109 MCDI_HEADER_XFLAGS, xflags);
110 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
111
112 for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) {
113 memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos),
114 MIN(sizeof (dword), emrp->emr_in_length - pos));
115 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
116 pdur + 1 + (pos >> 2), &dword, B_FALSE);
117 }
118
119 /* Ring the doorbell */
120 EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
121 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
122 }
123
124 static void
efx_mcdi_request_copyout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp)125 efx_mcdi_request_copyout(
126 __in efx_nic_t *enp,
127 __in efx_mcdi_req_t *emrp)
128 {
129 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
130 unsigned int pos;
131 unsigned int pdur;
132 efx_dword_t data;
133
134 pdur = (emip->emi_port == 1)
135 ? MC_SMEM_P0_PDU_OFST >> 2
136 : MC_SMEM_P1_PDU_OFST >> 2;
137
138 /* Copy payload out if caller supplied buffer */
139 if (emrp->emr_out_buf != NULL) {
140 size_t bytes = MIN(emrp->emr_out_length_used,
141 emrp->emr_out_length);
142 for (pos = 0; pos < bytes; pos += sizeof (efx_dword_t)) {
143 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
144 pdur + 1 + (pos >> 2), &data, B_FALSE);
145 memcpy(MCDI_OUT(*emrp, efx_dword_t, pos), &data,
146 MIN(sizeof (data), bytes - pos));
147 }
148 }
149 }
150
151 static int
efx_mcdi_request_errcode(__in unsigned int err)152 efx_mcdi_request_errcode(
153 __in unsigned int err)
154 {
155
156 switch (err) {
157 case MC_CMD_ERR_ENOENT:
158 return (ENOENT);
159 case MC_CMD_ERR_EINTR:
160 return (EINTR);
161 case MC_CMD_ERR_EACCES:
162 return (EACCES);
163 case MC_CMD_ERR_EBUSY:
164 return (EBUSY);
165 case MC_CMD_ERR_EINVAL:
166 return (EINVAL);
167 case MC_CMD_ERR_EDEADLK:
168 return (EDEADLK);
169 case MC_CMD_ERR_ENOSYS:
170 return (ENOTSUP);
171 case MC_CMD_ERR_ETIME:
172 return (ETIMEDOUT);
173 #ifdef WITH_MCDI_V2
174 case MC_CMD_ERR_EAGAIN:
175 return (EAGAIN);
176 case MC_CMD_ERR_ENOSPC:
177 return (ENOSPC);
178 #endif
179 default:
180 EFSYS_PROBE1(mc_pcol_error, int, err);
181 return (EIO);
182 }
183 }
184
185 static void
efx_mcdi_raise_exception(__in efx_nic_t * enp,__in_opt efx_mcdi_req_t * emrp,__in int rc)186 efx_mcdi_raise_exception(
187 __in efx_nic_t *enp,
188 __in_opt efx_mcdi_req_t *emrp,
189 __in int rc)
190 {
191 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
192 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
193 efx_mcdi_exception_t exception;
194
195 /* Reboot or Assertion failure only */
196 EFSYS_ASSERT(rc == EIO || rc == EINTR);
197
198 /*
199 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
200 * then the EIO is not worthy of an exception.
201 */
202 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
203 return;
204
205 exception = (rc == EIO)
206 ? EFX_MCDI_EXCEPTION_MC_REBOOT
207 : EFX_MCDI_EXCEPTION_MC_BADASSERT;
208
209 emtp->emt_exception(emtp->emt_context, exception);
210 }
211
212 static int
efx_mcdi_poll_reboot(__in efx_nic_t * enp)213 efx_mcdi_poll_reboot(
214 __in efx_nic_t *enp)
215 {
216 #ifndef EFX_GRACEFUL_MC_REBOOT
217 /*
218 * This function is not being used properly.
219 * Until its callers are fixed, it should always return 0.
220 */
221 _NOTE(ARGUNUSED(enp))
222 return (0);
223 #else
224 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
225 unsigned int rebootr;
226 efx_dword_t dword;
227 uint32_t value;
228
229 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
230 rebootr = ((emip->emi_port == 1)
231 ? MC_SMEM_P0_STATUS_OFST >> 2
232 : MC_SMEM_P1_STATUS_OFST >> 2);
233
234 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
235 value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
236
237 if (value == 0)
238 return (0);
239
240 EFX_ZERO_DWORD(dword);
241 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
242
243 if (value == MC_STATUS_DWORD_ASSERT)
244 return (EINTR);
245 else
246 return (EIO);
247 #endif
248 }
249
250 __checkReturn boolean_t
efx_mcdi_request_poll(__in efx_nic_t * enp)251 efx_mcdi_request_poll(
252 __in efx_nic_t *enp)
253 {
254 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
255 efx_mcdi_req_t *emrp;
256 efx_dword_t dword;
257 unsigned int pdur;
258 unsigned int seq;
259 unsigned int length;
260 int state;
261 int rc;
262
263 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
264 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
265 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
266 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
267
268 /* Serialise against post-watchdog efx_mcdi_ev* */
269 EFSYS_LOCK(enp->en_eslp, state);
270
271 EFSYS_ASSERT(emip->emi_pending_req != NULL);
272 EFSYS_ASSERT(!emip->emi_ev_cpl);
273 emrp = emip->emi_pending_req;
274
275 /* Check for reboot atomically w.r.t efx_mcdi_request_start */
276 if (emip->emi_poll_cnt++ == 0) {
277 if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
278 emip->emi_pending_req = NULL;
279 EFSYS_UNLOCK(enp->en_eslp, state);
280
281 goto fail1;
282 }
283 }
284
285 EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
286 pdur = (emip->emi_port == 1)
287 ? MC_SMEM_P0_PDU_OFST >> 2
288 : MC_SMEM_P1_PDU_OFST >> 2;
289
290 /* Read the command header */
291 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_FALSE);
292 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_RESPONSE) == 0) {
293 EFSYS_UNLOCK(enp->en_eslp, state);
294 return (B_FALSE);
295 }
296
297 /* Request complete */
298 emip->emi_pending_req = NULL;
299 seq = (emip->emi_seq - 1) & 0xf;
300
301 /* Check for synchronous reboot */
302 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR) != 0 &&
303 EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN) == 0) {
304 /* Consume status word */
305 EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
306 efx_mcdi_poll_reboot(enp);
307 EFSYS_UNLOCK(enp->en_eslp, state);
308 rc = EIO;
309 goto fail2;
310 }
311
312 EFSYS_UNLOCK(enp->en_eslp, state);
313
314 /* Check that the returned data is consistent */
315 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_CODE) != emrp->emr_cmd ||
316 EFX_DWORD_FIELD(dword, MCDI_HEADER_SEQ) != seq) {
317 /* Response is for a different request */
318 rc = EIO;
319 goto fail3;
320 }
321
322 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
323 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
324 efx_dword_t errdword;
325 int errcode;
326
327 EFSYS_ASSERT3U(length, ==, 4);
328 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
329 pdur + 1 + (MC_CMD_ERR_CODE_OFST >> 2),
330 &errdword, B_FALSE);
331 errcode = EFX_DWORD_FIELD(errdword, EFX_DWORD_0);
332 rc = efx_mcdi_request_errcode(errcode);
333 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd, int, errcode);
334 goto fail4;
335
336 } else {
337 emrp->emr_out_length_used = length;
338 emrp->emr_rc = 0;
339 efx_mcdi_request_copyout(enp, emrp);
340 }
341
342 goto out;
343
344 fail4:
345 EFSYS_PROBE(fail4);
346 fail3:
347 EFSYS_PROBE(fail3);
348 fail2:
349 EFSYS_PROBE(fail2);
350 fail1:
351 EFSYS_PROBE1(fail1, int, rc);
352
353 /* Fill out error state */
354 emrp->emr_rc = rc;
355 emrp->emr_out_length_used = 0;
356
357 /* Reboot/Assertion */
358 if (rc == EIO || rc == EINTR)
359 efx_mcdi_raise_exception(enp, emrp, rc);
360
361 out:
362 return (B_TRUE);
363 }
364
365 void
efx_mcdi_execute(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp)366 efx_mcdi_execute(
367 __in efx_nic_t *enp,
368 __in efx_mcdi_req_t *emrp)
369 {
370 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
371 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
372
373 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
374 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
375 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
376
377 emtp->emt_execute(emtp->emt_context, emrp);
378 }
379
380 void
efx_mcdi_ev_cpl(__in efx_nic_t * enp,__in unsigned int seq,__in unsigned int outlen,__in int errcode)381 efx_mcdi_ev_cpl(
382 __in efx_nic_t *enp,
383 __in unsigned int seq,
384 __in unsigned int outlen,
385 __in int errcode)
386 {
387 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
388 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
389 efx_mcdi_req_t *emrp;
390 int state;
391
392 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
393 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
394 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
395
396 /*
397 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
398 * when we're completing an aborted request.
399 */
400 EFSYS_LOCK(enp->en_eslp, state);
401 if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
402 (seq != ((emip->emi_seq - 1) & 0xf))) {
403 EFSYS_ASSERT(emip->emi_aborted > 0);
404 if (emip->emi_aborted > 0)
405 --emip->emi_aborted;
406 EFSYS_UNLOCK(enp->en_eslp, state);
407 return;
408 }
409
410 emrp = emip->emi_pending_req;
411 emip->emi_pending_req = NULL;
412 EFSYS_UNLOCK(enp->en_eslp, state);
413
414 /*
415 * Fill out the remaining hdr fields, and copyout the payload
416 * if the user supplied an output buffer.
417 */
418 if (errcode != 0) {
419 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
420 int, errcode);
421 emrp->emr_out_length_used = 0;
422 emrp->emr_rc = efx_mcdi_request_errcode(errcode);
423 } else {
424 emrp->emr_out_length_used = outlen;
425 emrp->emr_rc = 0;
426 efx_mcdi_request_copyout(enp, emrp);
427 }
428
429 emtp->emt_ev_cpl(emtp->emt_context);
430 }
431
432 void
efx_mcdi_ev_death(__in efx_nic_t * enp,__in int rc)433 efx_mcdi_ev_death(
434 __in efx_nic_t *enp,
435 __in int rc)
436 {
437 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
438 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
439 efx_mcdi_req_t *emrp = NULL;
440 boolean_t ev_cpl;
441 int state;
442
443 /*
444 * The MCDI request (if there is one) has been terminated, either
445 * by a BADASSERT or REBOOT event.
446 *
447 * If there is an outstanding event-completed MCDI operation, then we
448 * will never receive the completion event (because both MCDI
449 * completions and BADASSERT events are sent to the same evq). So
450 * complete this MCDI op.
451 *
452 * This function might run in parallel with efx_mcdi_request_poll()
453 * for poll completed mcdi requests, and also with
454 * efx_mcdi_request_start() for post-watchdog completions.
455 */
456 EFSYS_LOCK(enp->en_eslp, state);
457 emrp = emip->emi_pending_req;
458 ev_cpl = emip->emi_ev_cpl;
459 if (emrp != NULL && emip->emi_ev_cpl) {
460 emip->emi_pending_req = NULL;
461
462 emrp->emr_out_length_used = 0;
463 emrp->emr_rc = rc;
464 ++emip->emi_aborted;
465 }
466
467 /*
468 * Since we're running in parallel with a request, consume the
469 * status word before dropping the lock.
470 */
471 if (rc == EIO || rc == EINTR) {
472 EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
473 (void) efx_mcdi_poll_reboot(enp);
474 }
475
476 EFSYS_UNLOCK(enp->en_eslp, state);
477
478 efx_mcdi_raise_exception(enp, emrp, rc);
479
480 if (emrp != NULL && ev_cpl)
481 emtp->emt_ev_cpl(emtp->emt_context);
482 }
483
484 __checkReturn int
485 efx_mcdi_version(
486 __in efx_nic_t *enp,
487 __out_ecount_opt(4) uint16_t versionp[4],
488 __out_opt uint32_t *buildp,
489 __out_opt efx_mcdi_boot_t *statusp)
490 {
491 uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
492 MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
493 efx_mcdi_req_t req;
494 efx_word_t *ver_words;
495 uint16_t version[4];
496 uint32_t build;
497 efx_mcdi_boot_t status;
498 int rc;
499
500 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
501 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
502
503 EFX_STATIC_ASSERT(MC_CMD_GET_VERSION_IN_LEN == 0);
504 req.emr_cmd = MC_CMD_GET_VERSION;
505 req.emr_in_buf = NULL;
506 req.emr_in_length = 0;
507 req.emr_out_buf = outbuf;
508 req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
509
510 efx_mcdi_execute(enp, &req);
511
512 if (req.emr_rc != 0) {
513 rc = req.emr_rc;
514 goto fail1;
515 }
516
517 /* bootrom support */
518 if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
519 version[0] = version[1] = version[2] = version[3] = 0;
520 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
521
522 goto version;
523 }
524
525 if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
526 rc = EMSGSIZE;
527 goto fail2;
528 }
529
530 ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
531 version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
532 version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
533 version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
534 version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
535 build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
536
537 version:
538 /* The bootrom doesn't understand BOOT_STATUS */
539 if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_SIENA_BOOTROM) {
540 status = EFX_MCDI_BOOT_ROM;
541 goto out;
542 }
543
544 req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
545 EFX_STATIC_ASSERT(MC_CMD_GET_BOOT_STATUS_IN_LEN == 0);
546 req.emr_in_buf = NULL;
547 req.emr_in_length = 0;
548 req.emr_out_buf = outbuf;
549 req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
550
551 efx_mcdi_execute(enp, &req);
552
553 if (req.emr_rc != 0) {
554 rc = req.emr_rc;
555 goto fail3;
556 }
557
558 if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
559 rc = EMSGSIZE;
560 goto fail4;
561 }
562
563 if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
564 GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
565 status = EFX_MCDI_BOOT_PRIMARY;
566 else
567 status = EFX_MCDI_BOOT_SECONDARY;
568
569 out:
570 if (versionp != NULL)
571 memcpy(versionp, version, sizeof (version));
572 if (buildp != NULL)
573 *buildp = build;
574 if (statusp != NULL)
575 *statusp = status;
576
577 return (0);
578
579 fail4:
580 EFSYS_PROBE(fail4);
581 fail3:
582 EFSYS_PROBE(fail3);
583 fail2:
584 EFSYS_PROBE(fail2);
585 fail1:
586 EFSYS_PROBE1(fail1, int, rc);
587
588 return (rc);
589 }
590
591 __checkReturn int
efx_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * mtp)592 efx_mcdi_init(
593 __in efx_nic_t *enp,
594 __in const efx_mcdi_transport_t *mtp)
595 {
596 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
597 efx_oword_t oword;
598 unsigned int portnum;
599 int rc;
600
601 EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
602 enp->en_mod_flags |= EFX_MOD_MCDI;
603
604 if (enp->en_family == EFX_FAMILY_FALCON)
605 return (0);
606
607 emip->emi_mtp = mtp;
608
609 /* Determine the port number to use for MCDI */
610 EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
611 portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
612
613 if (portnum == 0) {
614 /* Presumably booted from ROM; only MCDI port 1 will work */
615 emip->emi_port = 1;
616 } else if (portnum <= 2) {
617 emip->emi_port = portnum;
618 } else {
619 rc = EINVAL;
620 goto fail1;
621 }
622
623 /*
624 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
625 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
626 * assertion handler.
627 */
628 (void) efx_mcdi_poll_reboot(enp);
629
630 return (0);
631
632 fail1:
633 EFSYS_PROBE1(fail1, int, rc);
634
635 enp->en_mod_flags &= ~EFX_MOD_MCDI;
636
637 return (rc);
638 }
639
640
641 __checkReturn int
efx_mcdi_reboot(__in efx_nic_t * enp)642 efx_mcdi_reboot(
643 __in efx_nic_t *enp)
644 {
645 uint8_t payload[MC_CMD_REBOOT_IN_LEN];
646 efx_mcdi_req_t req;
647 int rc;
648
649 /*
650 * We could require the caller to have caused en_mod_flags=0 to
651 * call this function. This doesn't help the other port though,
652 * who's about to get the MC ripped out from underneath them.
653 * Since they have to cope with the subsequent fallout of MCDI
654 * failures, we should as well.
655 */
656 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
657
658 req.emr_cmd = MC_CMD_REBOOT;
659 req.emr_in_buf = payload;
660 req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
661 req.emr_out_buf = NULL;
662 req.emr_out_length = 0;
663
664 MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
665
666 efx_mcdi_execute(enp, &req);
667
668 /* Invert EIO */
669 if (req.emr_rc != EIO) {
670 rc = EIO;
671 goto fail1;
672 }
673
674 return (0);
675
676 fail1:
677 EFSYS_PROBE1(fail1, int, rc);
678
679 return (rc);
680 }
681
682 __checkReturn boolean_t
efx_mcdi_request_abort(__in efx_nic_t * enp)683 efx_mcdi_request_abort(
684 __in efx_nic_t *enp)
685 {
686 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
687 efx_mcdi_req_t *emrp;
688 boolean_t aborted;
689 int state;
690
691 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
692 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
693
694 /*
695 * efx_mcdi_ev_* may have already completed this event, and be
696 * spinning/blocked on the upper layer lock. So it *is* legitimate
697 * to for emi_pending_req to be NULL. If there is a pending event
698 * completed request, then provide a "credit" to allow
699 * efx_mcdi_ev_cpl() to accept a single spurious completion.
700 */
701 EFSYS_LOCK(enp->en_eslp, state);
702 emrp = emip->emi_pending_req;
703 aborted = (emrp != NULL);
704 if (aborted) {
705 emip->emi_pending_req = NULL;
706
707 /* Error the request */
708 emrp->emr_out_length_used = 0;
709 emrp->emr_rc = ETIMEDOUT;
710
711 /* Provide a credit for seqno/emr_pending_req mismatches */
712 if (emip->emi_ev_cpl)
713 ++emip->emi_aborted;
714
715 /*
716 * The upper layer has called us, so we don't
717 * need to complete the request.
718 */
719 }
720 EFSYS_UNLOCK(enp->en_eslp, state);
721
722 return (aborted);
723 }
724
725 void
efx_mcdi_fini(__in efx_nic_t * enp)726 efx_mcdi_fini(
727 __in efx_nic_t *enp)
728 {
729 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
730
731 EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
732 enp->en_mod_flags &= ~EFX_MOD_MCDI;
733
734 if (~(enp->en_features) & EFX_FEATURE_MCDI)
735 return;
736
737 emip->emi_mtp = NULL;
738 emip->emi_port = 0;
739 emip->emi_aborted = 0;
740 }
741
742 #endif /* EFSYS_OPT_MCDI */
743