sfxge(4): support EVQ timer workaround via MCDI
Submitted by: Andy Moreton <amoreton at solarflare.com> Sponsored by: Solarflare Communications, Inc. MFC after: 1 week Differential Revision: https://reviews.freebsd.org/6675
This commit is contained in:
parent
524c69665f
commit
76c72fa482
@ -86,6 +86,52 @@ ef10_ev_mcdi(
|
||||
__in_opt void *arg);
|
||||
|
||||
|
||||
static __checkReturn efx_rc_t
|
||||
efx_mcdi_set_evq_tmr(
|
||||
__in efx_nic_t *enp,
|
||||
__in uint32_t instance,
|
||||
__in uint32_t mode,
|
||||
__in uint32_t timer_ns)
|
||||
{
|
||||
efx_mcdi_req_t req;
|
||||
uint8_t payload[MAX(MC_CMD_SET_EVQ_TMR_IN_LEN,
|
||||
MC_CMD_SET_EVQ_TMR_OUT_LEN)];
|
||||
efx_rc_t rc;
|
||||
|
||||
(void) memset(payload, 0, sizeof (payload));
|
||||
req.emr_cmd = MC_CMD_SET_EVQ_TMR;
|
||||
req.emr_in_buf = payload;
|
||||
req.emr_in_length = MC_CMD_SET_EVQ_TMR_IN_LEN;
|
||||
req.emr_out_buf = payload;
|
||||
req.emr_out_length = MC_CMD_SET_EVQ_TMR_OUT_LEN;
|
||||
|
||||
MCDI_IN_SET_DWORD(req, SET_EVQ_TMR_IN_INSTANCE, instance);
|
||||
MCDI_IN_SET_DWORD(req, SET_EVQ_TMR_IN_TMR_LOAD_REQ_NS, timer_ns);
|
||||
MCDI_IN_SET_DWORD(req, SET_EVQ_TMR_IN_TMR_RELOAD_REQ_NS, timer_ns);
|
||||
MCDI_IN_SET_DWORD(req, SET_EVQ_TMR_IN_TMR_MODE, mode);
|
||||
|
||||
efx_mcdi_execute(enp, &req);
|
||||
|
||||
if (req.emr_rc != 0) {
|
||||
rc = req.emr_rc;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (req.emr_out_length_used < MC_CMD_SET_EVQ_TMR_OUT_LEN) {
|
||||
rc = EMSGSIZE;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
fail2:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static __checkReturn efx_rc_t
|
||||
efx_mcdi_init_evq(
|
||||
__in efx_nic_t *enp,
|
||||
@ -437,9 +483,19 @@ ef10_ev_qmoderate(
|
||||
efx_nic_t *enp = eep->ee_enp;
|
||||
efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
|
||||
efx_dword_t dword;
|
||||
uint32_t timer_val, mode;
|
||||
uint32_t timer_ns, timer_val, mode;
|
||||
efx_rc_t rc;
|
||||
|
||||
/* Check that hardware and MCDI use the same timer MODE values */
|
||||
EFX_STATIC_ASSERT(FFE_CZ_TIMER_MODE_DIS ==
|
||||
MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_DIS);
|
||||
EFX_STATIC_ASSERT(FFE_CZ_TIMER_MODE_IMMED_START ==
|
||||
MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_IMMED_START);
|
||||
EFX_STATIC_ASSERT(FFE_CZ_TIMER_MODE_TRIG_START ==
|
||||
MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_TRIG_START);
|
||||
EFX_STATIC_ASSERT(FFE_CZ_TIMER_MODE_INT_HLDOFF ==
|
||||
MC_CMD_SET_EVQ_TMR_IN_TIMER_MODE_INT_HLDOFF);
|
||||
|
||||
if (us > encp->enc_evq_timer_max_us) {
|
||||
rc = EINVAL;
|
||||
goto fail1;
|
||||
@ -447,37 +503,46 @@ ef10_ev_qmoderate(
|
||||
|
||||
/* If the value is zero then disable the timer */
|
||||
if (us == 0) {
|
||||
timer_val = 0;
|
||||
timer_ns = 0;
|
||||
mode = FFE_CZ_TIMER_MODE_DIS;
|
||||
} else {
|
||||
timer_ns = us * 1000u;
|
||||
mode = FFE_CZ_TIMER_MODE_INT_HLDOFF;
|
||||
}
|
||||
|
||||
if (encp->enc_bug61265_workaround) {
|
||||
rc = efx_mcdi_set_evq_tmr(enp, eep->ee_index, mode, timer_ns);
|
||||
if (rc != 0)
|
||||
goto fail2;
|
||||
} else {
|
||||
/* Calculate the timer value in quanta */
|
||||
timer_val = us * 1000 / encp->enc_evq_timer_quantum_ns;
|
||||
timer_val = timer_ns / encp->enc_evq_timer_quantum_ns;
|
||||
|
||||
/* Moderation value is base 0 so we need to deduct 1 */
|
||||
if (timer_val > 0)
|
||||
timer_val--;
|
||||
|
||||
mode = FFE_CZ_TIMER_MODE_INT_HLDOFF;
|
||||
}
|
||||
|
||||
if (encp->enc_bug35388_workaround) {
|
||||
EFX_POPULATE_DWORD_3(dword,
|
||||
ERF_DD_EVQ_IND_TIMER_FLAGS,
|
||||
EFE_DD_EVQ_IND_TIMER_FLAGS,
|
||||
ERF_DD_EVQ_IND_TIMER_MODE, mode,
|
||||
ERF_DD_EVQ_IND_TIMER_VAL, timer_val);
|
||||
EFX_BAR_TBL_WRITED(enp, ER_DD_EVQ_INDIRECT,
|
||||
eep->ee_index, &dword, 0);
|
||||
} else {
|
||||
EFX_POPULATE_DWORD_2(dword,
|
||||
ERF_DZ_TC_TIMER_MODE, mode,
|
||||
ERF_DZ_TC_TIMER_VAL, timer_val);
|
||||
EFX_BAR_TBL_WRITED(enp, ER_DZ_EVQ_TMR_REG,
|
||||
eep->ee_index, &dword, 0);
|
||||
if (encp->enc_bug35388_workaround) {
|
||||
EFX_POPULATE_DWORD_3(dword,
|
||||
ERF_DD_EVQ_IND_TIMER_FLAGS,
|
||||
EFE_DD_EVQ_IND_TIMER_FLAGS,
|
||||
ERF_DD_EVQ_IND_TIMER_MODE, mode,
|
||||
ERF_DD_EVQ_IND_TIMER_VAL, timer_val);
|
||||
EFX_BAR_TBL_WRITED(enp, ER_DD_EVQ_INDIRECT,
|
||||
eep->ee_index, &dword, 0);
|
||||
} else {
|
||||
EFX_POPULATE_DWORD_2(dword,
|
||||
ERF_DZ_TC_TIMER_MODE, mode,
|
||||
ERF_DZ_TC_TIMER_VAL, timer_val);
|
||||
EFX_BAR_TBL_WRITED(enp, ER_DZ_EVQ_TMR_REG,
|
||||
eep->ee_index, &dword, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
fail2:
|
||||
EFSYS_PROBE(fail2);
|
||||
fail1:
|
||||
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
||||
|
||||
|
@ -1129,6 +1129,7 @@ typedef struct efx_nic_cfg_s {
|
||||
boolean_t enc_bug26807_workaround;
|
||||
boolean_t enc_bug35388_workaround;
|
||||
boolean_t enc_bug41750_workaround;
|
||||
boolean_t enc_bug61265_workaround;
|
||||
boolean_t enc_rx_batching_enabled;
|
||||
/* Maximum number of descriptors completed in an rx event. */
|
||||
uint32_t enc_rx_batch_max;
|
||||
|
@ -291,6 +291,8 @@ hunt_board_cfg(
|
||||
FRF_CZ_TC_TIMER_VAL_WIDTH) / 1000;
|
||||
}
|
||||
|
||||
encp->enc_bug61265_workaround = B_FALSE; /* Medford only */
|
||||
|
||||
/* Check capabilities of running datapath firmware */
|
||||
if ((rc = ef10_get_datapath_caps(enp)) != 0)
|
||||
goto fail12;
|
||||
|
@ -227,6 +227,23 @@ medford_board_cfg(
|
||||
epp->ep_default_adv_cap_mask = els.els_adv_cap_mask;
|
||||
epp->ep_adv_cap_mask = els.els_adv_cap_mask;
|
||||
|
||||
/*
|
||||
* Enable firmware workarounds for hardware errata.
|
||||
* Expected responses are:
|
||||
* - 0 (zero):
|
||||
* Success: workaround enabled or disabled as requested.
|
||||
* - MC_CMD_ERR_ENOSYS (reported as ENOTSUP):
|
||||
* Firmware does not support the MC_CMD_WORKAROUND request.
|
||||
* (assume that the workaround is not supported).
|
||||
* - MC_CMD_ERR_ENOENT (reported as ENOENT):
|
||||
* Firmware does not support the requested workaround.
|
||||
* - MC_CMD_ERR_EPERM (reported as EACCES):
|
||||
* Unprivileged function cannot enable/disable workarounds.
|
||||
*
|
||||
* See efx_mcdi_request_errcode() for MCDI error translations.
|
||||
*/
|
||||
|
||||
|
||||
if (EFX_PCI_FUNCTION_IS_VF(encp)) {
|
||||
/*
|
||||
* Interrupt testing does not work for VFs. See bug50084.
|
||||
@ -238,9 +255,23 @@ medford_board_cfg(
|
||||
/* Chained multicast is always enabled on Medford */
|
||||
encp->enc_bug26807_workaround = B_TRUE;
|
||||
|
||||
/*
|
||||
* If the bug61265 workaround is enabled, then interrupt holdoff timers
|
||||
* cannot be controlled by timer table writes, so MCDI must be used
|
||||
* (timer table writes can still be used for wakeup timers).
|
||||
*/
|
||||
rc = efx_mcdi_set_workaround(enp, MC_CMD_WORKAROUND_BUG61265, B_TRUE,
|
||||
NULL);
|
||||
if ((rc == 0) || (rc == EACCES))
|
||||
encp->enc_bug61265_workaround = B_TRUE;
|
||||
else if ((rc == ENOTSUP) || (rc == ENOENT))
|
||||
encp->enc_bug61265_workaround = B_FALSE;
|
||||
else
|
||||
goto fail8;
|
||||
|
||||
/* Get clock frequencies (in MHz). */
|
||||
if ((rc = efx_mcdi_get_clock(enp, &sysclk, &dpcpu_clk)) != 0)
|
||||
goto fail8;
|
||||
goto fail9;
|
||||
|
||||
/*
|
||||
* The Medford timer quantum is 1536 dpcpu_clk cycles, documented for
|
||||
@ -252,14 +283,14 @@ medford_board_cfg(
|
||||
|
||||
/* Check capabilities of running datapath firmware */
|
||||
if ((rc = ef10_get_datapath_caps(enp)) != 0)
|
||||
goto fail9;
|
||||
goto fail10;
|
||||
|
||||
/* Alignment for receive packet DMA buffers */
|
||||
encp->enc_rx_buf_align_start = 1;
|
||||
|
||||
/* Get the RX DMA end padding alignment configuration */
|
||||
if ((rc = efx_mcdi_get_rxdp_config(enp, &end_padding)) != 0)
|
||||
goto fail10;
|
||||
goto fail11;
|
||||
encp->enc_rx_buf_align_end = end_padding;
|
||||
|
||||
/* Alignment for WPTR updates */
|
||||
@ -288,13 +319,13 @@ medford_board_cfg(
|
||||
* can result in time-of-check/time-of-use bugs.
|
||||
*/
|
||||
if ((rc = ef10_get_privilege_mask(enp, &mask)) != 0)
|
||||
goto fail11;
|
||||
goto fail12;
|
||||
encp->enc_privilege_mask = mask;
|
||||
|
||||
/* Get interrupt vector limits */
|
||||
if ((rc = efx_mcdi_get_vector_cfg(enp, &base, &nvec, NULL)) != 0) {
|
||||
if (EFX_PCI_FUNCTION_IS_PF(encp))
|
||||
goto fail12;
|
||||
goto fail13;
|
||||
|
||||
/* Ignore error (cannot query vector limits from a VF). */
|
||||
base = 0;
|
||||
@ -317,12 +348,14 @@ medford_board_cfg(
|
||||
|
||||
rc = medford_nic_get_required_pcie_bandwidth(enp, &bandwidth);
|
||||
if (rc != 0)
|
||||
goto fail13;
|
||||
goto fail14;
|
||||
encp->enc_required_pcie_bandwidth_mbps = bandwidth;
|
||||
encp->enc_max_pcie_link_gen = EFX_PCIE_LINK_SPEED_GEN3;
|
||||
|
||||
return (0);
|
||||
|
||||
fail14:
|
||||
EFSYS_PROBE(fail14);
|
||||
fail13:
|
||||
EFSYS_PROBE(fail13);
|
||||
fail12:
|
||||
|
Loading…
Reference in New Issue
Block a user