tg3: Add ethtool selftest
Subsystems (2)
cover tg3: Add ethtool selftest
I tried to keep these patches reasonably small. The total added size is about 5K on i386. Patches will follow.
PATCH 1/9 tg3: Add basic selftest infrastructure
drivers/net/tg3.c +28 -0
@@ -133,6 +133,8 @@/* number of ETHTOOL_GSTATS u64's */ #define TG3_NUM_STATS (sizeof(struct tg3_ethtool_stats)/sizeof(u64)) +#define TG3_NUM_TEST 6 +static char version[]__devinitdata =DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n" ;
@@ -316,6 +318,17 @@ { "nic_tx_threshold_hit" } }; +static struct { +const char string[ETH_GSTRING_LEN ]; +} ethtool_test_keys[TG3_NUM_TEST ] = { + {"nvram test (online) " }, + {"link test (online) " }, + {"register test (offline)" }, + {"memory test (offline)" }, + {"loopback test (offline)" }, + {"interrupt test (offline)" }, +}; +static void tg3_write_indirect_reg32 (struct tg3 *tp,u32 off,u32 val) {if ((tp->tg3_flags &TG3_FLAG_PCIX_TARGET_HWBUG ) !=0 ) {
@@ -7199,12 +7212,20 @@return TG3_NUM_STATS ; } +static int tg3_get_test_count (struct net_device *dev) +{ +return TG3_NUM_TEST ; +} +static void tg3_get_strings (struct net_device *dev,u32 stringset,u8 *buf) {switch (stringset) {case ETH_SS_STATS :memcpy (buf, ðtool_stats_keys,sizeof (ethtool_stats_keys));break ; +case ETH_SS_TEST : +memcpy (buf, ðtool_test_keys,sizeof (ethtool_test_keys)); +break ;default :WARN_ON (1 );/* we need a WARN() */ break ;
@@ -7218,6 +7239,11 @@memcpy (tmp_stats,tg3_get_estats (tp),sizeof (tp->estats)); } +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest, +u64 *data) +{ +} +static int tg3_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd) {struct mii_ioctl_data *data =if_mii (ifr);
@@ -7331,6 +7357,8 @@ .get_tso = ethtool_op_get_tso, .set_tso = tg3_set_tso, #endif + .self_test_count = tg3_get_test_count, + .self_test = tg3_self_test, .get_strings = tg3_get_strings, .get_stats_count = tg3_get_stats_count, .get_ethtool_stats = tg3_get_ethtool_stats,
PATCH 2/9 tg3: Add nvram test
In reply to Michael Chandrivers/net/tg3.c +50 -0
@@ -7239,9 +7239,59 @@memcpy (tmp_stats,tg3_get_estats (tp),sizeof (tp->estats)); } +#define NVRAM_TEST_SIZE 0x100 + +static int tg3_test_nvram (struct tg3 *tp) +{ +u32 *buf, csum; +int i, j, err =0 ; + + buf =kmalloc (NVRAM_TEST_SIZE ,GFP_KERNEL ); +if (buf ==NULL ) +return -ENOMEM ; + +for (i =0 , j =0 ; i <NVRAM_TEST_SIZE ; i +=4 , j++) { +u32 val; + +if ((err =tg3_nvram_read (tp, i, &val)) !=0 ) +break ; + buf[j] =cpu_to_le32 (val); + } +if (i <NVRAM_TEST_SIZE ) + goto out; + + err = -EIO ; +if (cpu_to_be32 (buf[0 ]) !=TG3_EEPROM_MAGIC ) + goto out; + +/* Bootstrap checksum at offset 0x10 */ + csum =calc_crc ((unsigned char *) buf,0x10 ); +if (csum !=cpu_to_le32 (buf[0x10 /4 ])) + goto out; + +/* Manufacturing block starts at offset 0x74, checksum at 0xfc */ + csum =calc_crc ((unsigned char *) &buf[0x74 /4 ],0x88 ); +if (csum !=cpu_to_le32 (buf[0xfc /4 ])) + goto out; + + err =0 ; + +out: +kfree (buf); +return err; +} +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest,u64 *data) { +struct tg3 *tp =netdev_priv (dev); + +memset (data,0 ,sizeof (u64) *TG3_NUM_TEST ); + +if (tg3_test_nvram (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[0 ] =1 ; + } }static int tg3_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd)
PATCH 3/9 tg3: Add link test
In reply to Michael Chandrivers/net/tg3.c +30 -0
@@ -7281,6 +7281,32 @@return err; } +#define TG3_SERDES_TIMEOUT_SEC 2 +#define TG3_COPPER_TIMEOUT_SEC 6 + +static int tg3_test_link (struct tg3 *tp) +{ +int i, max; + +if (!netif_running (tp->dev)) +return -ENODEV ; + +if (tp->tg3_flags2 &TG3_FLG2_PHY_SERDES ) + max =TG3_SERDES_TIMEOUT_SEC ; +else + max =TG3_COPPER_TIMEOUT_SEC ; + +for (i =0 ; i < max; i++) { +if (netif_carrier_ok (tp->dev)) +return 0 ; + +if (msleep_interruptible (1000 )) +break ; + } + +return -EIO ; +} +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest,u64 *data) {
@@ -7292,6 +7318,10 @@ etest->flags |=ETH_TEST_FL_FAILED ; data[0 ] =1 ; } +if (tg3_test_link (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[1 ] =1 ; + } }static int tg3_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd)
PATCH 4/9 tg3: Add parameter to tg3_halt
In reply to Michael ChanAdd a reset kind parameter to tg3_halt() so that the RESET_KIND_SUSPEND parameter can be passed to tg3_halt() before doing offline tests.
All other calls to tg3_halt() will use the RESET_KIND_SHUTDOWN parameter.
drivers/net/tg3.c +15 -15
@@ -3083,7 +3083,7 @@ }static int tg3_init_hw (struct tg3 *); -static int tg3_halt (struct tg3 *,int ); +static int tg3_halt (struct tg3 *,int ,int );#ifdef CONFIG_NET_POLL_CONTROLLER static void tg3_poll_controller (struct net_device *dev)
@@ -3107,7 +3107,7 @@ restart_timer = tp->tg3_flags2 &TG3_FLG2_RESTART_TIMER ; tp->tg3_flags2 &= ~TG3_FLG2_RESTART_TIMER ; -tg3_halt (tp,0 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,0 );tg3_init_hw (tp);tg3_netif_start (tp);
@@ -3453,7 +3453,7 @@spin_lock_irq (&tp->lock);spin_lock (&tp->tx_lock); -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_set_mtu (dev, tp, new_mtu);
@@ -4144,19 +4144,19 @@ }/* tp->lock is held. */ -static int tg3_halt (struct tg3 *tp,int silent) +static int tg3_halt (struct tg3 *tp,int kind,int silent) {int err;tg3_stop_fw (tp); -tg3_write_sig_pre_reset (tp,RESET_KIND_SHUTDOWN ); +tg3_write_sig_pre_reset (tp, kind);tg3_abort_hw (tp, silent); err =tg3_chip_reset (tp); -tg3_write_sig_legacy (tp,RESET_KIND_SHUTDOWN ); -tg3_write_sig_post_reset (tp,RESET_KIND_SHUTDOWN ); +tg3_write_sig_legacy (tp, kind); +tg3_write_sig_post_reset (tp, kind);if (err)return err;
@@ -5997,7 +5997,7 @@spin_lock_irq (&tp->lock);spin_lock (&tp->tx_lock); -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 ); err =tg3_init_hw (tp);spin_unlock (&tp->tx_lock);
@@ -6073,7 +6073,7 @@ err =tg3_init_hw (tp);if (err) { -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_free_rings (tp); }else {if (tp->tg3_flags &TG3_FLAG_TAGGED_STATUS )
@@ -6117,7 +6117,7 @@pci_disable_msi (tp->pdev); tp->tg3_flags2 &= ~TG3_FLG2_USING_MSI ; } -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_free_rings (tp);tg3_free_consistent (tp);
@@ -6390,7 +6390,7 @@tg3_disable_ints (tp); -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_free_rings (tp); tp->tg3_flags &= ~(TG3_FLAG_INIT_COMPLETE |
@@ -7110,7 +7110,7 @@ tp->tx_pending = ering->tx_pending;if (netif_running (dev)) { -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_init_hw (tp);tg3_netif_start (tp); }
@@ -7153,7 +7153,7 @@ tp->tg3_flags &= ~TG3_FLAG_TX_PAUSE ;if (netif_running (dev)) { -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );tg3_init_hw (tp);tg3_netif_start (tp); }
@@ -9586,7 +9586,7 @@ (tr32(WDMAC_MODE) & WDMAC_MODE_ENABLE)) {pci_save_state (tp->pdev);tw32 (MEMARB_MODE ,MEMARB_MODE_ENABLE ); -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 ); } err =tg3_test_dma (tp);
@@ -9713,7 +9713,7 @@spin_lock_irq (&tp->lock);spin_lock (&tp->tx_lock); -tg3_halt (tp,1 ); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );spin_unlock (&tp->tx_lock);spin_unlock_irq (&tp->lock);
PATCH 5/9 tg3: Add register test
In reply to Michael Chandrivers/net/tg3.c +241 -0
@@ -7307,6 +7307,219 @@return -EIO ; } +/* Only test the commonly used registers */ +static int tg3_test_registers (struct tg3 *tp) +{ +int i, is_5705; +u32 offset, read_mask, write_mask, val, save_val, read_val; +static struct { +u16 offset; +u16 flags; +#define TG3_FL_5705 0x1 +#define TG3_FL_NOT_5705 0x2 +#define TG3_FL_NOT_5788 0x4 +u32 read_mask; +u32 write_mask; + } reg_tbl[] = { +/* MAC Control Registers */ + {MAC_MODE ,TG3_FL_NOT_5705 , +0x00000000 ,0x00ef6f8c }, + {MAC_MODE ,TG3_FL_5705 , +0x00000000 ,0x01ef6b8c }, + {MAC_STATUS ,TG3_FL_NOT_5705 , +0x03800107 ,0x00000000 }, + {MAC_STATUS ,TG3_FL_5705 , +0x03800100 ,0x00000000 }, + {MAC_ADDR_0_HIGH ,0x0000 , +0x00000000 ,0x0000ffff }, + {MAC_ADDR_0_LOW ,0x0000 , +0x00000000 ,0xffffffff }, + {MAC_RX_MTU_SIZE ,0x0000 , +0x00000000 ,0x0000ffff }, + {MAC_TX_MODE ,0x0000 , +0x00000000 ,0x00000070 }, + {MAC_TX_LENGTHS ,0x0000 , +0x00000000 ,0x00003fff }, + {MAC_RX_MODE ,TG3_FL_NOT_5705 , +0x00000000 ,0x000007fc }, + {MAC_RX_MODE ,TG3_FL_5705 , +0x00000000 ,0x000007dc }, + {MAC_HASH_REG_0 ,0x0000 , +0x00000000 ,0xffffffff }, + {MAC_HASH_REG_1 ,0x0000 , +0x00000000 ,0xffffffff }, + {MAC_HASH_REG_2 ,0x0000 , +0x00000000 ,0xffffffff }, + {MAC_HASH_REG_3 ,0x0000 , +0x00000000 ,0xffffffff }, + +/* Receive Data and Receive BD Initiator Control Registers. */ + {RCVDBDI_JUMBO_BD +0 ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {RCVDBDI_JUMBO_BD +4 ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {RCVDBDI_JUMBO_BD +8 ,TG3_FL_NOT_5705 , +0x00000000 ,0x00000003 }, + {RCVDBDI_JUMBO_BD +0xc ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {RCVDBDI_STD_BD +0 ,0x0000 , +0x00000000 ,0xffffffff }, + {RCVDBDI_STD_BD +4 ,0x0000 , +0x00000000 ,0xffffffff }, + {RCVDBDI_STD_BD +8 ,0x0000 , +0x00000000 ,0xffff0002 }, + {RCVDBDI_STD_BD +0xc ,0x0000 , +0x00000000 ,0xffffffff }, + +/* Receive BD Initiator Control Registers. */ + {RCVBDI_STD_THRESH ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {RCVBDI_STD_THRESH ,TG3_FL_5705 , +0x00000000 ,0x000003ff }, + {RCVBDI_JUMBO_THRESH ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + +/* Host Coalescing Control Registers. */ + {HOSTCC_MODE ,TG3_FL_NOT_5705 , +0x00000000 ,0x00000004 }, + {HOSTCC_MODE ,TG3_FL_5705 , +0x00000000 ,0x000000f6 }, + {HOSTCC_RXCOL_TICKS ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_RXCOL_TICKS ,TG3_FL_5705 , +0x00000000 ,0x000003ff }, + {HOSTCC_TXCOL_TICKS ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_TXCOL_TICKS ,TG3_FL_5705 , +0x00000000 ,0x000003ff }, + {HOSTCC_RXMAX_FRAMES ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_RXMAX_FRAMES ,TG3_FL_5705 |TG3_FL_NOT_5788 , +0x00000000 ,0x000000ff }, + {HOSTCC_TXMAX_FRAMES ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_TXMAX_FRAMES ,TG3_FL_5705 |TG3_FL_NOT_5788 , +0x00000000 ,0x000000ff }, + {HOSTCC_RXCOAL_TICK_INT ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_TXCOAL_TICK_INT ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_RXCOAL_MAXF_INT ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_RXCOAL_MAXF_INT ,TG3_FL_5705 |TG3_FL_NOT_5788 , +0x00000000 ,0x000000ff }, + {HOSTCC_TXCOAL_MAXF_INT ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_TXCOAL_MAXF_INT ,TG3_FL_5705 |TG3_FL_NOT_5788 , +0x00000000 ,0x000000ff }, + {HOSTCC_STAT_COAL_TICKS ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_STATS_BLK_HOST_ADDR ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_STATS_BLK_HOST_ADDR +4 ,TG3_FL_NOT_5705 , +0x00000000 ,0xffffffff }, + {HOSTCC_STATUS_BLK_HOST_ADDR ,0x0000 , +0x00000000 ,0xffffffff }, + {HOSTCC_STATUS_BLK_HOST_ADDR +4 ,0x0000 , +0x00000000 ,0xffffffff }, + {HOSTCC_STATS_BLK_NIC_ADDR ,0x0000 , +0xffffffff ,0x00000000 }, + {HOSTCC_STATUS_BLK_NIC_ADDR ,0x0000 , +0xffffffff ,0x00000000 }, + +/* Buffer Manager Control Registers. */ + {BUFMGR_MB_POOL_ADDR ,0x0000 , +0x00000000 ,0x007fff80 }, + {BUFMGR_MB_POOL_SIZE ,0x0000 , +0x00000000 ,0x007fffff }, + {BUFMGR_MB_RDMA_LOW_WATER ,0x0000 , +0x00000000 ,0x0000003f }, + {BUFMGR_MB_MACRX_LOW_WATER ,0x0000 , +0x00000000 ,0x000001ff }, + {BUFMGR_MB_HIGH_WATER ,0x0000 , +0x00000000 ,0x000001ff }, + {BUFMGR_DMA_DESC_POOL_ADDR ,TG3_FL_NOT_5705 , +0xffffffff ,0x00000000 }, + {BUFMGR_DMA_DESC_POOL_SIZE ,TG3_FL_NOT_5705 , +0xffffffff ,0x00000000 }, + +/* Mailbox Registers */ + {GRCMBOX_RCVSTD_PROD_IDX +4 ,0x0000 , +0x00000000 ,0x000001ff }, + {GRCMBOX_RCVJUMBO_PROD_IDX +4 ,TG3_FL_NOT_5705 , +0x00000000 ,0x000001ff }, + {GRCMBOX_RCVRET_CON_IDX_0 +4 ,0x0000 , +0x00000000 ,0x000007ff }, + {GRCMBOX_SNDHOST_PROD_IDX_0 +4 ,0x0000 , +0x00000000 ,0x000001ff }, + + {0xffff ,0x0000 ,0x00000000 ,0x00000000 }, + }; + +if (tp->tg3_flags2 &TG3_FLG2_5705_PLUS ) + is_5705 =1 ; +else + is_5705 =0 ; + +for (i =0 ; reg_tbl[i].offset !=0xffff ; i++) { +if (is_5705 && (reg_tbl[i].flags &TG3_FL_NOT_5705 )) +continue ; + +if (!is_5705 && (reg_tbl[i].flags &TG3_FL_5705 )) +continue ; + +if ((tp->tg3_flags2 &TG3_FLG2_IS_5788 ) && + (reg_tbl[i].flags &TG3_FL_NOT_5788 )) +continue ; + + offset = (u32 ) reg_tbl[i].offset; + read_mask = reg_tbl[i].read_mask; + write_mask = reg_tbl[i].write_mask; + +/* Save the original register content */ + save_val =tr32 (offset); + +/* Determine the read-only value. */ + read_val = save_val & read_mask; + +/* Write zero to the register, then make sure the read-only bits +* are not changed and the read/write bits are all zeros. +*/ +tw32 (offset,0 ); + + val =tr32 (offset); + +/* Test the read-only and read/write bits. */ +if (((val & read_mask) != read_val) || (val & write_mask)) + goto out; + +/* Write ones to all the bits defined by RdMask and WrMask, then +* make sure the read-only bits are not changed and the +* read/write bits are all ones. +*/ +tw32 (offset, read_mask | write_mask); + + val =tr32 (offset); + +/* Test the read-only bits. */ +if ((val & read_mask) != read_val) + goto out; + +/* Test the read/write bits. */ +if ((val & write_mask) != write_mask) + goto out; + +tw32 (offset, save_val); + } + +return 0 ; + +out: + printk(KERN_ERR PFX "Register test failed at offset %x\n", offset); +tw32 (offset, save_val); +return -EIO ; +} +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest,u64 *data) {
@@ -7322,6 +7535,34 @@ etest->flags |=ETH_TEST_FL_FAILED ; data[1 ] =1 ; } +if (etest->flags Ð_TEST_FL_OFFLINE ) { +if (netif_running (dev)) +tg3_netif_stop (tp); + +spin_lock_irq (&tp->lock); +spin_lock (&tp->tx_lock); + +tg3_halt (tp,RESET_KIND_SUSPEND ,1 ); +tg3_nvram_lock (tp); +tg3_halt_cpu (tp,RX_CPU_BASE ); +if (!(tp->tg3_flags2 &TG3_FLG2_5705_PLUS )) +tg3_halt_cpu (tp,TX_CPU_BASE ); +tg3_nvram_unlock (tp); + +if (tg3_test_registers (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[2 ] =1 ; + } + +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 ); +if (netif_running (dev)) { + tp->tg3_flags |=TG3_FLAG_INIT_COMPLETE ; +tg3_init_hw (tp); +tg3_netif_start (tp); + } +spin_unlock (&tp->tx_lock); +spin_unlock_irq (&tp->lock); + } }static int tg3_ioctl (struct net_device *dev,struct ifreq *ifr,int cmd)
PATCH 6/9 tg3: Add memory test
In reply to Michael Chandrivers/net/tg3.c +60 -0
@@ -7520,6 +7520,62 @@return -EIO ; } +static int tg3_do_mem_test (struct tg3 *tp,u32 offset,u32 len) +{ +static u32 test_pattern[] = {0x00000000 ,0xffffffff ,0xaa55a55a }; +int i; +u32 j; + +for (i =0 ; i <sizeof (test_pattern)/sizeof (u32); i++) { +for (j =0 ; j < len; j +=4 ) { +u32 val; + +tg3_write_mem (tp, offset + j, test_pattern[i]); +tg3_read_mem (tp, offset + j, &val); +if (val != test_pattern[i]) +return -EIO ; + } + } +return 0 ; +} + +static int tg3_test_memory (struct tg3 *tp) +{ +static struct mem_entry { +u32 offset; +u32 len; + } mem_tbl_570x[] = { + {0x00000000 ,0x01000 }, + {0x00002000 ,0x1c000 }, + {0xffffffff ,0x00000 } + }, mem_tbl_5705[] = { + {0x00000100 ,0x0000c }, + {0x00000200 ,0x00008 }, + {0x00000b50 ,0x00400 }, + {0x00004000 ,0x00800 }, + {0x00006000 ,0x01000 }, + {0x00008000 ,0x02000 }, + {0x00010000 ,0x0e000 }, + {0xffffffff ,0x00000 } + }; +struct mem_entry *mem_tbl; +int err =0 ; +int i; + +if (tp->tg3_flags2 &TG3_FLG2_5705_PLUS ) + mem_tbl = mem_tbl_5705; +else + mem_tbl = mem_tbl_570x; + +for (i =0 ; mem_tbl[i].offset !=0xffffffff ; i++) { +if ((err =tg3_do_mem_test (tp, mem_tbl[i].offset, + mem_tbl[i].len)) !=0 ) +break ; + } + +return err; +} +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest,u64 *data) {
@@ -7553,6 +7609,10 @@ etest->flags |=ETH_TEST_FL_FAILED ; data[2 ] =1 ; } +if (tg3_test_memory (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[3 ] =1 ; + }tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );if (netif_running (dev)) {
PATCH 7/9 tg3: Add loopback test
In reply to Michael ChanThe test will loopback one packet in MAC loopback mode and verify the packet data.
drivers/net/tg3.c +115 -0
@@ -7576,6 +7576,117 @@return err; } +static int tg3_test_loopback (struct tg3 *tp) +{ +u32 mac_mode, send_idx, rx_start_idx, rx_idx, tx_idx, opaque_key; +u32 desc_idx; +struct sk_buff *skb, *rx_skb; +u8 *tx_data; +dma_addr_t map; +int num_pkts, tx_len, rx_len, i, err; +struct tg3_rx_buffer_desc *desc; + +if (!netif_running (tp->dev)) +return -ENODEV ; + + err = -EIO ; + +tg3_abort_hw (tp,1 ); + +/* Clearing this flag to keep interrupts disabled */ + tp->tg3_flags &= ~TG3_FLAG_INIT_COMPLETE ; +tg3_reset_hw (tp); + + mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK ) | +MAC_MODE_PORT_INT_LPBACK |MAC_MODE_LINK_POLARITY | +MAC_MODE_PORT_MODE_GMII ; +tw32 (MAC_MODE , mac_mode); + + tx_len =1514 ; + skb =dev_alloc_skb (tx_len); + tx_data =skb_put (skb, tx_len); +memcpy (tx_data, tp->dev->dev_addr,6 ); +memset (tx_data +6 ,0x0 ,8 ); + +tw32 (MAC_RX_MTU_SIZE , tx_len +4 ); + +for (i =14 ; i < tx_len; i++) + tx_data[i] = (u8) (i &0xff ); + + map =pci_map_single (tp->pdev, skb->data, tx_len,PCI_DMA_TODEVICE ); + +tw32_f (HOSTCC_MODE , tp->coalesce_mode |HOSTCC_MODE_ENABLE | +HOSTCC_MODE_NOW ); + +udelay (10 ); + + rx_start_idx = tp->hw_status->idx[0 ].rx_producer; + + send_idx =0 ; + num_pkts =0 ; + +tg3_set_txd (tp, send_idx, map, tx_len,0 ,1 ); + + send_idx++; + num_pkts++; + +tw32_tx_mbox (MAILBOX_SNDHOST_PROD_IDX_0 +TG3_64BIT_REG_LOW , send_idx); +tr32 (MAILBOX_SNDHOST_PROD_IDX_0 +TG3_64BIT_REG_LOW ); + +udelay (10 ); + +for (i =0 ; i <10 ; i++) { +tw32_f (HOSTCC_MODE , tp->coalesce_mode |HOSTCC_MODE_ENABLE | +HOSTCC_MODE_NOW ); + +udelay (10 ); + + tx_idx = tp->hw_status->idx[0 ].tx_consumer; + rx_idx = tp->hw_status->idx[0 ].rx_producer; +if ((tx_idx == send_idx) && + (rx_idx == (rx_start_idx + num_pkts))) +break ; + } + +pci_unmap_single (tp->pdev, map, tx_len,PCI_DMA_TODEVICE ); +dev_kfree_skb (skb); + +if (tx_idx != send_idx) + goto out; + +if (rx_idx != rx_start_idx + num_pkts) + goto out; + + desc = &tp->rx_rcb[rx_start_idx]; + desc_idx = desc->opaque &RXD_OPAQUE_INDEX_MASK ; + opaque_key = desc->opaque &RXD_OPAQUE_RING_MASK ; +if (opaque_key !=RXD_OPAQUE_RING_STD ) + goto out; + +if ((desc->err_vlan &RXD_ERR_MASK ) !=0 && + (desc->err_vlan !=RXD_ERR_ODD_NIBBLE_RCVD_MII )) + goto out; + + rx_len = ((desc->idx_len &RXD_LEN_MASK ) >>RXD_LEN_SHIFT ) -4 ; +if (rx_len != tx_len) + goto out; + + rx_skb = tp->rx_std_buffers[desc_idx].skb; + + map =pci_unmap_addr (&tp->rx_std_buffers[desc_idx], mapping); +pci_dma_sync_single_for_cpu (tp->pdev, map, rx_len,PCI_DMA_FROMDEVICE ); + +for (i =14 ; i < tx_len; i++) { +if (*(rx_skb->data + i) != (u8) (i &0xff )) + goto out; + } + err =0 ; + +/* tg3_free_rings will unmap and free the rx_skb */ +out: +return err; +} +static void tg3_self_test (struct net_device *dev,struct ethtool_test *etest,u64 *data) {
@@ -7613,6 +7724,10 @@ etest->flags |=ETH_TEST_FL_FAILED ; data[3 ] =1 ; } +if (tg3_test_loopback (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[4 ] =1 ; + }tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );if (netif_running (dev)) {
PATCH 8/9 tg3: Add interrupt test
In reply to Michael ChanThis test uses the previously added tg3_test_interrupt() to perform the test.
drivers/net/tg3.c +12 -0
@@ -5894,6 +5894,9 @@int err, i;u32 int_mbox =0 ; +if (!netif_running (dev)) +return -ENODEV ; +tg3_disable_ints (tp);free_irq (tp->pdev->irq, dev);
@@ -7729,6 +7732,15 @@ data[4 ] =1 ; } +spin_unlock (&tp->tx_lock); +spin_unlock_irq (&tp->lock); +if (tg3_test_interrupt (tp) !=0 ) { + etest->flags |=ETH_TEST_FL_FAILED ; + data[5 ] =1 ; + } +spin_lock_irq (&tp->lock); +spin_lock (&tp->tx_lock); +tg3_halt (tp,RESET_KIND_SHUTDOWN ,1 );if (netif_running (dev)) { tp->tg3_flags |=TG3_FLAG_INIT_COMPLETE ;
PATCH 9/9 tg3: Fix bug in tg3_load_firmware_cpu
In reply to Michael ChanAdd tg3_nvram_lock() and tg3_nvram_unlock() calls around tg3_halt_cpu(). It is possible that the bootcode may be loading code from nvram during this call and stopping the cpu without getting the lock may cause uncompleted nvram data to be left in the nvram data register. Subsequent calls to read/write nvram data will fail.
drivers/net/tg3.c +5 -0
@@ -4370,7 +4370,12 @@*/ tp->tg3_flags |=TG3_FLAG_PCIX_TARGET_HWBUG ; +/* It is possible that bootcode is still loading at this point. +* Get the nvram lock first before halting the cpu. +*/ +tg3_nvram_lock (tp); err =tg3_halt_cpu (tp, cpu_base); +tg3_nvram_unlock (tp);if (err) goto out;
Re: [PATCH 2.6.12-rc5 9/9] tg3: Fix bug in tg3_load_firmware_cpu
Quoted 7 lines · Michael Chan
Add
tg3_nvram_lock()andtg3_nvram_unlock()calls aroundtg3_halt_cpu(). It is possible that the bootcode may be loading code from nvram during this call and stopping the cpu without getting the lock may cause uncompleted nvram data to be left in the nvram data register. Subsequent calls to read/write nvram data will fail.Signed-off-by Michael Chan <[email protected]>
All the patches seem sane to me.
To touch a bit on patch order, I would suggest ordering bug fixes before feature patches. Your patch #9 could have been better ordered as patch #1.
The patch order did not matter today, but sometime in the future, if an add-a-feature patch is rejected or delayed, you would still likely want DaveM to apply other bugfix patches.
Regards,
Jeff
David S. Miller
maintainerRe: [PATCH 2.6.12-rc5 9/9] tg3: Fix bug in tg3_load_firmware_cpu
In reply to Michael ChanAll 9 patches applied, thanks Michael.