Ejemplo n.º 1
0
void efx_mcdi_init(struct efx_nic *efx)
{
	struct efx_mcdi_iface *mcdi;

	if (efx_nic_rev(efx) < EFX_REV_SIENA_A0)
		return;

	mcdi = efx_mcdi(efx);
	init_waitqueue_head(&mcdi->wq);
	spin_lock_init(&mcdi->iface_lock);
	atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT);
	mcdi->mode = MCDI_MODE_POLL;

	(void) efx_mcdi_poll_reboot(efx);
}
Ejemplo n.º 2
0
static int efx_mcdi_poll(struct efx_nic *efx)
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned int time, finish;
	unsigned int respseq, respcmd, error;
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned int rc, spins;
	efx_dword_t reg;

	/* Check for a reboot atomically with respect to efx_mcdi_copyout() */
	rc = -efx_mcdi_poll_reboot(efx);
	if (rc)
		goto out;

	/* Poll for completion. Poll quickly (once a us) for the 1st jiffy,
	 * because generally mcdi responses are fast. After that, back off
	 * and poll once a jiffy (approximately)
	 */
	spins = TICK_USEC;
	finish = get_seconds() + MCDI_RPC_TIMEOUT;

	while (1) {
		if (spins != 0) {
			--spins;
			udelay(1);
		} else {
			schedule_timeout_uninterruptible(1);
		}

		time = get_seconds();

		rmb();
		efx_readd(efx, &reg, pdu);

		/* All 1's indicates that shared memory is in reset (and is
		 * not a valid header). Wait for it to come out reset before
		 * completing the command */
		if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff &&
		    EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE))
			break;

		if (time >= finish)
			return -ETIMEDOUT;
	}

	mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN);
	respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ);
	respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE);
	error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR);

	if (error && mcdi->resplen == 0) {
		netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
		rc = EIO;
	} else if ((respseq ^ mcdi->seqno) & SEQ_MASK) {
		netif_err(efx, hw, efx->net_dev,
			  "MC response mismatch tx seq 0x%x rx seq 0x%x\n",
			  respseq, mcdi->seqno);
		rc = EIO;
	} else if (error) {
		efx_readd(efx, &reg, pdu + 4);
		switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) {
#define TRANSLATE_ERROR(name)					\
		case MC_CMD_ERR_ ## name:			\
			rc = name;				\
			break
			TRANSLATE_ERROR(ENOENT);
			TRANSLATE_ERROR(EINTR);
			TRANSLATE_ERROR(EACCES);
			TRANSLATE_ERROR(EBUSY);
			TRANSLATE_ERROR(EINVAL);
			TRANSLATE_ERROR(EDEADLK);
			TRANSLATE_ERROR(ENOSYS);
			TRANSLATE_ERROR(ETIME);
#undef TRANSLATE_ERROR
		default:
			rc = EIO;
			break;
		}
	} else
		rc = 0;

out:
	mcdi->resprc = rc;
	if (rc)
		mcdi->resplen = 0;

	/* Return rc=0 like wait_event_timeout() */
	return 0;
}
Ejemplo n.º 3
0
			void
efx_mcdi_read_response_header(
	__in		efx_nic_t *enp,
	__inout		efx_mcdi_req_t *emrp)
{
#if EFSYS_OPT_MCDI_LOGGING
	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
#endif /* EFSYS_OPT_MCDI_LOGGING */
	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
	efx_dword_t hdr[2];
	unsigned int hdr_len;
	unsigned int data_len;
	unsigned int seq;
	unsigned int cmd;
	unsigned int error;
	efx_rc_t rc;

	EFSYS_ASSERT(emrp != NULL);

	efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
	hdr_len = sizeof (hdr[0]);

	cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
	seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
	error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);

	if (cmd != MC_CMD_V2_EXTN) {
		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
	} else {
		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
		hdr_len += sizeof (hdr[1]);

		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
		data_len =
		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
	}

	if (error && (data_len == 0)) {
		/* The MC has rebooted since the request was sent. */
		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
		efx_mcdi_poll_reboot(enp);
		rc = EIO;
		goto fail1;
	}
	if ((cmd != emrp->emr_cmd) ||
	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
		/* Response is for a different request */
		rc = EIO;
		goto fail2;
	}
	if (error) {
		efx_dword_t err[2];
		unsigned int err_len = MIN(data_len, sizeof (err));
		int err_code = MC_CMD_ERR_EPROTO;
		int err_arg = 0;

		/* Read error code (and arg num for MCDI v2 commands) */
		efx_mcdi_read_response(enp, &err, hdr_len, err_len);

		if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
#ifdef WITH_MCDI_V2
		if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
#endif
		emrp->emr_err_code = err_code;
		emrp->emr_err_arg = err_arg;

#if EFSYS_OPT_MCDI_PROXY_AUTH
		if ((err_code == MC_CMD_ERR_PROXY_PENDING) &&
		    (err_len == sizeof (err))) {
			/*
			 * The MCDI request would normally fail with EPERM, but
			 * firmware has forwarded it to an authorization agent
			 * attached to a privileged PF.
			 *
			 * Save the authorization request handle. The client
			 * must wait for a PROXY_RESPONSE event, or timeout.
			 */
			emrp->emr_proxy_handle = err_arg;
		}
#endif /* EFSYS_OPT_MCDI_PROXY_AUTH */

#if EFSYS_OPT_MCDI_LOGGING
		if (emtp->emt_logger != NULL) {
			emtp->emt_logger(emtp->emt_context,
			    EFX_LOG_MCDI_RESPONSE,
			    &hdr, hdr_len,
			    &err, err_len);
		}
#endif /* EFSYS_OPT_MCDI_LOGGING */

		if (!emrp->emr_quiet) {
			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
			    int, err_code, int, err_arg);
		}

		rc = efx_mcdi_request_errcode(err_code);
		goto fail3;
	}