Skip to content

Commit

Permalink
Merge branch 'sfc-SFN8000-support-improvements'
Browse files Browse the repository at this point in the history
Bert Kenward says:

====================
sfc: SFN8000 support improvements

This series improves support for the recently released SFN8000 series
of adapters. Specifically, it retrieves interrupt moderation timer
settings directly from the adapter and uses those settings. It also
uses a new event queue initialisation interface, allowing specification
of a performance objective rather than enabling individual flags.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 13, 2016
2 parents e3e9652 + d95e329 commit 85be21b
Show file tree
Hide file tree
Showing 11 changed files with 782 additions and 118 deletions.
221 changes: 175 additions & 46 deletions drivers/net/ethernet/sfc/ef10.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static int efx_ef10_get_vf_index(struct efx_nic *efx)

static int efx_ef10_init_datapath_caps(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CAPABILITIES_OUT_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_CAPABILITIES_V2_OUT_LEN);
struct efx_ef10_nic_data *nic_data = efx->nic_data;
size_t outlen;
int rc;
Expand All @@ -188,7 +188,7 @@ static int efx_ef10_init_datapath_caps(struct efx_nic *efx)
outbuf, sizeof(outbuf), &outlen);
if (rc)
return rc;
if (outlen < sizeof(outbuf)) {
if (outlen < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
netif_err(efx, drv, efx->net_dev,
"unable to read datapath firmware capabilities\n");
return -EIO;
Expand All @@ -197,6 +197,12 @@ static int efx_ef10_init_datapath_caps(struct efx_nic *efx)
nic_data->datapath_caps =
MCDI_DWORD(outbuf, GET_CAPABILITIES_OUT_FLAGS1);

if (outlen >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
nic_data->datapath_caps2 = MCDI_DWORD(outbuf,
GET_CAPABILITIES_V2_OUT_FLAGS2);
else
nic_data->datapath_caps2 = 0;

/* record the DPCPU firmware IDs to determine VEB vswitching support.
*/
nic_data->rx_dpcpu_fw_id =
Expand Down Expand Up @@ -227,6 +233,116 @@ static int efx_ef10_get_sysclk_freq(struct efx_nic *efx)
return rc > 0 ? rc : -ERANGE;
}

static int efx_ef10_get_timer_workarounds(struct efx_nic *efx)
{
struct efx_ef10_nic_data *nic_data = efx->nic_data;
unsigned int implemented;
unsigned int enabled;
int rc;

nic_data->workaround_35388 = false;
nic_data->workaround_61265 = false;

rc = efx_mcdi_get_workarounds(efx, &implemented, &enabled);

if (rc == -ENOSYS) {
/* Firmware without GET_WORKAROUNDS - not a problem. */
rc = 0;
} else if (rc == 0) {
/* Bug61265 workaround is always enabled if implemented. */
if (enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG61265)
nic_data->workaround_61265 = true;

if (enabled & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388) {
nic_data->workaround_35388 = true;
} else if (implemented & MC_CMD_GET_WORKAROUNDS_OUT_BUG35388) {
/* Workaround is implemented but not enabled.
* Try to enable it.
*/
rc = efx_mcdi_set_workaround(efx,
MC_CMD_WORKAROUND_BUG35388,
true, NULL);
if (rc == 0)
nic_data->workaround_35388 = true;
/* If we failed to set the workaround just carry on. */
rc = 0;
}
}

netif_dbg(efx, probe, efx->net_dev,
"workaround for bug 35388 is %sabled\n",
nic_data->workaround_35388 ? "en" : "dis");
netif_dbg(efx, probe, efx->net_dev,
"workaround for bug 61265 is %sabled\n",
nic_data->workaround_61265 ? "en" : "dis");

return rc;
}

static void efx_ef10_process_timer_config(struct efx_nic *efx,
const efx_dword_t *data)
{
unsigned int max_count;

if (EFX_EF10_WORKAROUND_61265(efx)) {
efx->timer_quantum_ns = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_MCDI_TMR_STEP_NS);
efx->timer_max_ns = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_MCDI_TMR_MAX_NS);
} else if (EFX_EF10_WORKAROUND_35388(efx)) {
efx->timer_quantum_ns = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_BUG35388_TMR_NS_PER_COUNT);
max_count = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_BUG35388_TMR_MAX_COUNT);
efx->timer_max_ns = max_count * efx->timer_quantum_ns;
} else {
efx->timer_quantum_ns = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_TMR_REG_NS_PER_COUNT);
max_count = MCDI_DWORD(data,
GET_EVQ_TMR_PROPERTIES_OUT_TMR_REG_MAX_COUNT);
efx->timer_max_ns = max_count * efx->timer_quantum_ns;
}

netif_dbg(efx, probe, efx->net_dev,
"got timer properties from MC: quantum %u ns; max %u ns\n",
efx->timer_quantum_ns, efx->timer_max_ns);
}

