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:
Andrew Rybchenko 2016-06-03 05:27:34 +00:00
parent 57dbe10e6d
commit e26f5dac7f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=301237
4 changed files with 127 additions and 26 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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: