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