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); }
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); }
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); }
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); }
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(); }
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, ®, 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, ®, 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; }