예제 #1
0
파일: siena.c 프로젝트: AlexShiLucky/linux
static void siena_mcdi_read_response(struct efx_nic *efx, efx_dword_t *outbuf,
				     size_t offset, size_t outlen)
{
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned int outlen_dw = DIV_ROUND_UP(outlen, 4);
	int i;

	for (i = 0; i < outlen_dw; i++)
		efx_readd(efx, &outbuf[i], pdu + offset + 4 * i);
}
예제 #2
0
static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen)
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	int i;

	BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
	BUG_ON(outlen & 3 || outlen >= 0x100);

	for (i = 0; i < outlen; i += 4)
		*((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i);
}
예제 #3
0
파일: siena.c 프로젝트: AlexShiLucky/linux
static bool siena_mcdi_poll_response(struct efx_nic *efx)
{
	unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	efx_dword_t hdr;

	efx_readd(efx, &hdr, pdu);

	/* All 1's indicates that shared memory is in reset (and is
	 * not a valid hdr). Wait for it to come out reset before
	 * completing the command
	 */
	return EFX_DWORD_FIELD(hdr, EFX_DWORD_0) != 0xffffffff &&
		EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE);
}
예제 #4
0
파일: siena.c 프로젝트: AlexShiLucky/linux
static void siena_mcdi_request(struct efx_nic *efx,
			       const efx_dword_t *hdr, size_t hdr_len,
			       const efx_dword_t *sdu, size_t sdu_len)
{
	unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
	unsigned int i;
	unsigned int inlen_dw = DIV_ROUND_UP(sdu_len, 4);

	EFX_WARN_ON_PARANOID(hdr_len != 4);

	efx_writed(efx, hdr, pdu);

	for (i = 0; i < inlen_dw; i++)
		efx_writed(efx, &sdu[i], pdu + hdr_len + 4 * i);

	/* Ensure the request is written out before the doorbell */
	wmb();

	/* ring the doorbell with a distinctive value */
	_efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
}
예제 #5
0
static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd,
			    const u8 *inbuf, size_t inlen)
{
	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
	unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx);
	unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx);
	unsigned int i;
	efx_dword_t hdr;
	u32 xflags, seqno;

	BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT);
	BUG_ON(inlen & 3 || inlen >= 0x100);

	seqno = mcdi->seqno & SEQ_MASK;
	xflags = 0;
	if (mcdi->mode == MCDI_MODE_EVENTS)
		xflags |= MCDI_HEADER_XFLAGS_EVREQ;

	EFX_POPULATE_DWORD_6(hdr,
			     MCDI_HEADER_RESPONSE, 0,
			     MCDI_HEADER_RESYNC, 1,
			     MCDI_HEADER_CODE, cmd,
			     MCDI_HEADER_DATALEN, inlen,
			     MCDI_HEADER_SEQ, seqno,
			     MCDI_HEADER_XFLAGS, xflags);

	efx_writed(efx, &hdr, pdu);

	for (i = 0; i < inlen; i += 4) {
		_efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i);
		/* use wmb() within loop to inhibit write combining */
		wmb();
	}

	/* ring the doorbell with a distinctive value */
	_efx_writed(efx, (__force __le32) 0x45789abc, doorbell);
	wmb();
}
예제 #6
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;
}