162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/**************************************************************************** 362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards 462306a36Sopenharmony_ci * Copyright 2005-2006 Fen Systems Ltd. 562306a36Sopenharmony_ci * Copyright 2006-2012 Solarflare Communications Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/netdevice.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/kernel_stat.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/ethtool.h> 1462306a36Sopenharmony_ci#include <linux/ip.h> 1562306a36Sopenharmony_ci#include <linux/in.h> 1662306a36Sopenharmony_ci#include <linux/udp.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include "net_driver.h" 2062306a36Sopenharmony_ci#include "efx.h" 2162306a36Sopenharmony_ci#include "nic.h" 2262306a36Sopenharmony_ci#include "selftest.h" 2362306a36Sopenharmony_ci#include "workarounds.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* IRQ latency can be enormous because: 2662306a36Sopenharmony_ci * - All IRQs may be disabled on a CPU for a *long* time by e.g. a 2762306a36Sopenharmony_ci * slow serial console or an old IDE driver doing error recovery 2862306a36Sopenharmony_ci * - The PREEMPT_RT patches mostly deal with this, but also allow a 2962306a36Sopenharmony_ci * tasklet or normal task to be given higher priority than our IRQ 3062306a36Sopenharmony_ci * threads 3162306a36Sopenharmony_ci * Try to avoid blaming the hardware for this. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci#define IRQ_TIMEOUT HZ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * Loopback test packet structure 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * The self-test should stress every RSS vector, and unfortunately 3962306a36Sopenharmony_ci * Falcon only performs RSS on TCP/UDP packets. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistruct ef4_loopback_payload { 4262306a36Sopenharmony_ci char pad[2]; /* Ensures ip is 4-byte aligned */ 4362306a36Sopenharmony_ci struct_group_attr(packet, __packed, 4462306a36Sopenharmony_ci struct ethhdr header; 4562306a36Sopenharmony_ci struct iphdr ip; 4662306a36Sopenharmony_ci struct udphdr udp; 4762306a36Sopenharmony_ci __be16 iteration; 4862306a36Sopenharmony_ci char msg[64]; 4962306a36Sopenharmony_ci ); 5062306a36Sopenharmony_ci} __packed __aligned(4); 5162306a36Sopenharmony_ci#define EF4_LOOPBACK_PAYLOAD_LEN \ 5262306a36Sopenharmony_ci sizeof_field(struct ef4_loopback_payload, packet) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Loopback test source MAC address */ 5562306a36Sopenharmony_cistatic const u8 payload_source[ETH_ALEN] __aligned(2) = { 5662306a36Sopenharmony_ci 0x00, 0x0f, 0x53, 0x1b, 0x1b, 0x1b, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const char payload_msg[] = 6062306a36Sopenharmony_ci "Hello world! This is an Efx loopback test in progress!"; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Interrupt mode names */ 6362306a36Sopenharmony_cistatic const unsigned int ef4_interrupt_mode_max = EF4_INT_MODE_MAX; 6462306a36Sopenharmony_cistatic const char *const ef4_interrupt_mode_names[] = { 6562306a36Sopenharmony_ci [EF4_INT_MODE_MSIX] = "MSI-X", 6662306a36Sopenharmony_ci [EF4_INT_MODE_MSI] = "MSI", 6762306a36Sopenharmony_ci [EF4_INT_MODE_LEGACY] = "legacy", 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci#define INT_MODE(efx) \ 7062306a36Sopenharmony_ci STRING_TABLE_LOOKUP(efx->interrupt_mode, ef4_interrupt_mode) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/** 7362306a36Sopenharmony_ci * struct ef4_loopback_state - persistent state during a loopback selftest 7462306a36Sopenharmony_ci * @flush: Drop all packets in ef4_loopback_rx_packet 7562306a36Sopenharmony_ci * @packet_count: Number of packets being used in this test 7662306a36Sopenharmony_ci * @skbs: An array of skbs transmitted 7762306a36Sopenharmony_ci * @offload_csum: Checksums are being offloaded 7862306a36Sopenharmony_ci * @rx_good: RX good packet count 7962306a36Sopenharmony_ci * @rx_bad: RX bad packet count 8062306a36Sopenharmony_ci * @payload: Payload used in tests 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistruct ef4_loopback_state { 8362306a36Sopenharmony_ci bool flush; 8462306a36Sopenharmony_ci int packet_count; 8562306a36Sopenharmony_ci struct sk_buff **skbs; 8662306a36Sopenharmony_ci bool offload_csum; 8762306a36Sopenharmony_ci atomic_t rx_good; 8862306a36Sopenharmony_ci atomic_t rx_bad; 8962306a36Sopenharmony_ci struct ef4_loopback_payload payload; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* How long to wait for all the packets to arrive (in ms) */ 9362306a36Sopenharmony_ci#define LOOPBACK_TIMEOUT_MS 1000 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/************************************************************************** 9662306a36Sopenharmony_ci * 9762306a36Sopenharmony_ci * MII, NVRAM and register tests 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci **************************************************************************/ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int ef4_test_phy_alive(struct ef4_nic *efx, struct ef4_self_tests *tests) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int rc = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (efx->phy_op->test_alive) { 10662306a36Sopenharmony_ci rc = efx->phy_op->test_alive(efx); 10762306a36Sopenharmony_ci tests->phy_alive = rc ? -1 : 1; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return rc; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int ef4_test_nvram(struct ef4_nic *efx, struct ef4_self_tests *tests) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int rc = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (efx->type->test_nvram) { 11862306a36Sopenharmony_ci rc = efx->type->test_nvram(efx); 11962306a36Sopenharmony_ci if (rc == -EPERM) 12062306a36Sopenharmony_ci rc = 0; 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci tests->nvram = rc ? -1 : 1; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return rc; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/************************************************************************** 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * Interrupt and event queue testing 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci **************************************************************************/ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Test generation and receipt of interrupts */ 13562306a36Sopenharmony_cistatic int ef4_test_interrupts(struct ef4_nic *efx, 13662306a36Sopenharmony_ci struct ef4_self_tests *tests) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci unsigned long timeout, wait; 13962306a36Sopenharmony_ci int cpu; 14062306a36Sopenharmony_ci int rc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, "testing interrupts\n"); 14362306a36Sopenharmony_ci tests->interrupt = -1; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rc = ef4_nic_irq_test_start(efx); 14662306a36Sopenharmony_ci if (rc == -ENOTSUPP) { 14762306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 14862306a36Sopenharmony_ci "direct interrupt testing not supported\n"); 14962306a36Sopenharmony_ci tests->interrupt = 0; 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci timeout = jiffies + IRQ_TIMEOUT; 15462306a36Sopenharmony_ci wait = 1; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Wait for arrival of test interrupt. */ 15762306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, "waiting for test interrupt\n"); 15862306a36Sopenharmony_ci do { 15962306a36Sopenharmony_ci schedule_timeout_uninterruptible(wait); 16062306a36Sopenharmony_ci cpu = ef4_nic_irq_test_irq_cpu(efx); 16162306a36Sopenharmony_ci if (cpu >= 0) 16262306a36Sopenharmony_ci goto success; 16362306a36Sopenharmony_ci wait *= 2; 16462306a36Sopenharmony_ci } while (time_before(jiffies, timeout)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, "timed out waiting for interrupt\n"); 16762306a36Sopenharmony_ci return -ETIMEDOUT; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci success: 17062306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, "%s test interrupt seen on CPU%d\n", 17162306a36Sopenharmony_ci INT_MODE(efx), cpu); 17262306a36Sopenharmony_ci tests->interrupt = 1; 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* Test generation and receipt of interrupting events */ 17762306a36Sopenharmony_cistatic int ef4_test_eventq_irq(struct ef4_nic *efx, 17862306a36Sopenharmony_ci struct ef4_self_tests *tests) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct ef4_channel *channel; 18162306a36Sopenharmony_ci unsigned int read_ptr[EF4_MAX_CHANNELS]; 18262306a36Sopenharmony_ci unsigned long napi_ran = 0, dma_pend = 0, int_pend = 0; 18362306a36Sopenharmony_ci unsigned long timeout, wait; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci BUILD_BUG_ON(EF4_MAX_CHANNELS > BITS_PER_LONG); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ef4_for_each_channel(channel, efx) { 18862306a36Sopenharmony_ci read_ptr[channel->channel] = channel->eventq_read_ptr; 18962306a36Sopenharmony_ci set_bit(channel->channel, &dma_pend); 19062306a36Sopenharmony_ci set_bit(channel->channel, &int_pend); 19162306a36Sopenharmony_ci ef4_nic_event_test_start(channel); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci timeout = jiffies + IRQ_TIMEOUT; 19562306a36Sopenharmony_ci wait = 1; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Wait for arrival of interrupts. NAPI processing may or may 19862306a36Sopenharmony_ci * not complete in time, but we can cope in any case. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci do { 20162306a36Sopenharmony_ci schedule_timeout_uninterruptible(wait); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ef4_for_each_channel(channel, efx) { 20462306a36Sopenharmony_ci ef4_stop_eventq(channel); 20562306a36Sopenharmony_ci if (channel->eventq_read_ptr != 20662306a36Sopenharmony_ci read_ptr[channel->channel]) { 20762306a36Sopenharmony_ci set_bit(channel->channel, &napi_ran); 20862306a36Sopenharmony_ci clear_bit(channel->channel, &dma_pend); 20962306a36Sopenharmony_ci clear_bit(channel->channel, &int_pend); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci if (ef4_nic_event_present(channel)) 21262306a36Sopenharmony_ci clear_bit(channel->channel, &dma_pend); 21362306a36Sopenharmony_ci if (ef4_nic_event_test_irq_cpu(channel) >= 0) 21462306a36Sopenharmony_ci clear_bit(channel->channel, &int_pend); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci ef4_start_eventq(channel); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci wait *= 2; 22062306a36Sopenharmony_ci } while ((dma_pend || int_pend) && time_before(jiffies, timeout)); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ef4_for_each_channel(channel, efx) { 22362306a36Sopenharmony_ci bool dma_seen = !test_bit(channel->channel, &dma_pend); 22462306a36Sopenharmony_ci bool int_seen = !test_bit(channel->channel, &int_pend); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci tests->eventq_dma[channel->channel] = dma_seen ? 1 : -1; 22762306a36Sopenharmony_ci tests->eventq_int[channel->channel] = int_seen ? 1 : -1; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (dma_seen && int_seen) { 23062306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 23162306a36Sopenharmony_ci "channel %d event queue passed (with%s NAPI)\n", 23262306a36Sopenharmony_ci channel->channel, 23362306a36Sopenharmony_ci test_bit(channel->channel, &napi_ran) ? 23462306a36Sopenharmony_ci "" : "out"); 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci /* Report failure and whether either interrupt or DMA 23762306a36Sopenharmony_ci * worked 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 24062306a36Sopenharmony_ci "channel %d timed out waiting for event queue\n", 24162306a36Sopenharmony_ci channel->channel); 24262306a36Sopenharmony_ci if (int_seen) 24362306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 24462306a36Sopenharmony_ci "channel %d saw interrupt " 24562306a36Sopenharmony_ci "during event queue test\n", 24662306a36Sopenharmony_ci channel->channel); 24762306a36Sopenharmony_ci if (dma_seen) 24862306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 24962306a36Sopenharmony_ci "channel %d event was generated, but " 25062306a36Sopenharmony_ci "failed to trigger an interrupt\n", 25162306a36Sopenharmony_ci channel->channel); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return (dma_pend || int_pend) ? -ETIMEDOUT : 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int ef4_test_phy(struct ef4_nic *efx, struct ef4_self_tests *tests, 25962306a36Sopenharmony_ci unsigned flags) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci int rc; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (!efx->phy_op->run_tests) 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 26762306a36Sopenharmony_ci rc = efx->phy_op->run_tests(efx, tests->phy_ext, flags); 26862306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 26962306a36Sopenharmony_ci if (rc == -EPERM) 27062306a36Sopenharmony_ci rc = 0; 27162306a36Sopenharmony_ci else 27262306a36Sopenharmony_ci netif_info(efx, drv, efx->net_dev, 27362306a36Sopenharmony_ci "%s phy selftest\n", rc ? "Failed" : "Passed"); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return rc; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/************************************************************************** 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Loopback testing 28162306a36Sopenharmony_ci * NB Only one loopback test can be executing concurrently. 28262306a36Sopenharmony_ci * 28362306a36Sopenharmony_ci **************************************************************************/ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* Loopback test RX callback 28662306a36Sopenharmony_ci * This is called for each received packet during loopback testing. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_civoid ef4_loopback_rx_packet(struct ef4_nic *efx, 28962306a36Sopenharmony_ci const char *buf_ptr, int pkt_len) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 29262306a36Sopenharmony_ci struct ef4_loopback_payload received; 29362306a36Sopenharmony_ci struct ef4_loopback_payload *payload; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci BUG_ON(!buf_ptr); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* If we are just flushing, then drop the packet */ 29862306a36Sopenharmony_ci if ((state == NULL) || state->flush) 29962306a36Sopenharmony_ci return; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci payload = &state->payload; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci memcpy(&received.packet, buf_ptr, 30462306a36Sopenharmony_ci min_t(int, pkt_len, EF4_LOOPBACK_PAYLOAD_LEN)); 30562306a36Sopenharmony_ci received.ip.saddr = payload->ip.saddr; 30662306a36Sopenharmony_ci if (state->offload_csum) 30762306a36Sopenharmony_ci received.ip.check = payload->ip.check; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Check that header exists */ 31062306a36Sopenharmony_ci if (pkt_len < sizeof(received.header)) { 31162306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 31262306a36Sopenharmony_ci "saw runt RX packet (length %d) in %s loopback " 31362306a36Sopenharmony_ci "test\n", pkt_len, LOOPBACK_MODE(efx)); 31462306a36Sopenharmony_ci goto err; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Check that the ethernet header exists */ 31862306a36Sopenharmony_ci if (memcmp(&received.header, &payload->header, ETH_HLEN) != 0) { 31962306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 32062306a36Sopenharmony_ci "saw non-loopback RX packet in %s loopback test\n", 32162306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 32262306a36Sopenharmony_ci goto err; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Check packet length */ 32662306a36Sopenharmony_ci if (pkt_len != EF4_LOOPBACK_PAYLOAD_LEN) { 32762306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 32862306a36Sopenharmony_ci "saw incorrect RX packet length %d (wanted %d) in " 32962306a36Sopenharmony_ci "%s loopback test\n", pkt_len, 33062306a36Sopenharmony_ci (int)EF4_LOOPBACK_PAYLOAD_LEN, LOOPBACK_MODE(efx)); 33162306a36Sopenharmony_ci goto err; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Check that IP header matches */ 33562306a36Sopenharmony_ci if (memcmp(&received.ip, &payload->ip, sizeof(payload->ip)) != 0) { 33662306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 33762306a36Sopenharmony_ci "saw corrupted IP header in %s loopback test\n", 33862306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 33962306a36Sopenharmony_ci goto err; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Check that msg and padding matches */ 34362306a36Sopenharmony_ci if (memcmp(&received.msg, &payload->msg, sizeof(received.msg)) != 0) { 34462306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 34562306a36Sopenharmony_ci "saw corrupted RX packet in %s loopback test\n", 34662306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 34762306a36Sopenharmony_ci goto err; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Check that iteration matches */ 35162306a36Sopenharmony_ci if (received.iteration != payload->iteration) { 35262306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 35362306a36Sopenharmony_ci "saw RX packet from iteration %d (wanted %d) in " 35462306a36Sopenharmony_ci "%s loopback test\n", ntohs(received.iteration), 35562306a36Sopenharmony_ci ntohs(payload->iteration), LOOPBACK_MODE(efx)); 35662306a36Sopenharmony_ci goto err; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Increase correct RX count */ 36062306a36Sopenharmony_ci netif_vdbg(efx, drv, efx->net_dev, 36162306a36Sopenharmony_ci "got loopback RX in %s loopback test\n", LOOPBACK_MODE(efx)); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci atomic_inc(&state->rx_good); 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci err: 36762306a36Sopenharmony_ci#ifdef DEBUG 36862306a36Sopenharmony_ci if (atomic_read(&state->rx_bad) == 0) { 36962306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, "received packet:\n"); 37062306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, 37162306a36Sopenharmony_ci buf_ptr, pkt_len, 0); 37262306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, "expected packet:\n"); 37362306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "", DUMP_PREFIX_OFFSET, 0x10, 1, 37462306a36Sopenharmony_ci &state->payload.packet, EF4_LOOPBACK_PAYLOAD_LEN, 37562306a36Sopenharmony_ci 0); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci#endif 37862306a36Sopenharmony_ci atomic_inc(&state->rx_bad); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* Initialise an ef4_selftest_state for a new iteration */ 38262306a36Sopenharmony_cistatic void ef4_iterate_state(struct ef4_nic *efx) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 38562306a36Sopenharmony_ci struct net_device *net_dev = efx->net_dev; 38662306a36Sopenharmony_ci struct ef4_loopback_payload *payload = &state->payload; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* Initialise the layerII header */ 38962306a36Sopenharmony_ci ether_addr_copy((u8 *)&payload->header.h_dest, net_dev->dev_addr); 39062306a36Sopenharmony_ci ether_addr_copy((u8 *)&payload->header.h_source, payload_source); 39162306a36Sopenharmony_ci payload->header.h_proto = htons(ETH_P_IP); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* saddr set later and used as incrementing count */ 39462306a36Sopenharmony_ci payload->ip.daddr = htonl(INADDR_LOOPBACK); 39562306a36Sopenharmony_ci payload->ip.ihl = 5; 39662306a36Sopenharmony_ci payload->ip.check = (__force __sum16) htons(0xdead); 39762306a36Sopenharmony_ci payload->ip.tot_len = htons(sizeof(*payload) - 39862306a36Sopenharmony_ci offsetof(struct ef4_loopback_payload, ip)); 39962306a36Sopenharmony_ci payload->ip.version = IPVERSION; 40062306a36Sopenharmony_ci payload->ip.protocol = IPPROTO_UDP; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Initialise udp header */ 40362306a36Sopenharmony_ci payload->udp.source = 0; 40462306a36Sopenharmony_ci payload->udp.len = htons(sizeof(*payload) - 40562306a36Sopenharmony_ci offsetof(struct ef4_loopback_payload, udp)); 40662306a36Sopenharmony_ci payload->udp.check = 0; /* checksum ignored */ 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Fill out payload */ 40962306a36Sopenharmony_ci payload->iteration = htons(ntohs(payload->iteration) + 1); 41062306a36Sopenharmony_ci memcpy(&payload->msg, payload_msg, sizeof(payload_msg)); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Fill out remaining state members */ 41362306a36Sopenharmony_ci atomic_set(&state->rx_good, 0); 41462306a36Sopenharmony_ci atomic_set(&state->rx_bad, 0); 41562306a36Sopenharmony_ci smp_wmb(); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int ef4_begin_loopback(struct ef4_tx_queue *tx_queue) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct ef4_nic *efx = tx_queue->efx; 42162306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 42262306a36Sopenharmony_ci struct ef4_loopback_payload *payload; 42362306a36Sopenharmony_ci struct sk_buff *skb; 42462306a36Sopenharmony_ci int i; 42562306a36Sopenharmony_ci netdev_tx_t rc; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Transmit N copies of buffer */ 42862306a36Sopenharmony_ci for (i = 0; i < state->packet_count; i++) { 42962306a36Sopenharmony_ci /* Allocate an skb, holding an extra reference for 43062306a36Sopenharmony_ci * transmit completion counting */ 43162306a36Sopenharmony_ci skb = alloc_skb(sizeof(state->payload), GFP_KERNEL); 43262306a36Sopenharmony_ci if (!skb) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci state->skbs[i] = skb; 43562306a36Sopenharmony_ci skb_get(skb); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Copy the payload in, incrementing the source address to 43862306a36Sopenharmony_ci * exercise the rss vectors */ 43962306a36Sopenharmony_ci payload = skb_put(skb, sizeof(state->payload)); 44062306a36Sopenharmony_ci memcpy(payload, &state->payload, sizeof(state->payload)); 44162306a36Sopenharmony_ci payload->ip.saddr = htonl(INADDR_LOOPBACK | (i << 2)); 44262306a36Sopenharmony_ci /* Strip off the leading padding */ 44362306a36Sopenharmony_ci skb_pull(skb, offsetof(struct ef4_loopback_payload, header)); 44462306a36Sopenharmony_ci /* Strip off the trailing padding */ 44562306a36Sopenharmony_ci skb_trim(skb, EF4_LOOPBACK_PAYLOAD_LEN); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Ensure everything we've written is visible to the 44862306a36Sopenharmony_ci * interrupt handler. */ 44962306a36Sopenharmony_ci smp_wmb(); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci netif_tx_lock_bh(efx->net_dev); 45262306a36Sopenharmony_ci rc = ef4_enqueue_skb(tx_queue, skb); 45362306a36Sopenharmony_ci netif_tx_unlock_bh(efx->net_dev); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (rc != NETDEV_TX_OK) { 45662306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 45762306a36Sopenharmony_ci "TX queue %d could not transmit packet %d of " 45862306a36Sopenharmony_ci "%d in %s loopback test\n", tx_queue->queue, 45962306a36Sopenharmony_ci i + 1, state->packet_count, 46062306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Defer cleaning up the other skbs for the caller */ 46362306a36Sopenharmony_ci kfree_skb(skb); 46462306a36Sopenharmony_ci return -EPIPE; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return 0; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int ef4_poll_loopback(struct ef4_nic *efx) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return atomic_read(&state->rx_good) == state->packet_count; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int ef4_end_loopback(struct ef4_tx_queue *tx_queue, 47962306a36Sopenharmony_ci struct ef4_loopback_self_tests *lb_tests) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct ef4_nic *efx = tx_queue->efx; 48262306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 48362306a36Sopenharmony_ci struct sk_buff *skb; 48462306a36Sopenharmony_ci int tx_done = 0, rx_good, rx_bad; 48562306a36Sopenharmony_ci int i, rc = 0; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci netif_tx_lock_bh(efx->net_dev); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Count the number of tx completions, and decrement the refcnt. Any 49062306a36Sopenharmony_ci * skbs not already completed will be free'd when the queue is flushed */ 49162306a36Sopenharmony_ci for (i = 0; i < state->packet_count; i++) { 49262306a36Sopenharmony_ci skb = state->skbs[i]; 49362306a36Sopenharmony_ci if (skb && !skb_shared(skb)) 49462306a36Sopenharmony_ci ++tx_done; 49562306a36Sopenharmony_ci dev_kfree_skb(skb); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci netif_tx_unlock_bh(efx->net_dev); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Check TX completion and received packet counts */ 50162306a36Sopenharmony_ci rx_good = atomic_read(&state->rx_good); 50262306a36Sopenharmony_ci rx_bad = atomic_read(&state->rx_bad); 50362306a36Sopenharmony_ci if (tx_done != state->packet_count) { 50462306a36Sopenharmony_ci /* Don't free the skbs; they will be picked up on TX 50562306a36Sopenharmony_ci * overflow or channel teardown. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 50862306a36Sopenharmony_ci "TX queue %d saw only %d out of an expected %d " 50962306a36Sopenharmony_ci "TX completion events in %s loopback test\n", 51062306a36Sopenharmony_ci tx_queue->queue, tx_done, state->packet_count, 51162306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 51262306a36Sopenharmony_ci rc = -ETIMEDOUT; 51362306a36Sopenharmony_ci /* Allow to fall through so we see the RX errors as well */ 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* We may always be up to a flush away from our desired packet total */ 51762306a36Sopenharmony_ci if (rx_good != state->packet_count) { 51862306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 51962306a36Sopenharmony_ci "TX queue %d saw only %d out of an expected %d " 52062306a36Sopenharmony_ci "received packets in %s loopback test\n", 52162306a36Sopenharmony_ci tx_queue->queue, rx_good, state->packet_count, 52262306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 52362306a36Sopenharmony_ci rc = -ETIMEDOUT; 52462306a36Sopenharmony_ci /* Fall through */ 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Update loopback test structure */ 52862306a36Sopenharmony_ci lb_tests->tx_sent[tx_queue->queue] += state->packet_count; 52962306a36Sopenharmony_ci lb_tests->tx_done[tx_queue->queue] += tx_done; 53062306a36Sopenharmony_ci lb_tests->rx_good += rx_good; 53162306a36Sopenharmony_ci lb_tests->rx_bad += rx_bad; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return rc; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int 53762306a36Sopenharmony_cief4_test_loopback(struct ef4_tx_queue *tx_queue, 53862306a36Sopenharmony_ci struct ef4_loopback_self_tests *lb_tests) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct ef4_nic *efx = tx_queue->efx; 54162306a36Sopenharmony_ci struct ef4_loopback_state *state = efx->loopback_selftest; 54262306a36Sopenharmony_ci int i, begin_rc, end_rc; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 54562306a36Sopenharmony_ci /* Determine how many packets to send */ 54662306a36Sopenharmony_ci state->packet_count = efx->txq_entries / 3; 54762306a36Sopenharmony_ci state->packet_count = min(1 << (i << 2), state->packet_count); 54862306a36Sopenharmony_ci state->skbs = kcalloc(state->packet_count, 54962306a36Sopenharmony_ci sizeof(state->skbs[0]), GFP_KERNEL); 55062306a36Sopenharmony_ci if (!state->skbs) 55162306a36Sopenharmony_ci return -ENOMEM; 55262306a36Sopenharmony_ci state->flush = false; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 55562306a36Sopenharmony_ci "TX queue %d testing %s loopback with %d packets\n", 55662306a36Sopenharmony_ci tx_queue->queue, LOOPBACK_MODE(efx), 55762306a36Sopenharmony_ci state->packet_count); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci ef4_iterate_state(efx); 56062306a36Sopenharmony_ci begin_rc = ef4_begin_loopback(tx_queue); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* This will normally complete very quickly, but be 56362306a36Sopenharmony_ci * prepared to wait much longer. */ 56462306a36Sopenharmony_ci msleep(1); 56562306a36Sopenharmony_ci if (!ef4_poll_loopback(efx)) { 56662306a36Sopenharmony_ci msleep(LOOPBACK_TIMEOUT_MS); 56762306a36Sopenharmony_ci ef4_poll_loopback(efx); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci end_rc = ef4_end_loopback(tx_queue, lb_tests); 57162306a36Sopenharmony_ci kfree(state->skbs); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (begin_rc || end_rc) { 57462306a36Sopenharmony_ci /* Wait a while to ensure there are no packets 57562306a36Sopenharmony_ci * floating around after a failure. */ 57662306a36Sopenharmony_ci schedule_timeout_uninterruptible(HZ / 10); 57762306a36Sopenharmony_ci return begin_rc ? begin_rc : end_rc; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 58262306a36Sopenharmony_ci "TX queue %d passed %s loopback test with a burst length " 58362306a36Sopenharmony_ci "of %d packets\n", tx_queue->queue, LOOPBACK_MODE(efx), 58462306a36Sopenharmony_ci state->packet_count); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return 0; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci/* Wait for link up. On Falcon, we would prefer to rely on ef4_monitor, but 59062306a36Sopenharmony_ci * any contention on the mac lock (via e.g. ef4_mac_mcast_work) causes it 59162306a36Sopenharmony_ci * to delay and retry. Therefore, it's safer to just poll directly. Wait 59262306a36Sopenharmony_ci * for link up and any faults to dissipate. */ 59362306a36Sopenharmony_cistatic int ef4_wait_for_link(struct ef4_nic *efx) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci struct ef4_link_state *link_state = &efx->link_state; 59662306a36Sopenharmony_ci int count, link_up_count = 0; 59762306a36Sopenharmony_ci bool link_up; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci for (count = 0; count < 40; count++) { 60062306a36Sopenharmony_ci schedule_timeout_uninterruptible(HZ / 10); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (efx->type->monitor != NULL) { 60362306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 60462306a36Sopenharmony_ci efx->type->monitor(efx); 60562306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 60962306a36Sopenharmony_ci link_up = link_state->up; 61062306a36Sopenharmony_ci if (link_up) 61162306a36Sopenharmony_ci link_up = !efx->type->check_mac_fault(efx); 61262306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (link_up) { 61562306a36Sopenharmony_ci if (++link_up_count == 2) 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci } else { 61862306a36Sopenharmony_ci link_up_count = 0; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return -ETIMEDOUT; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic int ef4_test_loopbacks(struct ef4_nic *efx, struct ef4_self_tests *tests, 62662306a36Sopenharmony_ci unsigned int loopback_modes) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci enum ef4_loopback_mode mode; 62962306a36Sopenharmony_ci struct ef4_loopback_state *state; 63062306a36Sopenharmony_ci struct ef4_channel *channel = 63162306a36Sopenharmony_ci ef4_get_channel(efx, efx->tx_channel_offset); 63262306a36Sopenharmony_ci struct ef4_tx_queue *tx_queue; 63362306a36Sopenharmony_ci int rc = 0; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Set the port loopback_selftest member. From this point on 63662306a36Sopenharmony_ci * all received packets will be dropped. Mark the state as 63762306a36Sopenharmony_ci * "flushing" so all inflight packets are dropped */ 63862306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 63962306a36Sopenharmony_ci if (state == NULL) 64062306a36Sopenharmony_ci return -ENOMEM; 64162306a36Sopenharmony_ci BUG_ON(efx->loopback_selftest); 64262306a36Sopenharmony_ci state->flush = true; 64362306a36Sopenharmony_ci efx->loopback_selftest = state; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Test all supported loopback modes */ 64662306a36Sopenharmony_ci for (mode = LOOPBACK_NONE; mode <= LOOPBACK_TEST_MAX; mode++) { 64762306a36Sopenharmony_ci if (!(loopback_modes & (1 << mode))) 64862306a36Sopenharmony_ci continue; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Move the port into the specified loopback mode. */ 65162306a36Sopenharmony_ci state->flush = true; 65262306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 65362306a36Sopenharmony_ci efx->loopback_mode = mode; 65462306a36Sopenharmony_ci rc = __ef4_reconfigure_port(efx); 65562306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 65662306a36Sopenharmony_ci if (rc) { 65762306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 65862306a36Sopenharmony_ci "unable to move into %s loopback\n", 65962306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 66062306a36Sopenharmony_ci goto out; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci rc = ef4_wait_for_link(efx); 66462306a36Sopenharmony_ci if (rc) { 66562306a36Sopenharmony_ci netif_err(efx, drv, efx->net_dev, 66662306a36Sopenharmony_ci "loopback %s never came up\n", 66762306a36Sopenharmony_ci LOOPBACK_MODE(efx)); 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Test all enabled types of TX queue */ 67262306a36Sopenharmony_ci ef4_for_each_channel_tx_queue(tx_queue, channel) { 67362306a36Sopenharmony_ci state->offload_csum = (tx_queue->queue & 67462306a36Sopenharmony_ci EF4_TXQ_TYPE_OFFLOAD); 67562306a36Sopenharmony_ci rc = ef4_test_loopback(tx_queue, 67662306a36Sopenharmony_ci &tests->loopback[mode]); 67762306a36Sopenharmony_ci if (rc) 67862306a36Sopenharmony_ci goto out; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci out: 68362306a36Sopenharmony_ci /* Remove the flush. The caller will remove the loopback setting */ 68462306a36Sopenharmony_ci state->flush = true; 68562306a36Sopenharmony_ci efx->loopback_selftest = NULL; 68662306a36Sopenharmony_ci wmb(); 68762306a36Sopenharmony_ci kfree(state); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (rc == -EPERM) 69062306a36Sopenharmony_ci rc = 0; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return rc; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/************************************************************************** 69662306a36Sopenharmony_ci * 69762306a36Sopenharmony_ci * Entry point 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci *************************************************************************/ 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ciint ef4_selftest(struct ef4_nic *efx, struct ef4_self_tests *tests, 70262306a36Sopenharmony_ci unsigned flags) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci enum ef4_loopback_mode loopback_mode = efx->loopback_mode; 70562306a36Sopenharmony_ci int phy_mode = efx->phy_mode; 70662306a36Sopenharmony_ci int rc_test = 0, rc_reset, rc; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci ef4_selftest_async_cancel(efx); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Online (i.e. non-disruptive) testing 71162306a36Sopenharmony_ci * This checks interrupt generation, event delivery and PHY presence. */ 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci rc = ef4_test_phy_alive(efx, tests); 71462306a36Sopenharmony_ci if (rc && !rc_test) 71562306a36Sopenharmony_ci rc_test = rc; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci rc = ef4_test_nvram(efx, tests); 71862306a36Sopenharmony_ci if (rc && !rc_test) 71962306a36Sopenharmony_ci rc_test = rc; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci rc = ef4_test_interrupts(efx, tests); 72262306a36Sopenharmony_ci if (rc && !rc_test) 72362306a36Sopenharmony_ci rc_test = rc; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci rc = ef4_test_eventq_irq(efx, tests); 72662306a36Sopenharmony_ci if (rc && !rc_test) 72762306a36Sopenharmony_ci rc_test = rc; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (rc_test) 73062306a36Sopenharmony_ci return rc_test; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (!(flags & ETH_TEST_FL_OFFLINE)) 73362306a36Sopenharmony_ci return ef4_test_phy(efx, tests, flags); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Offline (i.e. disruptive) testing 73662306a36Sopenharmony_ci * This checks MAC and PHY loopback on the specified port. */ 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Detach the device so the kernel doesn't transmit during the 73962306a36Sopenharmony_ci * loopback test and the watchdog timeout doesn't fire. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci ef4_device_detach_sync(efx); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (efx->type->test_chip) { 74462306a36Sopenharmony_ci rc_reset = efx->type->test_chip(efx, tests); 74562306a36Sopenharmony_ci if (rc_reset) { 74662306a36Sopenharmony_ci netif_err(efx, hw, efx->net_dev, 74762306a36Sopenharmony_ci "Unable to recover from chip test\n"); 74862306a36Sopenharmony_ci ef4_schedule_reset(efx, RESET_TYPE_DISABLE); 74962306a36Sopenharmony_ci return rc_reset; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if ((tests->memory < 0 || tests->registers < 0) && !rc_test) 75362306a36Sopenharmony_ci rc_test = -EIO; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Ensure that the phy is powered and out of loopback 75762306a36Sopenharmony_ci * for the bist and loopback tests */ 75862306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 75962306a36Sopenharmony_ci efx->phy_mode &= ~PHY_MODE_LOW_POWER; 76062306a36Sopenharmony_ci efx->loopback_mode = LOOPBACK_NONE; 76162306a36Sopenharmony_ci __ef4_reconfigure_port(efx); 76262306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci rc = ef4_test_phy(efx, tests, flags); 76562306a36Sopenharmony_ci if (rc && !rc_test) 76662306a36Sopenharmony_ci rc_test = rc; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci rc = ef4_test_loopbacks(efx, tests, efx->loopback_modes); 76962306a36Sopenharmony_ci if (rc && !rc_test) 77062306a36Sopenharmony_ci rc_test = rc; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* restore the PHY to the previous state */ 77362306a36Sopenharmony_ci mutex_lock(&efx->mac_lock); 77462306a36Sopenharmony_ci efx->phy_mode = phy_mode; 77562306a36Sopenharmony_ci efx->loopback_mode = loopback_mode; 77662306a36Sopenharmony_ci __ef4_reconfigure_port(efx); 77762306a36Sopenharmony_ci mutex_unlock(&efx->mac_lock); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci netif_device_attach(efx->net_dev); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci return rc_test; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_civoid ef4_selftest_async_start(struct ef4_nic *efx) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct ef4_channel *channel; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ef4_for_each_channel(channel, efx) 78962306a36Sopenharmony_ci ef4_nic_event_test_start(channel); 79062306a36Sopenharmony_ci schedule_delayed_work(&efx->selftest_work, IRQ_TIMEOUT); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_civoid ef4_selftest_async_cancel(struct ef4_nic *efx) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci cancel_delayed_work_sync(&efx->selftest_work); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_civoid ef4_selftest_async_work(struct work_struct *data) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct ef4_nic *efx = container_of(data, struct ef4_nic, 80162306a36Sopenharmony_ci selftest_work.work); 80262306a36Sopenharmony_ci struct ef4_channel *channel; 80362306a36Sopenharmony_ci int cpu; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci ef4_for_each_channel(channel, efx) { 80662306a36Sopenharmony_ci cpu = ef4_nic_event_test_irq_cpu(channel); 80762306a36Sopenharmony_ci if (cpu < 0) 80862306a36Sopenharmony_ci netif_err(efx, ifup, efx->net_dev, 80962306a36Sopenharmony_ci "channel %d failed to trigger an interrupt\n", 81062306a36Sopenharmony_ci channel->channel); 81162306a36Sopenharmony_ci else 81262306a36Sopenharmony_ci netif_dbg(efx, ifup, efx->net_dev, 81362306a36Sopenharmony_ci "channel %d triggered interrupt on CPU %d\n", 81462306a36Sopenharmony_ci channel->channel, cpu); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci} 817