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