static int efx_ef10_get_timer_config(struct efx_nic *efx)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_EVQ_TMR_PROPERTIES_OUT_LEN);
int rc;

rc = efx_ef10_get_timer_workarounds(efx);
if (rc)
return rc;

rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_EVQ_TMR_PROPERTIES, NULL, 0,
outbuf, sizeof(outbuf), NULL);

if (rc == 0) {
efx_ef10_process_timer_config(efx, outbuf);
} else if (rc == -ENOSYS || rc == -EPERM) {
/* Not available - fall back to Huntington defaults. */
unsigned int quantum;

rc = efx_ef10_get_sysclk_freq(efx);
if (rc < 0)
return rc;

quantum = 1536000 / rc; /* 1536 cycles */
efx->timer_quantum_ns = quantum;
efx->timer_max_ns = efx->type->timer_period_max * quantum;
rc = 0;
} else {
efx_mcdi_display_error(efx, MC_CMD_GET_EVQ_TMR_PROPERTIES,
MC_CMD_GET_EVQ_TMR_PROPERTIES_OUT_LEN,
NULL, 0, rc);
}

return rc;
}

static int efx_ef10_get_mac_address_pf(struct efx_nic *efx, u8 *mac_address)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_MAC_ADDRESSES_OUT_LEN);
Expand Down Expand Up @@ -527,33 +643,11 @@ static int efx_ef10_probe(struct efx_nic *efx)
if (rc)
goto fail5;

rc = efx_ef10_get_sysclk_freq(efx);
rc = efx_ef10_get_timer_config(efx);
if (rc < 0)
goto fail5;
efx->timer_quantum_ns = 1536000 / rc; /* 1536 cycles */

/* Check whether firmware supports bug 35388 workaround.
* First try to enable it, then if we get EPERM, just
* ask if it's already enabled
*/
rc = efx_mcdi_set_workaround(efx, MC_CMD_WORKAROUND_BUG35388, true, NULL);
if (rc == 0) {
nic_data->workaround_35388 = true;
} else if (rc == -EPERM) {
unsigned int enabled;

rc = efx_mcdi_get_workarounds(efx, NULL, &enabled);
if (rc)
goto fail3;
nic_data->workaround_35388 = enabled &
MC_CMD_GET_WORKAROUNDS_OUT_BUG35388;
} else if (rc != -ENOSYS && rc != -ENOENT) {
goto fail5;
}
netif_dbg(efx, probe, efx->net_dev,
"workaround for bug 35388 is %sabled\n",
nic_data->workaround_35388 ? "en" : "dis");

rc = efx_mcdi_mon_probe(efx);
if (rc && rc != -EPERM)
goto fail5;
Expand Down Expand Up @@ -1743,27 +1837,43 @@ static size_t efx_ef10_update_stats_vf(struct efx_nic *efx, u64 *full_stats,
static void efx_ef10_push_irq_moderation(struct efx_channel *channel)
{
struct efx_nic *efx = channel->efx;
unsigned int mode, value;
unsigned int mode, usecs;
efx_dword_t timer_cmd;

if (channel->irq_moderation) {
if (channel->irq_moderation_us) {
mode = 3;
value = channel->irq_moderation - 1;
usecs = channel->irq_moderation_us;
} else {
mode = 0;
value = 0;
usecs = 0;
}

if (EFX_EF10_WORKAROUND_35388(efx)) {
if (EFX_EF10_WORKAROUND_61265(efx)) {
MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_EVQ_TMR_IN_LEN);
unsigned int ns = usecs * 1000;

MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_INSTANCE,
channel->channel);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_LOAD_REQ_NS, ns);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_RELOAD_REQ_NS, ns);
MCDI_SET_DWORD(inbuf, SET_EVQ_TMR_IN_TMR_MODE, mode);

efx_mcdi_rpc_async(efx, MC_CMD_SET_EVQ_TMR,
inbuf, sizeof(inbuf), 0, NULL, 0);
} else if (EFX_EF10_WORKAROUND_35388(efx)) {
unsigned int ticks = efx_usecs_to_ticks(efx, usecs);

EFX_POPULATE_DWORD_3(timer_cmd, 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, value);
ERF_DD_EVQ_IND_TIMER_VAL, ticks);
efx_writed_page(efx, &timer_cmd, ER_DD_EVQ_INDIRECT,
channel->channel);
} else {
unsigned int ticks = efx_usecs_to_ticks(efx, usecs);

EFX_POPULATE_DWORD_2(timer_cmd, ERF_DZ_TC_TIMER_MODE, mode,
ERF_DZ_TC_TIMER_VAL, value);
ERF_DZ_TC_TIMER_VAL, ticks);
efx_writed_page(efx, &timer_cmd, ER_DZ_EVQ_TMR,
channel->channel);
}
Expand Down Expand Up @@ -2535,23 +2645,19 @@ static void efx_ef10_ev_fini(struct efx_channel *channel)
static int efx_ef10_ev_init(struct efx_channel *channel)
{
MCDI_DECLARE_BUF(inbuf,
MC_CMD_INIT_EVQ_IN_LEN(EFX_MAX_EVQ_SIZE * 8 /
EFX_BUF_SIZE));
MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_OUT_LEN);
MC_CMD_INIT_EVQ_V2_IN_LEN(EFX_MAX_EVQ_SIZE * 8 /
EFX_BUF_SIZE));
MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_V2_OUT_LEN);
size_t entries = channel->eventq.buf.len / EFX_BUF_SIZE;
struct efx_nic *efx = channel->efx;
struct efx_ef10_nic_data *nic_data;
bool supports_rx_merge;
size_t inlen, outlen;
unsigned int enabled, implemented;
dma_addr_t dma_addr;
int rc;
int i;

