2008-05-07 16:36:19 +04:00
/****************************************************************************
* Driver for Solarflare Solarstorm network controllers and boards
* Copyright 2005 - 2006 Fen Systems Ltd .
* Copyright 2006 - 2008 Solarflare Communications Inc .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation , incorporated herein by reference .
*/
# include <linux/netdevice.h>
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/kernel_stat.h>
# include <linux/pci.h>
# include <linux/ethtool.h>
# include <linux/ip.h>
# include <linux/in.h>
# include <linux/udp.h>
# include <linux/rtnetlink.h>
# include <asm/io.h>
# include "net_driver.h"
# include "efx.h"
# include "falcon.h"
# include "selftest.h"
# include "workarounds.h"
2008-09-01 15:49:02 +04:00
# include "spi.h"
2009-10-23 12:30:46 +04:00
# include "io.h"
2008-09-01 15:49:02 +04:00
# include "mdio_10g.h"
2008-05-07 16:36:19 +04:00
/*
* Loopback test packet structure
*
* The self - test should stress every RSS vector , and unfortunately
* Falcon only performs RSS on TCP / UDP packets .
*/
struct efx_loopback_payload {
struct ethhdr header ;
struct iphdr ip ;
struct udphdr udp ;
__be16 iteration ;
const char msg [ 64 ] ;
} __attribute__ ( ( packed ) ) ;
/* Loopback test source MAC address */
static const unsigned char payload_source [ ETH_ALEN ] = {
0x00 , 0x0f , 0x53 , 0x1b , 0x1b , 0x1b ,
} ;
static const char * payload_msg =
" Hello world! This is an Efx loopback test in progress! " ;
/**
2008-09-01 15:49:02 +04:00
* efx_loopback_state - persistent state during a loopback selftest
2008-05-07 16:36:19 +04:00
* @ flush : Drop all packets in efx_loopback_rx_packet
* @ packet_count : Number of packets being used in this test
* @ skbs : An array of skbs transmitted
2009-11-25 19:12:24 +03:00
* @ offload_csum : Checksums are being offloaded
2008-05-07 16:36:19 +04:00
* @ rx_good : RX good packet count
* @ rx_bad : RX bad packet count
* @ payload : Payload used in tests
*/
2008-09-01 15:49:02 +04:00
struct efx_loopback_state {
2008-09-01 15:46:50 +04:00
bool flush ;
2008-05-07 16:36:19 +04:00
int packet_count ;
struct sk_buff * * skbs ;
2008-09-01 15:46:50 +04:00
bool offload_csum ;
2008-05-07 16:36:19 +04:00
atomic_t rx_good ;
atomic_t rx_bad ;
struct efx_loopback_payload payload ;
} ;
/**************************************************************************
*
2008-09-01 15:49:02 +04:00
* MII , NVRAM and register tests
2008-05-07 16:36:19 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-04-29 12:05:08 +04:00
static int efx_test_mdio ( struct efx_nic * efx , struct efx_self_tests * tests )
2008-09-01 15:49:02 +04:00
{
int rc = 0 ;
2009-04-29 12:05:08 +04:00
int devad = __ffs ( efx - > mdio . mmds ) ;
2008-09-01 15:49:02 +04:00
u16 physid1 , physid2 ;
if ( efx - > phy_type = = PHY_TYPE_NONE )
return 0 ;
mutex_lock ( & efx - > mac_lock ) ;
2009-04-29 12:05:08 +04:00
tests - > mdio = - 1 ;
2008-09-01 15:49:02 +04:00
2009-04-29 12:05:08 +04:00
physid1 = efx_mdio_read ( efx , devad , MDIO_DEVID1 ) ;
physid2 = efx_mdio_read ( efx , devad , MDIO_DEVID2 ) ;
2008-09-01 15:49:02 +04:00
if ( ( physid1 = = 0x0000 ) | | ( physid1 = = 0xffff ) | |
( physid2 = = 0x0000 ) | | ( physid2 = = 0xffff ) ) {
2009-04-29 12:05:08 +04:00
EFX_ERR ( efx , " no MDIO PHY present with ID %d \n " ,
efx - > mdio . prtad ) ;
2008-09-01 15:49:02 +04:00
rc = - EINVAL ;
goto out ;
}
2008-12-13 08:50:08 +03:00
if ( EFX_IS10G ( efx ) ) {
2009-04-29 12:05:08 +04:00
rc = efx_mdio_check_mmds ( efx , efx - > phy_op - > mmds , 0 ) ;
2008-12-13 08:50:08 +03:00
if ( rc )
goto out ;
}
2008-09-01 15:49:02 +04:00
out :
mutex_unlock ( & efx - > mac_lock ) ;
2009-04-29 12:05:08 +04:00
tests - > mdio = rc ? - 1 : 1 ;
2008-09-01 15:49:02 +04:00
return rc ;
}
static int efx_test_nvram ( struct efx_nic * efx , struct efx_self_tests * tests )
{
2009-11-29 06:43:33 +03:00
int rc = 0 ;
if ( efx - > type - > test_nvram ) {
rc = efx - > type - > test_nvram ( efx ) ;
tests - > nvram = rc ? - 1 : 1 ;
}
2008-09-01 15:49:02 +04:00
return rc ;
}
static int efx_test_chip ( struct efx_nic * efx , struct efx_self_tests * tests )
{
2009-11-29 06:43:23 +03:00
int rc = 0 ;
2008-09-01 15:49:02 +04:00
2009-11-29 06:43:23 +03:00
/* Test register access */
if ( efx - > type - > test_registers ) {
rc = efx - > type - > test_registers ( efx ) ;
tests - > registers = rc ? - 1 : 1 ;
}
2008-09-01 15:49:02 +04:00
return rc ;
}
2008-05-07 16:36:19 +04:00
/**************************************************************************
*
* Interrupt and event queue testing
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Test generation and receipt of interrupts */
static int efx_test_interrupts ( struct efx_nic * efx ,
struct efx_self_tests * tests )
{
struct efx_channel * channel ;
EFX_LOG ( efx , " testing interrupts \n " ) ;
tests - > interrupt = - 1 ;
/* Reset interrupt flag */
efx - > last_irq_cpu = - 1 ;
smp_wmb ( ) ;
/* ACK each interrupting event queue. Receiving an interrupt due to
* traffic before a test event is raised is considered a pass */
2008-09-01 15:47:38 +04:00
efx_for_each_channel ( channel , efx ) {
2008-05-07 16:36:19 +04:00
if ( channel - > work_pending )
efx_process_channel_now ( channel ) ;
if ( efx - > last_irq_cpu > = 0 )
goto success ;
}
falcon_generate_interrupt ( efx ) ;
/* Wait for arrival of test interrupt. */
EFX_LOG ( efx , " waiting for test interrupt \n " ) ;
schedule_timeout_uninterruptible ( HZ / 10 ) ;
if ( efx - > last_irq_cpu > = 0 )
goto success ;
EFX_ERR ( efx , " timed out waiting for interrupt \n " ) ;
return - ETIMEDOUT ;
success :
2009-11-23 19:08:17 +03:00
EFX_LOG ( efx , " %s test interrupt seen on CPU%d \n " , INT_MODE ( efx ) ,
efx - > last_irq_cpu ) ;
2008-05-07 16:36:19 +04:00
tests - > interrupt = 1 ;
return 0 ;
}
/* Test generation and receipt of interrupting events */
static int efx_test_eventq_irq ( struct efx_channel * channel ,
struct efx_self_tests * tests )
{
unsigned int magic , count ;
/* Channel specific code, limited to 20 bits */
magic = ( 0x00010150 + channel - > channel ) ;
EFX_LOG ( channel - > efx , " channel %d testing event queue with code %x \n " ,
channel - > channel , magic ) ;
tests - > eventq_dma [ channel - > channel ] = - 1 ;
tests - > eventq_int [ channel - > channel ] = - 1 ;
tests - > eventq_poll [ channel - > channel ] = - 1 ;
/* Reset flag and zero magic word */
channel - > efx - > last_irq_cpu = - 1 ;
channel - > eventq_magic = 0 ;
smp_wmb ( ) ;
falcon_generate_test_event ( channel , magic ) ;
/* Wait for arrival of interrupt */
count = 0 ;
do {
schedule_timeout_uninterruptible ( HZ / 100 ) ;
if ( channel - > work_pending )
efx_process_channel_now ( channel ) ;
if ( channel - > eventq_magic = = magic )
goto eventq_ok ;
} while ( + + count < 2 ) ;
EFX_ERR ( channel - > efx , " channel %d timed out waiting for event queue \n " ,
channel - > channel ) ;
/* See if interrupt arrived */
if ( channel - > efx - > last_irq_cpu > = 0 ) {
EFX_ERR ( channel - > efx , " channel %d saw interrupt on CPU%d "
" during event queue test \n " , channel - > channel ,
raw_smp_processor_id ( ) ) ;
tests - > eventq_int [ channel - > channel ] = 1 ;
}
/* Check to see if event was received even if interrupt wasn't */
efx_process_channel_now ( channel ) ;
if ( channel - > eventq_magic = = magic ) {
EFX_ERR ( channel - > efx , " channel %d event was generated, but "
" failed to trigger an interrupt \n " , channel - > channel ) ;
tests - > eventq_dma [ channel - > channel ] = 1 ;
}
return - ETIMEDOUT ;
eventq_ok :
EFX_LOG ( channel - > efx , " channel %d event queue passed \n " ,
channel - > channel ) ;
tests - > eventq_dma [ channel - > channel ] = 1 ;
tests - > eventq_int [ channel - > channel ] = 1 ;
tests - > eventq_poll [ channel - > channel ] = 1 ;
return 0 ;
}
2008-12-27 00:47:25 +03:00
static int efx_test_phy ( struct efx_nic * efx , struct efx_self_tests * tests ,
unsigned flags )
2008-05-07 16:36:19 +04:00
{
2008-09-01 15:49:02 +04:00
int rc ;
2008-05-07 16:36:19 +04:00
2008-12-27 00:47:25 +03:00
if ( ! efx - > phy_op - > run_tests )
2008-05-07 16:36:19 +04:00
return 0 ;
2008-12-27 00:47:25 +03:00
EFX_BUG_ON_PARANOID ( efx - > phy_op - > num_tests = = 0 | |
efx - > phy_op - > num_tests > EFX_MAX_PHY_TESTS ) ;
2008-09-01 15:49:02 +04:00
mutex_lock ( & efx - > mac_lock ) ;
2008-12-27 00:47:25 +03:00
rc = efx - > phy_op - > run_tests ( efx , tests - > phy , flags ) ;
2008-09-01 15:49:02 +04:00
mutex_unlock ( & efx - > mac_lock ) ;
return rc ;
2008-05-07 16:36:19 +04:00
}
/**************************************************************************
*
* Loopback testing
* NB Only one loopback test can be executing concurrently .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Loopback test RX callback
* This is called for each received packet during loopback testing .
*/
void efx_loopback_rx_packet ( struct efx_nic * efx ,
const char * buf_ptr , int pkt_len )
{
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-05-07 16:36:19 +04:00
struct efx_loopback_payload * received ;
struct efx_loopback_payload * payload ;
BUG_ON ( ! buf_ptr ) ;
/* If we are just flushing, then drop the packet */
if ( ( state = = NULL ) | | state - > flush )
return ;
payload = & state - > payload ;
2008-09-01 15:49:02 +04:00
2008-05-17 00:20:00 +04:00
received = ( struct efx_loopback_payload * ) buf_ptr ;
2008-05-07 16:36:19 +04:00
received - > ip . saddr = payload - > ip . saddr ;
2008-09-01 15:44:59 +04:00
if ( state - > offload_csum )
received - > ip . check = payload - > ip . check ;
2008-05-07 16:36:19 +04:00
/* Check that header exists */
if ( pkt_len < sizeof ( received - > header ) ) {
EFX_ERR ( efx , " saw runt RX packet (length %d) in %s loopback "
" test \n " , pkt_len , LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Check that the ethernet header exists */
if ( memcmp ( & received - > header , & payload - > header , ETH_HLEN ) ! = 0 ) {
EFX_ERR ( efx , " saw non-loopback RX packet in %s loopback test \n " ,
LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Check packet length */
if ( pkt_len ! = sizeof ( * payload ) ) {
EFX_ERR ( efx , " saw incorrect RX packet length %d (wanted %d) in "
" %s loopback test \n " , pkt_len , ( int ) sizeof ( * payload ) ,
LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Check that IP header matches */
if ( memcmp ( & received - > ip , & payload - > ip , sizeof ( payload - > ip ) ) ! = 0 ) {
EFX_ERR ( efx , " saw corrupted IP header in %s loopback test \n " ,
LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Check that msg and padding matches */
if ( memcmp ( & received - > msg , & payload - > msg , sizeof ( received - > msg ) ) ! = 0 ) {
EFX_ERR ( efx , " saw corrupted RX packet in %s loopback test \n " ,
LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Check that iteration matches */
if ( received - > iteration ! = payload - > iteration ) {
EFX_ERR ( efx , " saw RX packet from iteration %d (wanted %d) in "
" %s loopback test \n " , ntohs ( received - > iteration ) ,
ntohs ( payload - > iteration ) , LOOPBACK_MODE ( efx ) ) ;
goto err ;
}
/* Increase correct RX count */
EFX_TRACE ( efx , " got loopback RX in %s loopback test \n " ,
LOOPBACK_MODE ( efx ) ) ;
atomic_inc ( & state - > rx_good ) ;
return ;
err :
# ifdef EFX_ENABLE_DEBUG
if ( atomic_read ( & state - > rx_bad ) = = 0 ) {
EFX_ERR ( efx , " received packet: \n " ) ;
print_hex_dump ( KERN_ERR , " " , DUMP_PREFIX_OFFSET , 0x10 , 1 ,
buf_ptr , pkt_len , 0 ) ;
EFX_ERR ( efx , " expected packet: \n " ) ;
print_hex_dump ( KERN_ERR , " " , DUMP_PREFIX_OFFSET , 0x10 , 1 ,
& state - > payload , sizeof ( state - > payload ) , 0 ) ;
}
# endif
atomic_inc ( & state - > rx_bad ) ;
}
/* Initialise an efx_selftest_state for a new iteration */
static void efx_iterate_state ( struct efx_nic * efx )
{
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-05-07 16:36:19 +04:00
struct net_device * net_dev = efx - > net_dev ;
struct efx_loopback_payload * payload = & state - > payload ;
/* Initialise the layerII header */
memcpy ( & payload - > header . h_dest , net_dev - > dev_addr , ETH_ALEN ) ;
memcpy ( & payload - > header . h_source , & payload_source , ETH_ALEN ) ;
payload - > header . h_proto = htons ( ETH_P_IP ) ;
/* saddr set later and used as incrementing count */
payload - > ip . daddr = htonl ( INADDR_LOOPBACK ) ;
payload - > ip . ihl = 5 ;
payload - > ip . check = htons ( 0xdead ) ;
payload - > ip . tot_len = htons ( sizeof ( * payload ) - sizeof ( struct ethhdr ) ) ;
payload - > ip . version = IPVERSION ;
payload - > ip . protocol = IPPROTO_UDP ;
/* Initialise udp header */
payload - > udp . source = 0 ;
payload - > udp . len = htons ( sizeof ( * payload ) - sizeof ( struct ethhdr ) -
sizeof ( struct iphdr ) ) ;
payload - > udp . check = 0 ; /* checksum ignored */
/* Fill out payload */
payload - > iteration = htons ( ntohs ( payload - > iteration ) + 1 ) ;
memcpy ( & payload - > msg , payload_msg , sizeof ( payload_msg ) ) ;
/* Fill out remaining state members */
atomic_set ( & state - > rx_good , 0 ) ;
atomic_set ( & state - > rx_bad , 0 ) ;
smp_wmb ( ) ;
}
2008-09-01 15:46:33 +04:00
static int efx_begin_loopback ( struct efx_tx_queue * tx_queue )
2008-05-07 16:36:19 +04:00
{
struct efx_nic * efx = tx_queue - > efx ;
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-05-07 16:36:19 +04:00
struct efx_loopback_payload * payload ;
struct sk_buff * skb ;
2009-08-31 23:50:58 +04:00
int i ;
netdev_tx_t rc ;
2008-05-07 16:36:19 +04:00
/* Transmit N copies of buffer */
for ( i = 0 ; i < state - > packet_count ; i + + ) {
2008-09-01 15:49:02 +04:00
/* Allocate an skb, holding an extra reference for
2008-05-07 16:36:19 +04:00
* transmit completion counting */
skb = alloc_skb ( sizeof ( state - > payload ) , GFP_KERNEL ) ;
if ( ! skb )
return - ENOMEM ;
state - > skbs [ i ] = skb ;
skb_get ( skb ) ;
/* Copy the payload in, incrementing the source address to
* exercise the rss vectors */
payload = ( ( struct efx_loopback_payload * )
skb_put ( skb , sizeof ( state - > payload ) ) ) ;
memcpy ( payload , & state - > payload , sizeof ( state - > payload ) ) ;
payload - > ip . saddr = htonl ( INADDR_LOOPBACK | ( i < < 2 ) ) ;
/* Ensure everything we've written is visible to the
* interrupt handler . */
smp_wmb ( ) ;
2008-05-17 00:16:10 +04:00
if ( efx_dev_registered ( efx ) )
2008-05-07 16:36:19 +04:00
netif_tx_lock_bh ( efx - > net_dev ) ;
2009-11-23 19:07:05 +03:00
rc = efx_enqueue_skb ( tx_queue , skb ) ;
2008-05-17 00:16:10 +04:00
if ( efx_dev_registered ( efx ) )
2008-05-07 16:36:19 +04:00
netif_tx_unlock_bh ( efx - > net_dev ) ;
if ( rc ! = NETDEV_TX_OK ) {
EFX_ERR ( efx , " TX queue %d could not transmit packet %d "
" of %d in %s loopback test \n " , tx_queue - > queue ,
i + 1 , state - > packet_count , LOOPBACK_MODE ( efx ) ) ;
/* Defer cleaning up the other skbs for the caller */
kfree_skb ( skb ) ;
return - EPIPE ;
}
}
return 0 ;
}
2008-09-01 15:46:33 +04:00
static int efx_poll_loopback ( struct efx_nic * efx )
{
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-09-01 15:46:33 +04:00
struct efx_channel * channel ;
/* NAPI polling is not enabled, so process channels
* synchronously */
2008-09-01 15:47:38 +04:00
efx_for_each_channel ( channel , efx ) {
2008-09-01 15:46:33 +04:00
if ( channel - > work_pending )
efx_process_channel_now ( channel ) ;
}
return atomic_read ( & state - > rx_good ) = = state - > packet_count ;
}
static int efx_end_loopback ( struct efx_tx_queue * tx_queue ,
struct efx_loopback_self_tests * lb_tests )
2008-05-07 16:36:19 +04:00
{
struct efx_nic * efx = tx_queue - > efx ;
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-05-07 16:36:19 +04:00
struct sk_buff * skb ;
int tx_done = 0 , rx_good , rx_bad ;
int i , rc = 0 ;
2008-05-17 00:16:10 +04:00
if ( efx_dev_registered ( efx ) )
2008-05-07 16:36:19 +04:00
netif_tx_lock_bh ( efx - > net_dev ) ;
/* Count the number of tx completions, and decrement the refcnt. Any
* skbs not already completed will be free ' d when the queue is flushed */
for ( i = 0 ; i < state - > packet_count ; i + + ) {
skb = state - > skbs [ i ] ;
if ( skb & & ! skb_shared ( skb ) )
+ + tx_done ;
dev_kfree_skb_any ( skb ) ;
}
2008-05-17 00:16:10 +04:00
if ( efx_dev_registered ( efx ) )
2008-05-07 16:36:19 +04:00
netif_tx_unlock_bh ( efx - > net_dev ) ;
/* Check TX completion and received packet counts */
rx_good = atomic_read ( & state - > rx_good ) ;
rx_bad = atomic_read ( & state - > rx_bad ) ;
if ( tx_done ! = state - > packet_count ) {
/* Don't free the skbs; they will be picked up on TX
* overflow or channel teardown .
*/
EFX_ERR ( efx , " TX queue %d saw only %d out of an expected %d "
" TX completion events in %s loopback test \n " ,
tx_queue - > queue , tx_done , state - > packet_count ,
LOOPBACK_MODE ( efx ) ) ;
rc = - ETIMEDOUT ;
/* Allow to fall through so we see the RX errors as well */
}
/* We may always be up to a flush away from our desired packet total */
if ( rx_good ! = state - > packet_count ) {
EFX_LOG ( efx , " TX queue %d saw only %d out of an expected %d "
" received packets in %s loopback test \n " ,
tx_queue - > queue , rx_good , state - > packet_count ,
LOOPBACK_MODE ( efx ) ) ;
rc = - ETIMEDOUT ;
/* Fall through */
}
/* Update loopback test structure */
lb_tests - > tx_sent [ tx_queue - > queue ] + = state - > packet_count ;
lb_tests - > tx_done [ tx_queue - > queue ] + = tx_done ;
lb_tests - > rx_good + = rx_good ;
lb_tests - > rx_bad + = rx_bad ;
return rc ;
}
static int
efx_test_loopback ( struct efx_tx_queue * tx_queue ,
struct efx_loopback_self_tests * lb_tests )
{
struct efx_nic * efx = tx_queue - > efx ;
2008-09-01 15:49:02 +04:00
struct efx_loopback_state * state = efx - > loopback_selftest ;
2008-09-01 15:46:33 +04:00
int i , begin_rc , end_rc ;
2008-05-07 16:36:19 +04:00
2008-09-01 15:49:02 +04:00
for ( i = 0 ; i < 3 ; i + + ) {
2008-05-07 16:36:19 +04:00
/* Determine how many packets to send */
2009-10-23 12:30:58 +04:00
state - > packet_count = EFX_TXQ_SIZE / 3 ;
2008-05-07 16:36:19 +04:00
state - > packet_count = min ( 1 < < ( i < < 2 ) , state - > packet_count ) ;
state - > skbs = kzalloc ( sizeof ( state - > skbs [ 0 ] ) *
state - > packet_count , GFP_KERNEL ) ;
2008-05-17 00:20:20 +04:00
if ( ! state - > skbs )
return - ENOMEM ;
2008-09-01 15:46:50 +04:00
state - > flush = false ;
2008-05-07 16:36:19 +04:00
EFX_LOG ( efx , " TX queue %d testing %s loopback with %d "
" packets \n " , tx_queue - > queue , LOOPBACK_MODE ( efx ) ,
state - > packet_count ) ;
efx_iterate_state ( efx ) ;
2008-09-01 15:46:33 +04:00
begin_rc = efx_begin_loopback ( tx_queue ) ;
/* This will normally complete very quickly, but be
* prepared to wait up to 100 ms . */
msleep ( 1 ) ;
if ( ! efx_poll_loopback ( efx ) ) {
msleep ( 100 ) ;
efx_poll_loopback ( efx ) ;
2008-05-07 16:36:19 +04:00
}
2008-09-01 15:46:33 +04:00
end_rc = efx_end_loopback ( tx_queue , lb_tests ) ;
2008-05-07 16:36:19 +04:00
kfree ( state - > skbs ) ;
2008-09-01 15:46:33 +04:00
if ( begin_rc | | end_rc ) {
2008-05-07 16:36:19 +04:00
/* Wait a while to ensure there are no packets
* floating around after a failure . */
schedule_timeout_uninterruptible ( HZ / 10 ) ;
2008-09-01 15:46:33 +04:00
return begin_rc ? begin_rc : end_rc ;
2008-05-07 16:36:19 +04:00
}
}
EFX_LOG ( efx , " TX queue %d passed %s loopback test with a burst length "
" of %d packets \n " , tx_queue - > queue , LOOPBACK_MODE ( efx ) ,
state - > packet_count ) ;
2008-09-01 15:45:08 +04:00
return 0 ;
2008-05-07 16:36:19 +04:00
}
2009-11-29 06:43:00 +03:00
/* Wait for link up. On Falcon, we would prefer to rely on efx_monitor, but
* any contention on the mac lock ( via e . g . efx_mac_mcast_work ) causes it
* to delay and retry . Therefore , it ' s safer to just poll directly . Wait
* for link up and any faults to dissipate . */
static int efx_wait_for_link ( struct efx_nic * efx )
{
struct efx_link_state * link_state = & efx - > link_state ;
int count ;
bool link_up ;
for ( count = 0 ; count < 40 ; count + + ) {
schedule_timeout_uninterruptible ( HZ / 10 ) ;
if ( efx - > type - > monitor ! = NULL ) {
mutex_lock ( & efx - > mac_lock ) ;
efx - > type - > monitor ( efx ) ;
mutex_unlock ( & efx - > mac_lock ) ;
} else {
struct efx_channel * channel = & efx - > channel [ 0 ] ;
if ( channel - > work_pending )
efx_process_channel_now ( channel ) ;
}
mutex_lock ( & efx - > mac_lock ) ;
link_up = link_state - > up ;
if ( link_up )
link_up = ! efx - > mac_op - > check_fault ( efx ) ;
mutex_unlock ( & efx - > mac_lock ) ;
if ( link_up )
return 0 ;
}
return - ETIMEDOUT ;
}
2008-12-27 00:46:38 +03:00
static int efx_test_loopbacks ( struct efx_nic * efx , struct efx_self_tests * tests ,
2008-05-07 16:36:19 +04:00
unsigned int loopback_modes )
{
2008-09-01 15:49:02 +04:00
enum efx_loopback_mode mode ;
struct efx_loopback_state * state ;
2008-05-07 16:36:19 +04:00
struct efx_tx_queue * tx_queue ;
2009-11-29 06:43:00 +03:00
int rc = 0 ;
2008-05-07 16:36:19 +04:00
2008-09-01 15:49:02 +04:00
/* Set the port loopback_selftest member. From this point on
* all received packets will be dropped . Mark the state as
* " flushing " so all inflight packets are dropped */
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
if ( state = = NULL )
return - ENOMEM ;
BUG_ON ( efx - > loopback_selftest ) ;
state - > flush = true ;
efx - > loopback_selftest = state ;
2008-05-07 16:36:19 +04:00
/* Test all supported loopback modes */
2008-09-01 15:49:02 +04:00
for ( mode = LOOPBACK_NONE ; mode < = LOOPBACK_TEST_MAX ; mode + + ) {
2008-05-07 16:36:19 +04:00
if ( ! ( loopback_modes & ( 1 < < mode ) ) )
continue ;
/* Move the port into the specified loopback mode. */
2008-09-01 15:46:50 +04:00
state - > flush = true ;
2009-11-29 06:43:00 +03:00
mutex_lock ( & efx - > mac_lock ) ;
2008-05-07 16:36:19 +04:00
efx - > loopback_mode = mode ;
2009-11-29 06:43:00 +03:00
rc = __efx_reconfigure_port ( efx ) ;
mutex_unlock ( & efx - > mac_lock ) ;
if ( rc ) {
EFX_ERR ( efx , " unable to move into %s loopback \n " ,
LOOPBACK_MODE ( efx ) ) ;
goto out ;
}
2008-05-07 16:36:19 +04:00
2009-11-29 06:43:00 +03:00
rc = efx_wait_for_link ( efx ) ;
if ( rc ) {
2008-05-07 16:36:19 +04:00
EFX_ERR ( efx , " loopback %s never came up \n " ,
LOOPBACK_MODE ( efx ) ) ;
goto out ;
}
/* Test every TX queue */
efx_for_each_tx_queue ( tx_queue , efx ) {
2008-09-01 15:44:59 +04:00
state - > offload_csum = ( tx_queue - > queue = =
EFX_TX_QUEUE_OFFLOAD_CSUM ) ;
2008-09-01 15:46:28 +04:00
rc = efx_test_loopback ( tx_queue ,
& tests - > loopback [ mode ] ) ;
2008-05-07 16:36:19 +04:00
if ( rc )
goto out ;
}
}
out :
2008-09-01 15:49:02 +04:00
/* Remove the flush. The caller will remove the loopback setting */
2008-09-01 15:46:50 +04:00
state - > flush = true ;
2008-09-01 15:49:02 +04:00
efx - > loopback_selftest = NULL ;
wmb ( ) ;
kfree ( state ) ;
2008-05-07 16:36:19 +04:00
return rc ;
}
/**************************************************************************
*
2008-12-27 00:47:04 +03:00
* Entry point
2008-05-07 16:36:19 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-12-27 00:47:04 +03:00
int efx_selftest ( struct efx_nic * efx , struct efx_self_tests * tests ,
unsigned flags )
2008-05-07 16:36:19 +04:00
{
2008-12-27 00:47:04 +03:00
enum efx_loopback_mode loopback_mode = efx - > loopback_mode ;
int phy_mode = efx - > phy_mode ;
2009-01-29 20:50:51 +03:00
enum reset_type reset_method = RESET_TYPE_INVISIBLE ;
2008-05-07 16:36:19 +04:00
struct efx_channel * channel ;
2008-12-27 00:47:04 +03:00
int rc_test = 0 , rc_reset = 0 , rc ;
/* Online (i.e. non-disruptive) testing
* This checks interrupt generation , event delivery and PHY presence . */
2008-09-01 15:49:02 +04:00
2009-04-29 12:05:08 +04:00
rc = efx_test_mdio ( efx , tests ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-09-01 15:49:02 +04:00
rc = efx_test_nvram ( efx , tests ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-05-07 16:36:19 +04:00
2008-09-01 15:46:28 +04:00
rc = efx_test_interrupts ( efx , tests ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-09-01 15:49:02 +04:00
2008-05-07 16:36:19 +04:00
efx_for_each_channel ( channel , efx ) {
2008-09-01 15:47:38 +04:00
rc = efx_test_eventq_irq ( channel , tests ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-05-07 16:36:19 +04:00
}
2008-09-01 15:49:02 +04:00
2008-12-27 00:47:04 +03:00
if ( rc_test )
return rc_test ;
2008-05-07 16:36:19 +04:00
2008-12-27 00:47:04 +03:00
if ( ! ( flags & ETH_TEST_FL_OFFLINE ) )
2008-12-27 00:47:25 +03:00
return efx_test_phy ( efx , tests , flags ) ;
2008-12-27 00:47:04 +03:00
/* Offline (i.e. disruptive) testing
* This checks MAC and PHY loopback on the specified port . */
2008-09-01 15:49:02 +04:00
/* force the carrier state off so the kernel doesn't transmit during
* the loopback test , and the watchdog timeout doesn ' t fire . Also put
* falcon into loopback for the register test .
*/
mutex_lock ( & efx - > mac_lock ) ;
efx - > port_inhibited = true ;
2008-12-13 09:00:49 +03:00
if ( efx - > loopback_modes ) {
/* We need the 312 clock from the PHY to test the XMAC
* registers , so move into XGMII loopback if available */
if ( efx - > loopback_modes & ( 1 < < LOOPBACK_XGMII ) )
efx - > loopback_mode = LOOPBACK_XGMII ;
else
efx - > loopback_mode = __ffs ( efx - > loopback_modes ) ;
}
2008-09-01 15:49:02 +04:00
__efx_reconfigure_port ( efx ) ;
mutex_unlock ( & efx - > mac_lock ) ;
/* free up all consumers of SRAM (including all the queues) */
2009-11-29 06:42:41 +03:00
efx_reset_down ( efx , reset_method ) ;
2008-09-01 15:49:02 +04:00
rc = efx_test_chip ( efx , tests ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-09-01 15:49:02 +04:00
/* reset the chip to recover from the register test */
2009-11-29 06:42:31 +03:00
rc_reset = efx - > type - > reset ( efx , reset_method ) ;
2008-09-01 15:49:02 +04:00
2008-12-27 00:46:38 +03:00
/* Ensure that the phy is powered and out of loopback
* for the bist and loopback tests */
efx - > phy_mode & = ~ PHY_MODE_LOW_POWER ;
2008-09-01 15:49:02 +04:00
efx - > loopback_mode = LOOPBACK_NONE ;
2008-05-07 16:36:19 +04:00
2009-11-29 06:42:41 +03:00
rc = efx_reset_up ( efx , reset_method , rc_reset = = 0 ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_reset )
rc_reset = rc ;
if ( rc_reset ) {
2008-09-01 15:49:02 +04:00
EFX_ERR ( efx , " Unable to recover from chip test \n " ) ;
efx_schedule_reset ( efx , RESET_TYPE_DISABLE ) ;
2008-12-27 00:47:04 +03:00
return rc_reset ;
2008-09-01 15:49:02 +04:00
}
2008-05-07 16:36:19 +04:00
2008-12-27 00:47:25 +03:00
rc = efx_test_phy ( efx , tests , flags ) ;
2008-12-27 00:47:04 +03:00
if ( rc & & ! rc_test )
rc_test = rc ;
2008-05-07 16:36:19 +04:00
2008-12-27 00:47:04 +03:00
rc = efx_test_loopbacks ( efx , tests , efx - > loopback_modes ) ;
if ( rc & & ! rc_test )
rc_test = rc ;
2008-05-07 16:36:19 +04:00
2008-09-01 15:49:02 +04:00
/* restore the PHY to the previous state */
2009-11-29 06:42:41 +03:00
mutex_lock ( & efx - > mac_lock ) ;
2008-09-01 15:49:02 +04:00
efx - > phy_mode = phy_mode ;
efx - > port_inhibited = false ;
2009-11-29 06:42:41 +03:00
efx - > loopback_mode = loopback_mode ;
__efx_reconfigure_port ( efx ) ;
mutex_unlock ( & efx - > mac_lock ) ;
2008-09-01 15:49:02 +04:00
2008-12-27 00:47:04 +03:00
return rc_test ;
2008-05-07 16:36:19 +04:00
}