]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/sfxge/common/efx_mcdi.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 /* 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
54 efx_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
132 static                  void
133 efx_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
157 static                  int
158 efx_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
191 static                  void
192 efx_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
218 static                  int
219 efx_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
248 efx_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
339 fail4:
340         EFSYS_PROBE(fail4);
341 fail3:
342         EFSYS_PROBE(fail3);
343 fail2:
344         EFSYS_PROBE(fail2);
345 fail1:
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
356 out:
357         return (B_TRUE);
358 }
359
360                         void
361 efx_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
376 efx_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
428 efx_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
479 efx_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
531 version:
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
563 out:
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
573 fail4:
574         EFSYS_PROBE(fail4);
575 fail3:
576         EFSYS_PROBE(fail3);
577 fail2:
578         EFSYS_PROBE(fail2);
579 fail1:
580         EFSYS_PROBE1(fail1, int, rc);
581
582         return (rc);
583 }
584
585         __checkReturn   int
586 efx_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
626 fail1:
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
636 efx_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
670 fail1:
671         EFSYS_PROBE1(fail1, int, rc);
672
673         return (rc);
674 }
675
676         __checkReturn   boolean_t
677 efx_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
720 efx_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 */