void efx_mcdi_request_start( __in efx_nic_t *enp, __in efx_mcdi_req_t *emrp, __in boolean_t ev_cpl) { efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip); efx_dword_t dword; unsigned int seq; unsigned int xflags; unsigned int pdur; unsigned int dbr; unsigned int pos; int state; EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI); EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI); switch (emip->emi_port) { case 1: pdur = MCDI_P1_PDU_OFST; dbr = MCDI_P1_DBL_OFST; break; case 2: pdur = MCDI_P2_PDU_OFST; dbr = MCDI_P2_DBL_OFST; break; default: EFSYS_ASSERT(0); pdur = dbr = 0; }; /* * efx_mcdi_request_start() is naturally serialised against both * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(), * by virtue of there only being one oustanding MCDI request. * Unfortunately, upper layers may also call efx_mcdi_request_abort() * at any time, to timeout a pending mcdi request, That request may * then subsequently complete, meaning efx_mcdi_ev_cpl() or * efx_mcdi_ev_death() may end up running in parallel with * efx_mcdi_request_start(). This race is handled by ensuring that * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the * en_eslp lock. */ EFSYS_LOCK(enp->en_eslp, state); EFSYS_ASSERT(emip->emi_pending_req == NULL); emip->emi_pending_req = emrp; emip->emi_ev_cpl = ev_cpl; emip->emi_poll_cnt = 0; seq = emip->emi_seq++ & 0xf; EFSYS_UNLOCK(enp->en_eslp, state); xflags = 0; if (ev_cpl) xflags |= MCDI_HEADER_XFLAGS_EVREQ; /* Construct the header in shared memory */ EFX_POPULATE_DWORD_6(dword, MCDI_HEADER_CODE, emrp->emr_cmd, MCDI_HEADER_RESYNC, 1, MCDI_HEADER_DATALEN, emrp->emr_in_length, MCDI_HEADER_SEQ, seq, MCDI_HEADER_RESPONSE, 0, MCDI_HEADER_XFLAGS, xflags); EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE); for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) { memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos), MIN(sizeof (dword), emrp->emr_in_length - pos)); EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur + 1 + (pos >> 2), &dword, B_FALSE); } /* Ring the doorbell */ EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11); EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE); }
void hunt_mcdi_request_copyin( __in efx_nic_t *enp, __in efx_mcdi_req_t *emrp, __in unsigned int seq, __in boolean_t ev_cpl, __in boolean_t new_epoch) { const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; efsys_mem_t *esmp = emtp->emt_dma_mem; efx_mcdi_header_type_t hdr_type; efx_dword_t dword; efx_dword_t hdr[2]; unsigned int xflags; unsigned int pos; size_t offset; EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_HUNTINGTON); xflags = 0; if (ev_cpl) xflags |= MCDI_HEADER_XFLAGS_EVREQ; offset = 0; hdr_type = EFX_MCDI_HEADER_TYPE(emrp->emr_cmd, MAX(emrp->emr_in_length, emrp->emr_out_length)); if (hdr_type == EFX_MCDI_HEADER_TYPE_V2) { /* Construct MCDI v2 header */ EFX_POPULATE_DWORD_8(hdr[0], MCDI_HEADER_CODE, MC_CMD_V2_EXTN, MCDI_HEADER_RESYNC, 1, MCDI_HEADER_DATALEN, 0, MCDI_HEADER_SEQ, seq, MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1, MCDI_HEADER_ERROR, 0, MCDI_HEADER_RESPONSE, 0, MCDI_HEADER_XFLAGS, xflags); EFSYS_MEM_WRITED(esmp, offset, &hdr[0]); offset += sizeof (efx_dword_t); EFX_POPULATE_DWORD_2(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd, MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length); EFSYS_MEM_WRITED(esmp, offset, &hdr[1]); offset += sizeof (efx_dword_t); } else { /* Construct MCDI v1 header */ EFX_POPULATE_DWORD_8(hdr[0], MCDI_HEADER_CODE, emrp->emr_cmd, MCDI_HEADER_RESYNC, 1, MCDI_HEADER_DATALEN, emrp->emr_in_length, MCDI_HEADER_SEQ, seq, MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1, MCDI_HEADER_ERROR, 0, MCDI_HEADER_RESPONSE, 0, MCDI_HEADER_XFLAGS, xflags); EFSYS_MEM_WRITED(esmp, 0, &hdr[0]); offset += sizeof (efx_dword_t); } #if EFSYS_OPT_MCDI_LOGGING if (emtp->emt_logger != NULL) { emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST, &hdr, offset, emrp->emr_in_buf, emrp->emr_in_length); } #endif /* EFSYS_OPT_MCDI_LOGGING */ /* Construct the payload */ for (pos = 0; pos < emrp->emr_in_length; pos += sizeof (efx_dword_t)) { memcpy(&dword, MCDI_IN(*emrp, efx_dword_t, pos), MIN(sizeof (dword), emrp->emr_in_length - pos)); EFSYS_MEM_WRITED(esmp, offset + pos, &dword); } /* Ring the doorbell to post the command DMA address to the MC */ EFSYS_ASSERT((EFSYS_MEM_ADDR(esmp) & 0xFF) == 0); /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */ EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, offset + emrp->emr_in_length); EFSYS_PIO_WRITE_BARRIER(); EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) >> 32); EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE); EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, EFSYS_MEM_ADDR(esmp) & 0xffffffff); EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); }