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"
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.
43 #define MCDI_STATUS_SLEEP_US 10000
46 efx_mcdi_request_start(
48 __in efx_mcdi_req_t *emrp,
49 __in boolean_t ev_cpl)
51 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
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);
64 switch (emip->emi_port) {
66 pdur = MC_SMEM_P0_PDU_OFST >> 2;
67 dbr = MC_SMEM_P0_DOORBELL_OFST >> 2;
70 pdur = MC_SMEM_P1_PDU_OFST >> 2;
71 dbr = MC_SMEM_P1_DOORBELL_OFST >> 2;
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
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);
100 xflags |= MCDI_HEADER_XFLAGS_EVREQ;
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);
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);
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);
125 efx_mcdi_request_copyout(
127 __in efx_mcdi_req_t *emrp)
129 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
134 pdur = (emip->emi_port == 1)
135 ? MC_SMEM_P0_PDU_OFST >> 2
136 : MC_SMEM_P1_PDU_OFST >> 2;
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));
152 efx_mcdi_request_errcode(
153 __in unsigned int err)
157 case MC_CMD_ERR_ENOENT:
159 case MC_CMD_ERR_EINTR:
161 case MC_CMD_ERR_EACCES:
163 case MC_CMD_ERR_EBUSY:
165 case MC_CMD_ERR_EINVAL:
167 case MC_CMD_ERR_EDEADLK:
169 case MC_CMD_ERR_ENOSYS:
171 case MC_CMD_ERR_ETIME:
174 case MC_CMD_ERR_EAGAIN:
176 case MC_CMD_ERR_ENOSPC:
180 EFSYS_PROBE1(mc_pcol_error, int, err);
186 efx_mcdi_raise_exception(
188 __in_opt efx_mcdi_req_t *emrp,
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;
195 /* Reboot or Assertion failure only */
196 EFSYS_ASSERT(rc == EIO || rc == EINTR);
199 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
200 * then the EIO is not worthy of an exception.
202 if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
205 exception = (rc == EIO)
206 ? EFX_MCDI_EXCEPTION_MC_REBOOT
207 : EFX_MCDI_EXCEPTION_MC_BADASSERT;
209 emtp->emt_exception(emtp->emt_context, exception);
213 efx_mcdi_poll_reboot(
216 #ifndef EFX_GRACEFUL_MC_REBOOT
218 * This function is not being used properly.
219 * Until its callers are fixed, it should always return 0.
221 _NOTE(ARGUNUSED(enp))
224 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
225 unsigned int rebootr;
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);
234 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
235 value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
240 EFX_ZERO_DWORD(dword);
241 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
243 if (value == MC_STATUS_DWORD_ASSERT)
250 __checkReturn boolean_t
251 efx_mcdi_request_poll(
254 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
255 efx_mcdi_req_t *emrp;
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);
268 /* Serialise against post-watchdog efx_mcdi_ev* */
269 EFSYS_LOCK(enp->en_eslp, state);
271 EFSYS_ASSERT(emip->emi_pending_req != NULL);
272 EFSYS_ASSERT(!emip->emi_ev_cpl);
273 emrp = emip->emi_pending_req;
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);
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;
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);
297 /* Request complete */
298 emip->emi_pending_req = NULL;
299 seq = (emip->emi_seq - 1) & 0xf;
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);
312 EFSYS_UNLOCK(enp->en_eslp, state);
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 */
322 length = EFX_DWORD_FIELD(dword, MCDI_HEADER_DATALEN);
323 if (EFX_DWORD_FIELD(dword, MCDI_HEADER_ERROR)) {
324 efx_dword_t errdword;
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),
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);
337 emrp->emr_out_length_used = length;
339 efx_mcdi_request_copyout(enp, emrp);
351 EFSYS_PROBE1(fail1, int, rc);
353 /* Fill out error state */
355 emrp->emr_out_length_used = 0;
357 /* Reboot/Assertion */
358 if (rc == EIO || rc == EINTR)
359 efx_mcdi_raise_exception(enp, emrp, rc);
368 __in efx_mcdi_req_t *emrp)
370 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
371 const efx_mcdi_transport_t *emtp = emip->emi_mtp;
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);
377 emtp->emt_execute(emtp->emt_context, emrp);
383 __in unsigned int seq,
384 __in unsigned int outlen,
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;
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);
397 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
398 * when we're completing an aborted request.
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)
406 EFSYS_UNLOCK(enp->en_eslp, state);
410 emrp = emip->emi_pending_req;
411 emip->emi_pending_req = NULL;
412 EFSYS_UNLOCK(enp->en_eslp, state);
415 * Fill out the remaining hdr fields, and copyout the payload
416 * if the user supplied an output buffer.
419 EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
421 emrp->emr_out_length_used = 0;
422 emrp->emr_rc = efx_mcdi_request_errcode(errcode);
424 emrp->emr_out_length_used = outlen;
426 efx_mcdi_request_copyout(enp, emrp);
429 emtp->emt_ev_cpl(emtp->emt_context);
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;
444 * The MCDI request (if there is one) has been terminated, either
445 * by a BADASSERT or REBOOT event.
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.
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.
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;
462 emrp->emr_out_length_used = 0;
468 * Since we're running in parallel with a request, consume the
469 * status word before dropping the lock.
471 if (rc == EIO || rc == EINTR) {
472 EFSYS_SPIN(MCDI_STATUS_SLEEP_US);
473 (void) efx_mcdi_poll_reboot(enp);
476 EFSYS_UNLOCK(enp->en_eslp, state);
478 efx_mcdi_raise_exception(enp, emrp, rc);
480 if (emrp != NULL && ev_cpl)
481 emtp->emt_ev_cpl(emtp->emt_context);
487 __out_ecount_opt(4) uint16_t versionp[4],
488 __out_opt uint32_t *buildp,
489 __out_opt efx_mcdi_boot_t *statusp)
491 uint8_t outbuf[MAX(MC_CMD_GET_VERSION_OUT_LEN,
492 MC_CMD_GET_BOOT_STATUS_OUT_LEN)];
494 efx_word_t *ver_words;
497 efx_mcdi_boot_t status;
500 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
501 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
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;
510 efx_mcdi_execute(enp, &req);
512 if (req.emr_rc != 0) {
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);
525 if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
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);
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;
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;
551 efx_mcdi_execute(enp, &req);
553 if (req.emr_rc != 0) {
558 if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
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;
567 status = EFX_MCDI_BOOT_SECONDARY;
570 if (versionp != NULL)
571 memcpy(versionp, version, sizeof (version));
586 EFSYS_PROBE1(fail1, int, rc);
594 __in const efx_mcdi_transport_t *mtp)
596 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
598 unsigned int portnum;
601 EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
602 enp->en_mod_flags |= EFX_MOD_MCDI;
604 if (enp->en_family == EFX_FAMILY_FALCON)
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);
614 /* Presumably booted from ROM; only MCDI port 1 will work */
616 } else if (portnum <= 2) {
617 emip->emi_port = portnum;
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
628 (void) efx_mcdi_poll_reboot(enp);
633 EFSYS_PROBE1(fail1, int, rc);
635 enp->en_mod_flags &= ~EFX_MOD_MCDI;
645 uint8_t payload[MC_CMD_REBOOT_IN_LEN];
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.
656 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
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;
664 MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS, 0);
666 efx_mcdi_execute(enp, &req);
669 if (req.emr_rc != EIO) {
677 EFSYS_PROBE1(fail1, int, rc);
682 __checkReturn boolean_t
683 efx_mcdi_request_abort(
686 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
687 efx_mcdi_req_t *emrp;
691 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
692 EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
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.
701 EFSYS_LOCK(enp->en_eslp, state);
702 emrp = emip->emi_pending_req;
703 aborted = (emrp != NULL);
705 emip->emi_pending_req = NULL;
707 /* Error the request */
708 emrp->emr_out_length_used = 0;
709 emrp->emr_rc = ETIMEDOUT;
711 /* Provide a credit for seqno/emr_pending_req mismatches */
712 if (emip->emi_ev_cpl)
716 * The upper layer has called us, so we don't
717 * need to complete the request.
720 EFSYS_UNLOCK(enp->en_eslp, state);
729 efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
731 EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
732 enp->en_mod_flags &= ~EFX_MOD_MCDI;
734 if (~(enp->en_features) & EFX_FEATURE_MCDI)
737 emip->emi_mtp = NULL;
739 emip->emi_aborted = 0;
742 #endif /* EFSYS_OPT_MCDI */