]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sfxge/common/efx_mcdi.c
MFV ntp 4.2.8p1 (r258945, r275970, r276091, r276092, r276093, r278284)
[FreeBSD/FreeBSD.git] / sys / dev / sfxge / common / efx_mcdi.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */