static int siena_mcdi_poll_reboot(struct efx_nic *efx) { struct siena_nic_data *nic_data = efx->nic_data; unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx); efx_dword_t reg; u32 value; efx_readd(efx, ®, addr); value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); if (value == 0) return 0; EFX_ZERO_DWORD(reg); efx_writed(efx, ®, addr); /* MAC statistics have been cleared on the NIC; clear the local * copies that we update with efx_update_diff_stat(). */ nic_data->stats[SIENA_STAT_tx_good_bytes] = 0; nic_data->stats[SIENA_STAT_rx_good_bytes] = 0; if (value == MC_STATUS_DWORD_ASSERT) return -EINTR; else return -EIO; }
inline void falcon_irq_ack_a1(struct efx_nic *efx) { efx_dword_t reg; EFX_POPULATE_DWORD_1(reg, FRF_AA_INT_ACK_KER_FIELD, 0xb7eb7e); efx_writed(efx, ®, FR_AA_INT_ACK_KER); efx_readd(efx, ®, FR_AA_WORK_AROUND_BROKEN_PCI_READS); }
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 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); }
/* Test and clear MC-rebooted flag for this port/function */ int efx_mcdi_poll_reboot(struct efx_nic *efx) { unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); efx_dword_t reg; uint32_t value; if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) return false; efx_readd(efx, ®, addr); value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); if (value == 0) return 0; EFX_ZERO_DWORD(reg); efx_writed(efx, ®, addr); if (value == MC_STATUS_DWORD_ASSERT) return -EINTR; else return -EIO; }
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; }