2 * Copyright 2008-2009 Solarflare Communications Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
31 #include "efx_types.h"
33 #include "efx_regs_mcdi.h"
38 /* Shared memory layout */
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
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.
51 #define MCDI_STATUS_SLEEP_US 10000
54 efx_mcdi_request_start(
56 __in efx_mcdi_req_t *emrp,
57 __in boolean_t ev_cpl)
59 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
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);
72 switch (emip->emi_port) {
74 pdur = MCDI_P1_PDU_OFST;
75 dbr = MCDI_P1_DBL_OFST;
78 pdur = MCDI_P2_PDU_OFST;
79 dbr = MCDI_P2_DBL_OFST;
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 oustanding 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
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);
108 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
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);
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);
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);
133 efx_mcdi_request_copyout(
135 __in efx_mcdi_req_t *emrp)
137 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
142 pdur = (emip->emi_port == 1) ? MCDI_P1_PDU_OFST : MCDI_P2_PDU_OFST;
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));
158 efx_mcdi_request_errcode(
159 __in unsigned int err)
163 case MC_CMD_ERR_ENOENT:
165 case MC_CMD_ERR_EINTR:
167 case MC_CMD_ERR_EACCES:
169 case MC_CMD_ERR_EBUSY:
171 case MC_CMD_ERR_EINVAL:
173 case MC_CMD_ERR_EDEADLK:
175 case MC_CMD_ERR_ENOSYS:
177 case MC_CMD_ERR_ETIME:
180 case MC_CMD_ERR_EAGAIN:
182 case MC_CMD_ERR_ENOSPC:
186 EFSYS_PROBE1(mc_pcol_error, int, err);
192 efx_mcdi_raise_exception(
194 __in_opt efx_mcdi_req_t *emrp,
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;
201 /* Reboot or Assertion failure only */
202 EFSYS_ASSERT(rc == EIO || rc == EINTR);
205 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
206 * then the EIO is not worthy of an exception.
208 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
211 exception = (rc == EIO)
212 ? EFX_MCDI_EXCEPTION_MC_REBOOT
213 : EFX_MCDI_EXCEPTION_MC_BADASSERT;
215 emtp->emt_exception(emtp->emt_context, exception);
219 efx_mcdi_poll_reboot(
222 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
223 unsigned int rebootr;
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);
232 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
233 value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
238 EFX_ZERO_DWORD(dword);
239 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
241 if (value == MC_STATUS_DWORD_ASSERT)
247 __checkReturn boolean_t
248 efx_mcdi_request_poll(
251 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
252 efx_mcdi_req_t *emrp;
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);
265 /* Serialise against post-watchdog efx_mcdi_ev* */
266 EFSYS_LOCK(enp->en_eslp, state);
268 EFSYS_ASSERT(emip->emi_pending_req != NULL);
269 EFSYS_ASSERT(!emip->emi_ev_cpl);
270 emrp = emip->emi_pending_req;
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);
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;
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);
292 /* Request complete */
293 emip->emi_pending_req = NULL;
294 seq = (emip->emi_seq - 1) & 0xf;
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);
307 EFSYS_UNLOCK(enp->en_eslp, state);
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 */
317 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
318 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
319 efx_dword_t errdword;
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),
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);
332 emrp->emr_out_length_used = length;
334 efx_mcdi_request_copyout(enp, emrp);
346 EFSYS_PROBE1(fail1, int, rc);
348 /* Fill out error state */
350 emrp->emr_out_length_used = 0;
352 /* Reboot/Assertion */
353 if (rc == EIO || rc == EINTR)
354 efx_mcdi_raise_exception(enp, emrp, rc);
363 __in efx_mcdi_req_t *emrp)
365 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
366 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
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);
372 emtp->emt_execute(emtp->emt_context, emrp);
378 __in unsigned int seq,
379 __in unsigned int outlen,
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;
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);
392 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
393 * when we're completing an aborted request.
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)
401 EFSYS_UNLOCK(enp->en_eslp, state);
405 emrp = emip->emi_pending_req;
406 emip->emi_pending_req = NULL;
407 EFSYS_UNLOCK(enp->en_eslp, state);
410 * Fill out the remaining hdr fields, and copyout the payload
411 * if the user supplied an output buffer.
414 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
416 emrp->emr_out_length_used = 0;
417 emrp->emr_rc = efx_mcdi_request_errcode(errcode);
419 emrp->emr_out_length_used = outlen;
421 efx_mcdi_request_copyout(enp, emrp);
424 emtp->emt_ev_cpl(emtp->emt_context);
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;
439 * The MCDI request (if there is one) has been terminated, either
440 * by a BADASSERT or REBOOT event.
442 * If there is an oustanding 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.
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.
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;
457 emrp->emr_out_length_used = 0;
462 /* Since we're running in parallel with a request, consume the
463 * status word before dropping the lock.
465 if (rc == EIO || rc == EINTR) {
466 EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
467 (void) efx_mcdi_poll_reboot(enp);
470 EFSYS_UNLOCK(enp->en_eslp, state);
472 efx_mcdi_raise_exception(enp, emrp, rc);
474 if (emrp != NULL && ev_cpl)
475 emtp->emt_ev_cpl(emtp->emt_context);
481 __out_ecount_opt(4) uint16_t versionp[4],
482 __out_opt uint32_t *buildp,
483 __out_opt efx_mcdi_boot_t *statusp)
485 uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
486 MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
488 efx_word_t *ver_words;
491 efx_mcdi_boot_t status;
494 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
495 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
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;
504 efx_mcdi_execute(enp, &req);
506 if (req.emr_rc != 0) {
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);
519 if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
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);
532 /* The bootrom doesn't understand BOOT_STATUS */
533 if (build == MC_CMD_GET_VERSION_OUT_FIRMWARE_BOOTROM) {
534 status = EFX_MCDI_BOOT_ROM;
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;
545 efx_mcdi_execute(enp, &req);
547 if (req.emr_rc != 0) {
552 if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
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;
561 status = EFX_MCDI_BOOT_SECONDARY;
564 if (versionp != NULL)
565 memcpy(versionp, version, sizeof (version));
580 EFSYS_PROBE1(fail1, int, rc);
588 __in const efx_mcdi_transport_t *mtp)
590 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
592 unsigned int portnum;
595 EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
596 enp->en_mod_flags |= EFX_MOD_MCDI;
598 if (enp->en_family == EFX_FAMILY_FALCON)
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);
608 /* Presumably booted from ROM; only MCDI port 1 will work */
610 } else if (portnum <= 2) {
611 emip->emi_port = portnum;
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
622 (void) efx_mcdi_poll_reboot(enp);
627 EFSYS_PROBE1(fail1, int, rc);
629 enp->en_mod_flags &= ~EFX_MOD_MCDI;
639 uint8_t payload[MC_CMD_REBOOT_IN_LEN];
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.
650 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
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;
658 MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
660 efx_mcdi_execute(enp, &req);
663 if (req.emr_rc != EIO) {
671 EFSYS_PROBE1(fail1, int, rc);
676 __checkReturn boolean_t
677 efx_mcdi_request_abort(
680 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
681 efx_mcdi_req_t *emrp;
685 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
686 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
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.
695 EFSYS_LOCK(enp->en_eslp, state);
696 emrp = emip->emi_pending_req;
697 aborted = (emrp != NULL);
699 emip->emi_pending_req = NULL;
701 /* Error the request */
702 emrp->emr_out_length_used = 0;
703 emrp->emr_rc = ETIMEDOUT;
705 /* Provide a credit for seqno/emr_pending_req mismatches */
706 if (emip->emi_ev_cpl)
710 * The upper layer has called us, so we don't
711 * need to complete the request.
714 EFSYS_UNLOCK(enp->en_eslp, state);
723 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
725 EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
726 enp->en_mod_flags &= ~EFX_MOD_MCDI;
728 if (~(enp->en_features) & EFX_FEATURE_MCDI)
731 emip->emi_mtp = NULL;
733 emip->emi_aborted = 0;
736 #endif /* EFSYS_OPT_MCDI */