nic_data = efx->nic_data;
supports_rx_merge =
!!(nic_data->datapath_caps &
1 << MC_CMD_GET_CAPABILITIES_OUT_RX_BATCHING_LBN);

/* Fill event queue with all ones (i.e. empty events) */
memset(channel->eventq.buf.addr, 0xff, channel->eventq.buf.len);
Expand All @@ -2560,11 +2666,6 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_INSTANCE, channel->channel);
/* INIT_EVQ expects index in vector table, not absolute */
MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_IRQ_NUM, channel->channel);
MCDI_POPULATE_DWORD_4(inbuf, INIT_EVQ_IN_FLAGS,
INIT_EVQ_IN_FLAG_INTERRUPTING, 1,
INIT_EVQ_IN_FLAG_RX_MERGE, 1,
INIT_EVQ_IN_FLAG_TX_MERGE, 1,
INIT_EVQ_IN_FLAG_CUT_THRU, !supports_rx_merge);
MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_MODE,
MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS);
MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_LOAD, 0);
Expand All @@ -2573,6 +2674,27 @@ static int efx_ef10_ev_init(struct efx_channel *channel)
MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS);
MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_COUNT_THRSHLD, 0);

if (nic_data->datapath_caps2 &
1 << MC_CMD_GET_CAPABILITIES_V2_OUT_INIT_EVQ_V2_LBN) {
/* Use the new generic approach to specifying event queue
* configuration, requesting lower latency or higher throughput.
* The options that actually get used appear in the output.
*/
MCDI_POPULATE_DWORD_2(inbuf, INIT_EVQ_V2_IN_FLAGS,
INIT_EVQ_V2_IN_FLAG_INTERRUPTING, 1,
INIT_EVQ_V2_IN_FLAG_TYPE,
MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO);
} else {
bool cut_thru = !(nic_data->datapath_caps &
1 << MC_CMD_GET_CAPABILITIES_OUT_RX_BATCHING_LBN);

MCDI_POPULATE_DWORD_4(inbuf, INIT_EVQ_IN_FLAGS,
INIT_EVQ_IN_FLAG_INTERRUPTING, 1,
INIT_EVQ_IN_FLAG_RX_MERGE, 1,
INIT_EVQ_IN_FLAG_TX_MERGE, 1,
INIT_EVQ_IN_FLAG_CUT_THRU, cut_thru);
}

dma_addr = channel->eventq.buf.dma_addr;
for (i = 0; i < entries; ++i) {
MCDI_SET_ARRAY_QWORD(inbuf, INIT_EVQ_IN_DMA_ADDR, i, dma_addr);
Expand All @@ -2583,15 +2705,22 @@ static int efx_ef10_ev_init(struct efx_channel *channel)

rc = efx_mcdi_rpc(efx, MC_CMD_INIT_EVQ, inbuf, inlen,
outbuf, sizeof(outbuf), &outlen);

if (outlen >= MC_CMD_INIT_EVQ_V2_OUT_LEN)
netif_dbg(efx, drv, efx->net_dev,
"Channel %d using event queue flags %08x\n",
channel->channel,
MCDI_DWORD(outbuf, INIT_EVQ_V2_OUT_FLAGS));

/* IRQ return is ignored */
if (channel->channel || rc)
return rc;

/* Successfully created event queue on channel 0 */
rc = efx_mcdi_get_workarounds(efx, &implemented, &enabled);
if (rc == -ENOSYS) {
/* GET_WORKAROUNDS was implemented before the bug26807
* workaround, thus the latter must be unavailable in this fw
/* GET_WORKAROUNDS was implemented before this workaround,
* thus it must be unavailable in this firmware.
*/
nic_data->workaround_26807 = false;
rc = 0;
Expand Down
Loading

0 comments on commit 85be21b

Please sign in to comment.