aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@linux-foundation.org>2007-09-19 18:36:46 -0400
committerJeff Garzik <jeff@garzik.org>2007-09-20 15:23:00 -0400
commit75e806838a3327d4ca9030e588d34de11b04f341 (patch)
tree11dff9f4326b300ff80cbc4f627f797a06ad8c52 /drivers/net
parent05745c4ab1c58fbb6ab8e8d3a40e0e395d7e2b0e (diff)
sky2: receive FIFO checking
A driver writer from another operating system hinted that the versions of Yukon 2 chip with rambuffer (EC and XL) have a hardware bug that if the FIFO ever gets completely full it will hang. Sounds like a classic ring full vs ring empty wrap around bug. As a workaround, use the existing watchdog timer to check for ring full lockup. Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/sky2.c74
-rw-r--r--drivers/net/sky2.h9
2 files changed, 66 insertions, 17 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index d0e5875628e9..2831f44e6270 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1648,9 +1648,6 @@ static int sky2_down(struct net_device *dev)
1648 if (netif_msg_ifdown(sky2)) 1648 if (netif_msg_ifdown(sky2))
1649 printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); 1649 printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
1650 1650
1651 if (netif_carrier_ok(dev) && --hw->active == 0)
1652 del_timer(&hw->watchdog_timer);
1653
1654 /* Stop more packets from being queued */ 1651 /* Stop more packets from being queued */
1655 netif_stop_queue(dev); 1652 netif_stop_queue(dev);
1656 1653
@@ -1775,9 +1772,7 @@ static void sky2_link_up(struct sky2_port *sky2)
1775 1772
1776 netif_carrier_on(sky2->netdev); 1773 netif_carrier_on(sky2->netdev);
1777 1774
1778 if (hw->active++ == 0) 1775 mod_timer(&hw->watchdog_timer, jiffies + 1);
1779 mod_timer(&hw->watchdog_timer, jiffies + 1);
1780
1781 1776
1782 /* Turn on link LED */ 1777 /* Turn on link LED */
1783 sky2_write8(hw, SK_REG(port, LNK_LED_REG), 1778 sky2_write8(hw, SK_REG(port, LNK_LED_REG),
@@ -1828,11 +1823,6 @@ static void sky2_link_down(struct sky2_port *sky2)
1828 1823
1829 netif_carrier_off(sky2->netdev); 1824 netif_carrier_off(sky2->netdev);
1830 1825
1831 /* Stop watchdog if both ports are not active */
1832 if (--hw->active == 0)
1833 del_timer(&hw->watchdog_timer);
1834
1835
1836 /* Turn on link LED */ 1826 /* Turn on link LED */
1837 sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF); 1827 sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
1838 1828
@@ -2484,20 +2474,72 @@ static void sky2_le_error(struct sky2_hw *hw, unsigned port,
2484 sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK); 2474 sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK);
2485} 2475}
2486 2476
2487/* Check for lost IRQ once a second */ 2477static int sky2_rx_hung(struct net_device *dev)
2478{
2479 struct sky2_port *sky2 = netdev_priv(dev);
2480 struct sky2_hw *hw = sky2->hw;
2481 unsigned port = sky2->port;
2482 unsigned rxq = rxqaddr[port];
2483 u32 mac_rp = sky2_read32(hw, SK_REG(port, RX_GMF_RP));
2484 u8 mac_lev = sky2_read8(hw, SK_REG(port, RX_GMF_RLEV));
2485 u8 fifo_rp = sky2_read8(hw, Q_ADDR(rxq, Q_RP));
2486 u8 fifo_lev = sky2_read8(hw, Q_ADDR(rxq, Q_RL));
2487
2488 /* If idle and MAC or PCI is stuck */
2489 if (sky2->check.last == dev->last_rx &&
2490 ((mac_rp == sky2->check.mac_rp &&
2491 mac_lev != 0 && mac_lev >= sky2->check.mac_lev) ||
2492 /* Check if the PCI RX hang */
2493 (fifo_rp == sky2->check.fifo_rp &&
2494 fifo_lev != 0 && fifo_lev >= sky2->check.fifo_lev))) {
2495 printk(KERN_DEBUG PFX "%s: hung mac %d:%d fifo %d (%d:%d)\n",
2496 dev->name, mac_lev, mac_rp, fifo_lev, fifo_rp,
2497 sky2_read8(hw, Q_ADDR(rxq, Q_WP)));
2498 return 1;
2499 } else {
2500 sky2->check.last = dev->last_rx;
2501 sky2->check.mac_rp = mac_rp;
2502 sky2->check.mac_lev = mac_lev;
2503 sky2->check.fifo_rp = fifo_rp;
2504 sky2->check.fifo_lev = fifo_lev;
2505 return 0;
2506 }
2507}
2508
2488static void sky2_watchdog(unsigned long arg) 2509static void sky2_watchdog(unsigned long arg)
2489{ 2510{
2490 struct sky2_hw *hw = (struct sky2_hw *) arg; 2511 struct sky2_hw *hw = (struct sky2_hw *) arg;
2512 struct net_device *dev;
2491 2513
2514 /* Check for lost IRQ once a second */
2492 if (sky2_read32(hw, B0_ISRC)) { 2515 if (sky2_read32(hw, B0_ISRC)) {
2493 struct net_device *dev = hw->dev[0]; 2516 dev = hw->dev[0];
2494
2495 if (__netif_rx_schedule_prep(dev)) 2517 if (__netif_rx_schedule_prep(dev))
2496 __netif_rx_schedule(dev); 2518 __netif_rx_schedule(dev);
2519 } else {
2520 int i, active = 0;
2521
2522 for (i = 0; i < hw->ports; i++) {
2523 dev = hw->dev[i];
2524 if (!netif_running(dev))
2525 continue;
2526 ++active;
2527
2528 /* For chips with Rx FIFO, check if stuck */
2529 if ((hw->flags & SKY2_HW_RAMBUFFER) &&
2530 sky2_rx_hung(dev)) {
2531 pr_info(PFX "%s: receiver hang detected\n",
2532 dev->name);
2533 schedule_work(&hw->restart_work);
2534 return;
2535 }
2536 }
2537
2538 if (active == 0)
2539 return;
2497 } 2540 }
2498 2541
2499 if (hw->active > 0) 2542 mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ));
2500 mod_timer(&hw->watchdog_timer, round_jiffies(jiffies + HZ));
2501} 2543}
2502 2544
2503/* Hardware/software error handling */ 2545/* Hardware/software error handling */
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index a05b30b68fa1..69cd98400fe6 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -2027,6 +2027,14 @@ struct sky2_port {
2027 u16 rx_tag; 2027 u16 rx_tag;
2028 struct vlan_group *vlgrp; 2028 struct vlan_group *vlgrp;
2029#endif 2029#endif
2030 struct {
2031 unsigned long last;
2032 u32 mac_rp;
2033 u8 mac_lev;
2034 u8 fifo_rp;
2035 u8 fifo_lev;
2036 } check;
2037
2030 2038
2031 dma_addr_t rx_le_map; 2039 dma_addr_t rx_le_map;
2032 dma_addr_t tx_le_map; 2040 dma_addr_t tx_le_map;
@@ -2064,7 +2072,6 @@ struct sky2_hw {
2064 u8 chip_rev; 2072 u8 chip_rev;
2065 u8 pmd_type; 2073 u8 pmd_type;
2066 u8 ports; 2074 u8 ports;
2067 u8 active;
2068 2075
2069 struct sky2_status_le *st_le; 2076 struct sky2_status_le *st_le;
2070 u32 st_idx; 2077 u32 st_idx;