diff options
Diffstat (limited to 'drivers/net/ethernet/sfc/ef10.c')
-rw-r--r-- | drivers/net/ethernet/sfc/ef10.c | 549 |
1 files changed, 409 insertions, 140 deletions
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 676c3c057bfb..174a92f5fe51 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -14,6 +14,7 @@ #include "mcdi_pcol.h" #include "nic.h" #include "workarounds.h" +#include "selftest.h" #include <linux/in.h> #include <linux/jhash.h> #include <linux/wait.h> @@ -52,31 +53,31 @@ struct efx_ef10_filter_table { struct { unsigned long spec; /* pointer to spec plus flag bits */ -/* BUSY flag indicates that an update is in progress. STACK_OLD is - * used to mark and sweep stack-owned MAC filters. +/* BUSY flag indicates that an update is in progress. AUTO_OLD is + * used to mark and sweep MAC filters for the device address lists. */ #define EFX_EF10_FILTER_FLAG_BUSY 1UL -#define EFX_EF10_FILTER_FLAG_STACK_OLD 2UL +#define EFX_EF10_FILTER_FLAG_AUTO_OLD 2UL #define EFX_EF10_FILTER_FLAGS 3UL u64 handle; /* firmware handle */ } *entry; wait_queue_head_t waitq; /* Shadow of net_device address lists, guarded by mac_lock */ -#define EFX_EF10_FILTER_STACK_UC_MAX 32 -#define EFX_EF10_FILTER_STACK_MC_MAX 256 +#define EFX_EF10_FILTER_DEV_UC_MAX 32 +#define EFX_EF10_FILTER_DEV_MC_MAX 256 struct { u8 addr[ETH_ALEN]; u16 id; - } stack_uc_list[EFX_EF10_FILTER_STACK_UC_MAX], - stack_mc_list[EFX_EF10_FILTER_STACK_MC_MAX]; - int stack_uc_count; /* negative for PROMISC */ - int stack_mc_count; /* negative for PROMISC/ALLMULTI */ + } dev_uc_list[EFX_EF10_FILTER_DEV_UC_MAX], + dev_mc_list[EFX_EF10_FILTER_DEV_MC_MAX]; + int dev_uc_count; /* negative for PROMISC */ + int dev_mc_count; /* negative for PROMISC/ALLMULTI */ }; /* An arbitrary search limit for the software hash table */ #define EFX_EF10_FILTER_SEARCH_LIMIT 200 -static void efx_ef10_rx_push_indir_table(struct efx_nic *efx); +static void efx_ef10_rx_push_rss_config(struct efx_nic *efx); static void efx_ef10_rx_free_indir_table(struct efx_nic *efx); static void efx_ef10_filter_table_remove(struct efx_nic *efx); @@ -263,6 +264,8 @@ static int efx_ef10_probe(struct efx_nic *efx) if (rc) goto fail3; + efx_ptp_probe(efx, NULL); + return 0; fail3: @@ -277,11 +280,17 @@ fail1: static int efx_ef10_free_vis(struct efx_nic *efx) { - int rc = efx_mcdi_rpc(efx, MC_CMD_FREE_VIS, NULL, 0, NULL, 0, NULL); + MCDI_DECLARE_BUF_OUT_OR_ERR(outbuf, 0); + size_t outlen; + int rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FREE_VIS, NULL, 0, + outbuf, sizeof(outbuf), &outlen); /* -EALREADY means nothing to free, so ignore */ if (rc == -EALREADY) rc = 0; + if (rc) + efx_mcdi_display_error(efx, MC_CMD_FREE_VIS, 0, outbuf, outlen, + rc); return rc; } @@ -465,9 +474,10 @@ static void efx_ef10_remove(struct efx_nic *efx) struct efx_ef10_nic_data *nic_data = efx->nic_data; int rc; + efx_ptp_remove(efx); + efx_mcdi_mon_remove(efx); - /* This needs to be after efx_ptp_remove_channel() with no filters */ efx_ef10_rx_free_indir_table(efx); if (nic_data->wc_membase) @@ -669,10 +679,21 @@ static int efx_ef10_init_nic(struct efx_nic *efx) nic_data->must_restore_piobufs = false; } - efx_ef10_rx_push_indir_table(efx); + efx_ef10_rx_push_rss_config(efx); return 0; } +static void efx_ef10_reset_mc_allocations(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + /* All our allocations have been reset */ + nic_data->must_realloc_vis = true; + nic_data->must_restore_filters = true; + nic_data->must_restore_piobufs = true; + nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID; +} + static int efx_ef10_map_reset_flags(u32 *flags) { enum { @@ -703,6 +724,19 @@ static int efx_ef10_map_reset_flags(u32 *flags) return -EINVAL; } +static int efx_ef10_reset(struct efx_nic *efx, enum reset_type reset_type) +{ + int rc = efx_mcdi_reset(efx, reset_type); + + /* If it was a port reset, trigger reallocation of MC resources. + * Note that on an MC reset nothing needs to be done now because we'll + * detect the MC reset later and handle it then. + */ + if (reset_type == RESET_TYPE_ALL && !rc) + efx_ef10_reset_mc_allocations(efx); + return rc; +} + #define EF10_DMA_STAT(ext_name, mcdi_name) \ [EF10_STAT_ ## ext_name] = \ { #ext_name, 64, 8 * MC_CMD_MAC_ ## mcdi_name } @@ -764,8 +798,8 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = { EF10_DMA_STAT(rx_dp_q_disabled_packets, RXDP_Q_DISABLED_PKTS), EF10_DMA_STAT(rx_dp_di_dropped_packets, RXDP_DI_DROPPED_PKTS), EF10_DMA_STAT(rx_dp_streaming_packets, RXDP_STREAMING_PKTS), - EF10_DMA_STAT(rx_dp_emerg_fetch, RXDP_EMERGENCY_FETCH_CONDITIONS), - EF10_DMA_STAT(rx_dp_emerg_wait, RXDP_EMERGENCY_WAIT_CONDITIONS), + EF10_DMA_STAT(rx_dp_hlb_fetch, RXDP_EMERGENCY_FETCH_CONDITIONS), + EF10_DMA_STAT(rx_dp_hlb_wait, RXDP_EMERGENCY_WAIT_CONDITIONS), }; #define HUNT_COMMON_STAT_MASK ((1ULL << EF10_STAT_tx_bytes) | \ @@ -834,8 +868,8 @@ static const struct efx_hw_stat_desc efx_ef10_stat_desc[EF10_STAT_COUNT] = { (1ULL << EF10_STAT_rx_dp_q_disabled_packets) | \ (1ULL << EF10_STAT_rx_dp_di_dropped_packets) | \ (1ULL << EF10_STAT_rx_dp_streaming_packets) | \ - (1ULL << EF10_STAT_rx_dp_emerg_fetch) | \ - (1ULL << EF10_STAT_rx_dp_emerg_wait)) + (1ULL << EF10_STAT_rx_dp_hlb_fetch) | \ + (1ULL << EF10_STAT_rx_dp_hlb_wait)) static u64 efx_ef10_raw_stat_mask(struct efx_nic *efx) { @@ -901,6 +935,7 @@ static int efx_ef10_try_update_nic_stats(struct efx_nic *efx) return -EAGAIN; /* Update derived statistics */ + efx_nic_fix_nodesc_drop_stat(efx, &stats[EF10_STAT_rx_nodesc_drops]); stats[EF10_STAT_rx_good_bytes] = stats[EF10_STAT_rx_bytes] - stats[EF10_STAT_rx_bytes_minus_good_bytes]; @@ -1067,10 +1102,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) nic_data->warm_boot_count = rc; /* All our allocations have been reset */ - nic_data->must_realloc_vis = true; - nic_data->must_restore_filters = true; - nic_data->must_restore_piobufs = true; - nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID; + efx_ef10_reset_mc_allocations(efx); /* The datapath firmware might have been changed */ nic_data->must_check_datapath_caps = true; @@ -1241,8 +1273,8 @@ static void efx_ef10_tx_init(struct efx_tx_queue *tx_queue) return; fail: - WARN_ON(true); - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); + netdev_WARN(efx->net_dev, "failed to initialise TXQ %d\n", + tx_queue->queue); } static void efx_ef10_tx_fini(struct efx_tx_queue *tx_queue) @@ -1256,7 +1288,7 @@ static void efx_ef10_tx_fini(struct efx_tx_queue *tx_queue) MCDI_SET_DWORD(inbuf, FINI_TXQ_IN_INSTANCE, tx_queue->queue); - rc = efx_mcdi_rpc(efx, MC_CMD_FINI_TXQ, inbuf, sizeof(inbuf), + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_TXQ, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc && rc != -EALREADY) @@ -1265,7 +1297,8 @@ static void efx_ef10_tx_fini(struct efx_tx_queue *tx_queue) return; fail: - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); + efx_mcdi_display_error(efx, MC_CMD_FINI_TXQ, MC_CMD_FINI_TXQ_IN_LEN, + outbuf, outlen, rc); } static void efx_ef10_tx_remove(struct efx_tx_queue *tx_queue) @@ -1408,12 +1441,12 @@ static void efx_ef10_rx_free_indir_table(struct efx_nic *efx) nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID; } -static void efx_ef10_rx_push_indir_table(struct efx_nic *efx) +static void efx_ef10_rx_push_rss_config(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; int rc; - netif_dbg(efx, drv, efx->net_dev, "pushing RX indirection table\n"); + netif_dbg(efx, drv, efx->net_dev, "pushing RSS config\n"); if (nic_data->rx_rss_context == EFX_EF10_RSS_CONTEXT_INVALID) { rc = efx_ef10_alloc_rss_context(efx, &nic_data->rx_rss_context); @@ -1461,8 +1494,9 @@ static void efx_ef10_rx_init(struct efx_rx_queue *rx_queue) MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_LABEL, efx_rx_queue_index(rx_queue)); MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_INSTANCE, efx_rx_queue_index(rx_queue)); - MCDI_POPULATE_DWORD_1(inbuf, INIT_RXQ_IN_FLAGS, - INIT_RXQ_IN_FLAG_PREFIX, 1); + MCDI_POPULATE_DWORD_2(inbuf, INIT_RXQ_IN_FLAGS, + INIT_RXQ_IN_FLAG_PREFIX, 1, + INIT_RXQ_IN_FLAG_TIMESTAMP, 1); MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_OWNER_ID, 0); MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_PORT_ID, EVB_PORT_ID_ASSIGNED); @@ -1481,13 +1515,8 @@ static void efx_ef10_rx_init(struct efx_rx_queue *rx_queue) rc = efx_mcdi_rpc(efx, MC_CMD_INIT_RXQ, inbuf, inlen, outbuf, sizeof(outbuf), &outlen); if (rc) - goto fail; - - return; - -fail: - WARN_ON(true); - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); + netdev_WARN(efx->net_dev, "failed to initialise RXQ %d\n", + efx_rx_queue_index(rx_queue)); } static void efx_ef10_rx_fini(struct efx_rx_queue *rx_queue) @@ -1501,7 +1530,7 @@ static void efx_ef10_rx_fini(struct efx_rx_queue *rx_queue) MCDI_SET_DWORD(inbuf, FINI_RXQ_IN_INSTANCE, efx_rx_queue_index(rx_queue)); - rc = efx_mcdi_rpc(efx, MC_CMD_FINI_RXQ, inbuf, sizeof(inbuf), + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_RXQ, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc && rc != -EALREADY) @@ -1510,7 +1539,8 @@ static void efx_ef10_rx_fini(struct efx_rx_queue *rx_queue) return; fail: - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); + efx_mcdi_display_error(efx, MC_CMD_FINI_RXQ, MC_CMD_FINI_RXQ_IN_LEN, + outbuf, outlen, rc); } static void efx_ef10_rx_remove(struct efx_rx_queue *rx_queue) @@ -1647,15 +1677,7 @@ 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 (rc) - goto fail; - /* IRQ return is ignored */ - - return 0; - -fail: - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); return rc; } @@ -1669,7 +1691,7 @@ static void efx_ef10_ev_fini(struct efx_channel *channel) MCDI_SET_DWORD(inbuf, FINI_EVQ_IN_INSTANCE, channel->channel); - rc = efx_mcdi_rpc(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf), + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); if (rc && rc != -EALREADY) @@ -1678,7 +1700,8 @@ static void efx_ef10_ev_fini(struct efx_channel *channel) return; fail: - netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); + efx_mcdi_display_error(efx, MC_CMD_FINI_EVQ, MC_CMD_FINI_EVQ_IN_LEN, + outbuf, outlen, rc); } static void efx_ef10_ev_remove(struct efx_channel *channel) @@ -1717,8 +1740,6 @@ static void efx_ef10_handle_rx_abort(struct efx_rx_queue *rx_queue) { unsigned int rx_desc_ptr; - WARN_ON(rx_queue->scatter_n == 0); - netif_dbg(rx_queue->efx, hw, rx_queue->efx->net_dev, "scattered RX aborted (dropping %u buffers)\n", rx_queue->scatter_n); @@ -1754,7 +1775,10 @@ static int efx_ef10_handle_rx_event(struct efx_channel *channel, rx_l4_class = EFX_QWORD_FIELD(*event, ESF_DZ_RX_L4_CLASS); rx_cont = EFX_QWORD_FIELD(*event, ESF_DZ_RX_CONT); - WARN_ON(EFX_QWORD_FIELD(*event, ESF_DZ_RX_DROP_EVENT)); + if (EFX_QWORD_FIELD(*event, ESF_DZ_RX_DROP_EVENT)) + netdev_WARN(efx->net_dev, "saw RX_DROP_EVENT: event=" + EFX_QWORD_FMT "\n", + EFX_QWORD_VAL(*event)); rx_queue = efx_channel_get_rx_queue(channel); @@ -1765,17 +1789,27 @@ static int efx_ef10_handle_rx_event(struct efx_channel *channel, ((1 << ESF_DZ_RX_DSC_PTR_LBITS_WIDTH) - 1)); if (n_descs != rx_queue->scatter_n + 1) { + struct efx_ef10_nic_data *nic_data = efx->nic_data; + /* detect rx abort */ if (unlikely(n_descs == rx_queue->scatter_n)) { - WARN_ON(rx_bytes != 0); + if (rx_queue->scatter_n == 0 || rx_bytes != 0) + netdev_WARN(efx->net_dev, + "invalid RX abort: scatter_n=%u event=" + EFX_QWORD_FMT "\n", + rx_queue->scatter_n, + EFX_QWORD_VAL(*event)); efx_ef10_handle_rx_abort(rx_queue); return 0; } - if (unlikely(rx_queue->scatter_n != 0)) { - /* Scattered packet completions cannot be - * merged, so something has gone wrong. - */ + /* Check that RX completion merging is valid, i.e. + * the current firmware supports it and this is a + * non-scattered packet. + */ + if (!(nic_data->datapath_caps & + (1 << MC_CMD_GET_CAPABILITIES_OUT_RX_BATCHING_LBN)) || + rx_queue->scatter_n != 0 || rx_cont) { efx_ef10_handle_rx_bad_lbits( rx_queue, next_ptr_lbits, (rx_queue->removed_count + @@ -1901,7 +1935,7 @@ static void efx_ef10_handle_driver_generated_event(struct efx_channel *channel, * events, so efx_process_channel() won't refill the * queue. Refill it here */ - efx_fast_push_rx_descriptors(&channel->rx_queue); + efx_fast_push_rx_descriptors(&channel->rx_queue, true); break; default: netif_err(efx, hw, efx->net_dev, @@ -2232,7 +2266,9 @@ static void efx_ef10_filter_push_prep(struct efx_nic *efx, MC_CMD_FILTER_OP_IN_RX_DEST_HOST); MCDI_SET_DWORD(inbuf, FILTER_OP_IN_TX_DEST, MC_CMD_FILTER_OP_IN_TX_DEST_DEFAULT); - MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_QUEUE, spec->dmaq_id); + MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_QUEUE, + spec->dmaq_id == EFX_FILTER_RX_DMAQ_ID_DROP ? + 0 : spec->dmaq_id); MCDI_SET_DWORD(inbuf, FILTER_OP_IN_RX_MODE, (spec->flags & EFX_FILTER_FLAG_RX_RSS) ? MC_CMD_FILTER_OP_IN_RX_MODE_RSS : @@ -2257,6 +2293,8 @@ static int efx_ef10_filter_push(struct efx_nic *efx, outbuf, sizeof(outbuf), NULL); if (rc == 0) *handle = MCDI_QWORD(outbuf, FILTER_OP_OUT_HANDLE); + if (rc == -ENOSPC) + rc = -EBUSY; /* to match efx_farch_filter_insert() */ return rc; } @@ -2326,10 +2364,7 @@ static s32 efx_ef10_filter_insert(struct efx_nic *efx, EFX_EF10_FILTER_FLAG_BUSY) break; if (spec->priority < saved_spec->priority && - !(saved_spec->priority == - EFX_FILTER_PRI_REQUIRED && - saved_spec->flags & - EFX_FILTER_FLAG_RX_STACK)) { + spec->priority != EFX_FILTER_PRI_AUTO) { rc = -EPERM; goto out_unlock; } @@ -2383,11 +2418,13 @@ found: */ saved_spec = efx_ef10_filter_entry_spec(table, ins_index); if (saved_spec) { - if (spec->flags & EFX_FILTER_FLAG_RX_STACK) { + if (spec->priority == EFX_FILTER_PRI_AUTO && + saved_spec->priority >= EFX_FILTER_PRI_AUTO) { /* Just make sure it won't be removed */ - saved_spec->flags |= EFX_FILTER_FLAG_RX_STACK; + if (saved_spec->priority > EFX_FILTER_PRI_AUTO) + saved_spec->flags |= EFX_FILTER_FLAG_RX_OVER_AUTO; table->entry[ins_index].spec &= - ~EFX_EF10_FILTER_FLAG_STACK_OLD; + ~EFX_EF10_FILTER_FLAG_AUTO_OLD; rc = ins_index; goto out_unlock; } @@ -2427,8 +2464,11 @@ found: if (rc == 0) { if (replacing) { /* Update the fields that may differ */ + if (saved_spec->priority == EFX_FILTER_PRI_AUTO) + saved_spec->flags |= + EFX_FILTER_FLAG_RX_OVER_AUTO; saved_spec->priority = spec->priority; - saved_spec->flags &= EFX_FILTER_FLAG_RX_STACK; + saved_spec->flags &= EFX_FILTER_FLAG_RX_OVER_AUTO; saved_spec->flags |= spec->flags; saved_spec->rss_context = spec->rss_context; saved_spec->dmaq_id = spec->dmaq_id; @@ -2497,13 +2537,13 @@ static void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx) } /* Remove a filter. - * If !stack_requested, remove by ID - * If stack_requested, remove by index + * If !by_index, remove by ID + * If by_index, remove by index * Filter ID may come from userland and must be range-checked. */ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, - enum efx_filter_priority priority, - u32 filter_id, bool stack_requested) + unsigned int priority_mask, + u32 filter_id, bool by_index) { unsigned int filter_idx = filter_id % HUNT_FILTER_TBL_ROWS; struct efx_ef10_filter_table *table = efx->filter_state; @@ -2527,26 +2567,41 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, spin_unlock_bh(&efx->filter_lock); schedule(); } + spec = efx_ef10_filter_entry_spec(table, filter_idx); - if (!spec || spec->priority > priority || - (!stack_requested && + if (!spec || + (!by_index && efx_ef10_filter_rx_match_pri(table, spec->match_flags) != filter_id / HUNT_FILTER_TBL_ROWS)) { rc = -ENOENT; goto out_unlock; } + + if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO && + priority_mask == (1U << EFX_FILTER_PRI_AUTO)) { + /* Just remove flags */ + spec->flags &= ~EFX_FILTER_FLAG_RX_OVER_AUTO; + table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_AUTO_OLD; + rc = 0; + goto out_unlock; + } + + if (!(priority_mask & (1U << spec->priority))) { + rc = -ENOENT; + goto out_unlock; + } + table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_BUSY; spin_unlock_bh(&efx->filter_lock); - if (spec->flags & EFX_FILTER_FLAG_RX_STACK && !stack_requested) { - /* Reset steering of a stack-owned filter */ + if (spec->flags & EFX_FILTER_FLAG_RX_OVER_AUTO) { + /* Reset to an automatic filter */ struct efx_filter_spec new_spec = *spec; - new_spec.priority = EFX_FILTER_PRI_REQUIRED; + new_spec.priority = EFX_FILTER_PRI_AUTO; new_spec.flags = (EFX_FILTER_FLAG_RX | - EFX_FILTER_FLAG_RX_RSS | - EFX_FILTER_FLAG_RX_STACK); + EFX_FILTER_FLAG_RX_RSS); new_spec.dmaq_id = 0; new_spec.rss_context = EFX_FILTER_RSS_CONTEXT_DEFAULT; rc = efx_ef10_filter_push(efx, &new_spec, @@ -2574,6 +2629,7 @@ static int efx_ef10_filter_remove_internal(struct efx_nic *efx, efx_ef10_filter_set_entry(table, filter_idx, NULL, 0); } } + table->entry[filter_idx].spec &= ~EFX_EF10_FILTER_FLAG_BUSY; wake_up_all(&table->waitq); out_unlock: @@ -2586,7 +2642,8 @@ static int efx_ef10_filter_remove_safe(struct efx_nic *efx, enum efx_filter_priority priority, u32 filter_id) { - return efx_ef10_filter_remove_internal(efx, priority, filter_id, false); + return efx_ef10_filter_remove_internal(efx, 1U << priority, + filter_id, false); } static int efx_ef10_filter_get_safe(struct efx_nic *efx, @@ -2612,10 +2669,24 @@ static int efx_ef10_filter_get_safe(struct efx_nic *efx, return rc; } -static void efx_ef10_filter_clear_rx(struct efx_nic *efx, +static int efx_ef10_filter_clear_rx(struct efx_nic *efx, enum efx_filter_priority priority) { - /* TODO */ + unsigned int priority_mask; + unsigned int i; + int rc; + + priority_mask = (((1U << (priority + 1)) - 1) & + ~(1U << EFX_FILTER_PRI_AUTO)); + + for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { + rc = efx_ef10_filter_remove_internal(efx, priority_mask, + i, true); + if (rc && rc != -ENOENT) + return rc; + } + + return 0; } static u32 efx_ef10_filter_count_rx_used(struct efx_nic *efx, @@ -2716,8 +2787,6 @@ static s32 efx_ef10_filter_rfs_insert(struct efx_nic *efx, rc = -EBUSY; goto fail_unlock; } - EFX_WARN_ON_PARANOID(saved_spec->flags & - EFX_FILTER_FLAG_RX_STACK); if (spec->priority < saved_spec->priority) { rc = -EPERM; goto fail_unlock; @@ -3027,8 +3096,11 @@ static void efx_ef10_filter_table_remove(struct efx_nic *efx) table->entry[filter_idx].handle); rc = efx_mcdi_rpc(efx, MC_CMD_FILTER_OP, inbuf, sizeof(inbuf), NULL, 0, NULL); - - WARN_ON(rc != 0); + if (rc) + netdev_WARN(efx->net_dev, + "filter_idx=%#x handle=%#llx\n", + filter_idx, + table->entry[filter_idx].handle); kfree(spec); } @@ -3052,15 +3124,15 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) /* Mark old filters that may need to be removed */ spin_lock_bh(&efx->filter_lock); - n = table->stack_uc_count < 0 ? 1 : table->stack_uc_count; + n = table->dev_uc_count < 0 ? 1 : table->dev_uc_count; for (i = 0; i < n; i++) { - filter_idx = table->stack_uc_list[i].id % HUNT_FILTER_TBL_ROWS; - table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_STACK_OLD; + filter_idx = table->dev_uc_list[i].id % HUNT_FILTER_TBL_ROWS; + table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; } - n = table->stack_mc_count < 0 ? 1 : table->stack_mc_count; + n = table->dev_mc_count < 0 ? 1 : table->dev_mc_count; for (i = 0; i < n; i++) { - filter_idx = table->stack_mc_list[i].id % HUNT_FILTER_TBL_ROWS; - table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_STACK_OLD; + filter_idx = table->dev_mc_list[i].id % HUNT_FILTER_TBL_ROWS; + table->entry[filter_idx].spec |= EFX_EF10_FILTER_FLAG_AUTO_OLD; } spin_unlock_bh(&efx->filter_lock); @@ -3069,28 +3141,28 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) */ netif_addr_lock_bh(net_dev); if (net_dev->flags & IFF_PROMISC || - netdev_uc_count(net_dev) >= EFX_EF10_FILTER_STACK_UC_MAX) { - table->stack_uc_count = -1; + netdev_uc_count(net_dev) >= EFX_EF10_FILTER_DEV_UC_MAX) { + table->dev_uc_count = -1; } else { - table->stack_uc_count = 1 + netdev_uc_count(net_dev); - memcpy(table->stack_uc_list[0].addr, net_dev->dev_addr, + table->dev_uc_count = 1 + netdev_uc_count(net_dev); + memcpy(table->dev_uc_list[0].addr, net_dev->dev_addr, ETH_ALEN); i = 1; netdev_for_each_uc_addr(uc, net_dev) { - memcpy(table->stack_uc_list[i].addr, + memcpy(table->dev_uc_list[i].addr, uc->addr, ETH_ALEN); i++; } } if (net_dev->flags & (IFF_PROMISC | IFF_ALLMULTI) || - netdev_mc_count(net_dev) >= EFX_EF10_FILTER_STACK_MC_MAX) { - table->stack_mc_count = -1; + netdev_mc_count(net_dev) >= EFX_EF10_FILTER_DEV_MC_MAX) { + table->dev_mc_count = -1; } else { - table->stack_mc_count = 1 + netdev_mc_count(net_dev); - eth_broadcast_addr(table->stack_mc_list[0].addr); + table->dev_mc_count = 1 + netdev_mc_count(net_dev); + eth_broadcast_addr(table->dev_mc_list[0].addr); i = 1; netdev_for_each_mc_addr(mc, net_dev) { - memcpy(table->stack_mc_list[i].addr, + memcpy(table->dev_mc_list[i].addr, mc->addr, ETH_ALEN); i++; } @@ -3098,90 +3170,86 @@ static void efx_ef10_filter_sync_rx_mode(struct efx_nic *efx) netif_addr_unlock_bh(net_dev); /* Insert/renew unicast filters */ - if (table->stack_uc_count >= 0) { - for (i = 0; i < table->stack_uc_count; i++) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_REQUIRED, - EFX_FILTER_FLAG_RX_RSS | - EFX_FILTER_FLAG_RX_STACK, + if (table->dev_uc_count >= 0) { + for (i = 0; i < table->dev_uc_count; i++) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, 0); efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, - table->stack_uc_list[i].addr); + table->dev_uc_list[i].addr); rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { /* Fall back to unicast-promisc */ while (i--) efx_ef10_filter_remove_safe( - efx, EFX_FILTER_PRI_REQUIRED, - table->stack_uc_list[i].id); - table->stack_uc_count = -1; + efx, EFX_FILTER_PRI_AUTO, + table->dev_uc_list[i].id); + table->dev_uc_count = -1; break; } - table->stack_uc_list[i].id = rc; + table->dev_uc_list[i].id = rc; } } - if (table->stack_uc_count < 0) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_REQUIRED, - EFX_FILTER_FLAG_RX_RSS | - EFX_FILTER_FLAG_RX_STACK, + if (table->dev_uc_count < 0) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, 0); efx_filter_set_uc_def(&spec); rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { WARN_ON(1); - table->stack_uc_count = 0; + table->dev_uc_count = 0; } else { - table->stack_uc_list[0].id = rc; + table->dev_uc_list[0].id = rc; } } /* Insert/renew multicast filters */ - if (table->stack_mc_count >= 0) { - for (i = 0; i < table->stack_mc_count; i++) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_REQUIRED, - EFX_FILTER_FLAG_RX_RSS | - EFX_FILTER_FLAG_RX_STACK, + if (table->dev_mc_count >= 0) { + for (i = 0; i < table->dev_mc_count; i++) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, 0); efx_filter_set_eth_local(&spec, EFX_FILTER_VID_UNSPEC, - table->stack_mc_list[i].addr); + table->dev_mc_list[i].addr); rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { /* Fall back to multicast-promisc */ while (i--) efx_ef10_filter_remove_safe( - efx, EFX_FILTER_PRI_REQUIRED, - table->stack_mc_list[i].id); - table->stack_mc_count = -1; + efx, EFX_FILTER_PRI_AUTO, + table->dev_mc_list[i].id); + table->dev_mc_count = -1; break; } - table->stack_mc_list[i].id = rc; + table->dev_mc_list[i].id = rc; } } - if (table->stack_mc_count < 0) { - efx_filter_init_rx(&spec, EFX_FILTER_PRI_REQUIRED, - EFX_FILTER_FLAG_RX_RSS | - EFX_FILTER_FLAG_RX_STACK, + if (table->dev_mc_count < 0) { + efx_filter_init_rx(&spec, EFX_FILTER_PRI_AUTO, + EFX_FILTER_FLAG_RX_RSS, 0); efx_filter_set_mc_def(&spec); rc = efx_ef10_filter_insert(efx, &spec, true); if (rc < 0) { WARN_ON(1); - table->stack_mc_count = 0; + table->dev_mc_count = 0; } else { - table->stack_mc_list[0].id = rc; + table->dev_mc_list[0].id = rc; } } /* Remove filters that weren't renewed. Since nothing else - * changes the STACK_OLD flag or removes these filters, we + * changes the AUTO_OLD flag or removes these filters, we * don't need to hold the filter_lock while scanning for * these filters. */ for (i = 0; i < HUNT_FILTER_TBL_ROWS; i++) { if (ACCESS_ONCE(table->entry[i].spec) & - EFX_EF10_FILTER_FLAG_STACK_OLD) { - if (efx_ef10_filter_remove_internal(efx, - EFX_FILTER_PRI_REQUIRED, - i, true) < 0) + EFX_EF10_FILTER_FLAG_AUTO_OLD) { + if (efx_ef10_filter_remove_internal( + efx, 1U << EFX_FILTER_PRI_AUTO, + i, true) < 0) remove_failed = true; } } @@ -3195,6 +3263,87 @@ static int efx_ef10_mac_reconfigure(struct efx_nic *efx) return efx_mcdi_set_mac(efx); } +static int efx_ef10_start_bist(struct efx_nic *efx, u32 bist_type) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_START_BIST_IN_LEN); + + MCDI_SET_DWORD(inbuf, START_BIST_IN_TYPE, bist_type); + return efx_mcdi_rpc(efx, MC_CMD_START_BIST, inbuf, sizeof(inbuf), + NULL, 0, NULL); +} + +/* MC BISTs follow a different poll mechanism to phy BISTs. + * The BIST is done in the poll handler on the MC, and the MCDI command + * will block until the BIST is done. + */ +static int efx_ef10_poll_bist(struct efx_nic *efx) +{ + int rc; + MCDI_DECLARE_BUF(outbuf, MC_CMD_POLL_BIST_OUT_LEN); + size_t outlen; + u32 result; + + rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc != 0) + return rc; + + if (outlen < MC_CMD_POLL_BIST_OUT_LEN) + return -EIO; + + result = MCDI_DWORD(outbuf, POLL_BIST_OUT_RESULT); + switch (result) { + case MC_CMD_POLL_BIST_PASSED: + netif_dbg(efx, hw, efx->net_dev, "BIST passed.\n"); + return 0; + case MC_CMD_POLL_BIST_TIMEOUT: + netif_err(efx, hw, efx->net_dev, "BIST timed out\n"); + return -EIO; + case MC_CMD_POLL_BIST_FAILED: + netif_err(efx, hw, efx->net_dev, "BIST failed.\n"); + return -EIO; + default: + netif_err(efx, hw, efx->net_dev, + "BIST returned unknown result %u", result); + return -EIO; + } +} + +static int efx_ef10_run_bist(struct efx_nic *efx, u32 bist_type) +{ + int rc; + + netif_dbg(efx, drv, efx->net_dev, "starting BIST type %u\n", bist_type); + + rc = efx_ef10_start_bist(efx, bist_type); + if (rc != 0) + return rc; + + return efx_ef10_poll_bist(efx); +} + +static int +efx_ef10_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) +{ + int rc, rc2; + + efx_reset_down(efx, RESET_TYPE_WORLD); + + rc = efx_mcdi_rpc(efx, MC_CMD_ENABLE_OFFLINE_BIST, + NULL, 0, NULL, 0, NULL); + if (rc != 0) + goto out; + + tests->memory = efx_ef10_run_bist(efx, MC_CMD_MC_MEM_BIST) ? -1 : 1; + tests->registers = efx_ef10_run_bist(efx, MC_CMD_REG_BIST) ? -1 : 1; + + rc = efx_mcdi_reset(efx, RESET_TYPE_WORLD); + +out: + rc2 = efx_reset_up(efx, RESET_TYPE_WORLD, rc == 0); + return rc ? rc : rc2; +} + #ifdef CONFIG_SFC_MTD struct efx_ef10_nvram_type_info { @@ -3213,6 +3362,7 @@ static const struct efx_ef10_nvram_type_info efx_ef10_nvram_types[] = { { NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 0, 1, "sfc_exp_rom_cfg" }, { NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 0, 2, "sfc_exp_rom_cfg" }, { NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 0, 3, "sfc_exp_rom_cfg" }, + { NVRAM_PARTITION_TYPE_LICENSE, 0, 0, "sfc_license" }, { NVRAM_PARTITION_TYPE_PHY_MIN, 0xff, 0, "sfc_phy_fw" }, }; @@ -3320,6 +3470,119 @@ static void efx_ef10_ptp_write_host_time(struct efx_nic *efx, u32 host_time) _efx_writed(efx, cpu_to_le32(host_time), ER_DZ_MC_DB_LWRD); } +static int efx_ef10_rx_enable_timestamping(struct efx_channel *channel, + bool temp) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_TIME_EVENT_SUBSCRIBE_LEN); + int rc; + + if (channel->sync_events_state == SYNC_EVENTS_REQUESTED || + channel->sync_events_state == SYNC_EVENTS_VALID || + (temp && channel->sync_events_state == SYNC_EVENTS_DISABLED)) + return 0; + channel->sync_events_state = SYNC_EVENTS_REQUESTED; + + MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_TIME_EVENT_SUBSCRIBE); + MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); + MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_SUBSCRIBE_QUEUE, + channel->channel); + + rc = efx_mcdi_rpc(channel->efx, MC_CMD_PTP, + inbuf, sizeof(inbuf), NULL, 0, NULL); + + if (rc != 0) + channel->sync_events_state = temp ? SYNC_EVENTS_QUIESCENT : + SYNC_EVENTS_DISABLED; + + return rc; +} + +static int efx_ef10_rx_disable_timestamping(struct efx_channel *channel, + bool temp) +{ + MCDI_DECLARE_BUF(inbuf, MC_CMD_PTP_IN_TIME_EVENT_UNSUBSCRIBE_LEN); + int rc; + + if (channel->sync_events_state == SYNC_EVENTS_DISABLED || + (temp && channel->sync_events_state == SYNC_EVENTS_QUIESCENT)) + return 0; + if (channel->sync_events_state == SYNC_EVENTS_QUIESCENT) { + channel->sync_events_state = SYNC_EVENTS_DISABLED; + return 0; + } + channel->sync_events_state = temp ? SYNC_EVENTS_QUIESCENT : + SYNC_EVENTS_DISABLED; + + MCDI_SET_DWORD(inbuf, PTP_IN_OP, MC_CMD_PTP_OP_TIME_EVENT_UNSUBSCRIBE); + MCDI_SET_DWORD(inbuf, PTP_IN_PERIPH_ID, 0); + MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_UNSUBSCRIBE_CONTROL, + MC_CMD_PTP_IN_TIME_EVENT_UNSUBSCRIBE_SINGLE); + MCDI_SET_DWORD(inbuf, PTP_IN_TIME_EVENT_UNSUBSCRIBE_QUEUE, + channel->channel); + + rc = efx_mcdi_rpc(channel->efx, MC_CMD_PTP, + inbuf, sizeof(inbuf), NULL, 0, NULL); + + return rc; +} + +static int efx_ef10_ptp_set_ts_sync_events(struct efx_nic *efx, bool en, + bool temp) +{ + int (*set)(struct efx_channel *channel, bool temp); + struct efx_channel *channel; + + set = en ? + efx_ef10_rx_enable_timestamping : + efx_ef10_rx_disable_timestamping; + + efx_for_each_channel(channel, efx) { + int rc = set(channel, temp); + if (en && rc != 0) { + efx_ef10_ptp_set_ts_sync_events(efx, false, temp); + return rc; + } + } + + return 0; +} + +static int efx_ef10_ptp_set_ts_config(struct efx_nic *efx, + struct hwtstamp_config *init) +{ + int rc; + + switch (init->rx_filter) { + case HWTSTAMP_FILTER_NONE: + efx_ef10_ptp_set_ts_sync_events(efx, false, false); + /* if TX timestamping is still requested then leave PTP on */ + return efx_ptp_change_mode(efx, + init->tx_type != HWTSTAMP_TX_OFF, 0); + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + init->rx_filter = HWTSTAMP_FILTER_ALL; + rc = efx_ptp_change_mode(efx, true, 0); + if (!rc) + rc = efx_ef10_ptp_set_ts_sync_events(efx, true, false); + if (rc) + efx_ptp_change_mode(efx, false, 0); + return rc; + default: + return -ERANGE; + } +} + const struct efx_nic_type efx_hunt_a0_nic_type = { .mem_map_size = efx_ef10_mem_map_size, .probe = efx_ef10_probe, @@ -3329,13 +3592,14 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .fini = efx_port_dummy_op_void, .map_reset_reason = efx_mcdi_map_reset_reason, .map_reset_flags = efx_ef10_map_reset_flags, - .reset = efx_mcdi_reset, + .reset = efx_ef10_reset, .probe_port = efx_mcdi_port_probe, .remove_port = efx_mcdi_port_remove, .fini_dmaq = efx_ef10_fini_dmaq, .describe_stats = efx_ef10_describe_stats, .update_stats = efx_ef10_update_stats, .start_stats = efx_mcdi_mac_start_stats, + .pull_stats = efx_mcdi_mac_pull_stats, .stop_stats = efx_mcdi_mac_stop_stats, .set_id_led = efx_mcdi_set_id_led, .push_irq_moderation = efx_ef10_push_irq_moderation, @@ -3345,7 +3609,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .get_wol = efx_ef10_get_wol, .set_wol = efx_ef10_set_wol, .resume_wol = efx_port_dummy_op_void, - /* TODO: test_chip */ + .test_chip = efx_ef10_test_chip, .test_nvram = efx_mcdi_nvram_test_all, .mcdi_request = efx_ef10_mcdi_request, .mcdi_poll_response = efx_ef10_mcdi_poll_response, @@ -3360,7 +3624,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .tx_init = efx_ef10_tx_init, .tx_remove = efx_ef10_tx_remove, .tx_write = efx_ef10_tx_write, - .rx_push_indir_table = efx_ef10_rx_push_indir_table, + .rx_push_rss_config = efx_ef10_rx_push_rss_config, .rx_probe = efx_ef10_rx_probe, .rx_init = efx_ef10_rx_init, .rx_remove = efx_ef10_rx_remove, @@ -3397,11 +3661,14 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { .mtd_sync = efx_mcdi_mtd_sync, #endif .ptp_write_host_time = efx_ef10_ptp_write_host_time, + .ptp_set_ts_sync_events = efx_ef10_ptp_set_ts_sync_events, + .ptp_set_ts_config = efx_ef10_ptp_set_ts_config, .revision = EFX_REV_HUNT_A0, .max_dma_mask = DMA_BIT_MASK(ESF_DZ_TX_KER_BUF_ADDR_WIDTH), .rx_prefix_size = ES_DZ_RX_PREFIX_SIZE, .rx_hash_offset = ES_DZ_RX_PREFIX_HASH_OFST, + .rx_ts_offset = ES_DZ_RX_PREFIX_TSTAMP_OFST, .can_rx_scatter = true, .always_rx_scatter = true, .max_interrupt_mode = EFX_INT_MODE_MSIX, @@ -3410,4 +3677,6 @@ const struct efx_nic_type efx_hunt_a0_nic_type = { NETIF_F_RXHASH | NETIF_F_NTUPLE), .mcdi_max_ver = 2, .max_rx_ip_filters = HUNT_FILTER_TBL_ROWS, + .hwtstamp_filters = 1 << HWTSTAMP_FILTER_NONE | + 1 << HWTSTAMP_FILTER_ALL, }; |