diff options
author | Lennert Buytenhek <buytenh@wantstofly.org> | 2008-06-01 19:47:21 -0400 |
---|---|---|
committer | Lennert Buytenhek <buytenh@wantstofly.org> | 2008-06-12 02:40:37 -0400 |
commit | 226bb6b732f8c2cc7004279c509333fa41186a6d (patch) | |
tree | 8b94892772c21f3be5f22b844ed18cf49c438dd5 /drivers/net/mv643xx_eth.c | |
parent | 3d6b35bc5090cf8d8b7e62eca1f9c21ca56fc6c7 (diff) |
mv643xx_eth: work around TX hang hardware issue
Under some conditions, the TXQ ('TX queue being served') bit can clear
before all packets queued for that TX queue have been transmitted.
This patch enables TXend interrupts, and uses those to re-kick TX
queues that claim to be idle but still have queued descriptors from
the interrupt handler.
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Acked-by: Dale Farnsworth <dale@farnsworth.org>
Diffstat (limited to 'drivers/net/mv643xx_eth.c')
-rw-r--r-- | drivers/net/mv643xx_eth.c | 35 |
1 files changed, 30 insertions, 5 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 287155ea9ce1..c99915740779 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c | |||
@@ -96,6 +96,7 @@ static char mv643xx_eth_driver_version[] = "1.0"; | |||
96 | #define TX_BW_MTU(p) (0x0458 + ((p) << 10)) | 96 | #define TX_BW_MTU(p) (0x0458 + ((p) << 10)) |
97 | #define TX_BW_BURST(p) (0x045c + ((p) << 10)) | 97 | #define TX_BW_BURST(p) (0x045c + ((p) << 10)) |
98 | #define INT_CAUSE(p) (0x0460 + ((p) << 10)) | 98 | #define INT_CAUSE(p) (0x0460 + ((p) << 10)) |
99 | #define INT_TX_END 0x07f80000 | ||
99 | #define INT_RX 0x0007fbfc | 100 | #define INT_RX 0x0007fbfc |
100 | #define INT_EXT 0x00000002 | 101 | #define INT_EXT 0x00000002 |
101 | #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) | 102 | #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) |
@@ -619,7 +620,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) | |||
619 | netif_rx_complete(mp->dev, napi); | 620 | netif_rx_complete(mp->dev, napi); |
620 | wrl(mp, INT_CAUSE(mp->port_num), 0); | 621 | wrl(mp, INT_CAUSE(mp->port_num), 0); |
621 | wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); | 622 | wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); |
622 | wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT); | 623 | wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); |
623 | } | 624 | } |
624 | 625 | ||
625 | return rx; | 626 | return rx; |
@@ -1622,8 +1623,10 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) | |||
1622 | struct mv643xx_eth_private *mp = netdev_priv(dev); | 1623 | struct mv643xx_eth_private *mp = netdev_priv(dev); |
1623 | u32 int_cause; | 1624 | u32 int_cause; |
1624 | u32 int_cause_ext; | 1625 | u32 int_cause_ext; |
1626 | u32 txq_active; | ||
1625 | 1627 | ||
1626 | int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & (INT_RX | INT_EXT); | 1628 | int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & |
1629 | (INT_TX_END | INT_RX | INT_EXT); | ||
1627 | if (int_cause == 0) | 1630 | if (int_cause == 0) |
1628 | return IRQ_NONE; | 1631 | return IRQ_NONE; |
1629 | 1632 | ||
@@ -1675,6 +1678,8 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) | |||
1675 | } | 1678 | } |
1676 | #endif | 1679 | #endif |
1677 | 1680 | ||
1681 | txq_active = rdl(mp, TXQ_COMMAND(mp->port_num)); | ||
1682 | |||
1678 | /* | 1683 | /* |
1679 | * TxBuffer or TxError set for any of the 8 queues? | 1684 | * TxBuffer or TxError set for any of the 8 queues? |
1680 | */ | 1685 | */ |
@@ -1684,8 +1689,28 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) | |||
1684 | for (i = 0; i < 8; i++) | 1689 | for (i = 0; i < 8; i++) |
1685 | if (mp->txq_mask & (1 << i)) | 1690 | if (mp->txq_mask & (1 << i)) |
1686 | txq_reclaim(mp->txq + i, 0); | 1691 | txq_reclaim(mp->txq + i, 0); |
1692 | } | ||
1687 | 1693 | ||
1688 | __txq_maybe_wake(mp->txq + mp->txq_primary); | 1694 | /* |
1695 | * Any TxEnd interrupts? | ||
1696 | */ | ||
1697 | if (int_cause & INT_TX_END) { | ||
1698 | int i; | ||
1699 | |||
1700 | wrl(mp, INT_CAUSE(mp->port_num), ~(int_cause & INT_TX_END)); | ||
1701 | for (i = 0; i < 8; i++) { | ||
1702 | struct tx_queue *txq = mp->txq + i; | ||
1703 | if (txq->tx_desc_count && !((txq_active >> i) & 1)) | ||
1704 | txq_enable(txq); | ||
1705 | } | ||
1706 | } | ||
1707 | |||
1708 | /* | ||
1709 | * Enough space again in the primary TX queue for a full packet? | ||
1710 | */ | ||
1711 | if (int_cause_ext & INT_EXT_TX) { | ||
1712 | struct tx_queue *txq = mp->txq + mp->txq_primary; | ||
1713 | __txq_maybe_wake(txq); | ||
1689 | } | 1714 | } |
1690 | 1715 | ||
1691 | return IRQ_HANDLED; | 1716 | return IRQ_HANDLED; |
@@ -1869,7 +1894,7 @@ static int mv643xx_eth_open(struct net_device *dev) | |||
1869 | wrl(mp, INT_MASK_EXT(mp->port_num), | 1894 | wrl(mp, INT_MASK_EXT(mp->port_num), |
1870 | INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); | 1895 | INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); |
1871 | 1896 | ||
1872 | wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT); | 1897 | wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_EXT); |
1873 | 1898 | ||
1874 | return 0; | 1899 | return 0; |
1875 | 1900 | ||
@@ -2005,7 +2030,7 @@ static void mv643xx_eth_netpoll(struct net_device *dev) | |||
2005 | 2030 | ||
2006 | mv643xx_eth_irq(dev->irq, dev); | 2031 | mv643xx_eth_irq(dev->irq, dev); |
2007 | 2032 | ||
2008 | wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_CAUSE_EXT); | 2033 | wrl(mp, INT_MASK(mp->port_num), INT_TX_END | INT_RX | INT_CAUSE_EXT); |
2009 | } | 2034 | } |
2010 | #endif | 2035 | #endif |
2011 | 2036 | ||