summaryrefslogtreecommitdiffstats
path: root/drivers/net/macb.c
diff options
context:
space:
mode:
authorRichard Röjfors <richard.rojfors@endian.se>2009-01-19 06:57:35 +0100
committerDavid S. Miller <davem@davemloft.net>2009-01-20 01:20:16 +0100
commit39eddb4c3970e9aadbc87b8a7cab7b4fefff077f (patch)
tree840836c811ba0b518be07ff92c1d331e16c94212 /drivers/net/macb.c
parentnet/9p: fid->fid is used uninitialized (diff)
downloadlinux-39eddb4c3970e9aadbc87b8a7cab7b4fefff077f.tar.xz
linux-39eddb4c3970e9aadbc87b8a7cab7b4fefff077f.zip
macb: avoid lockup when TGO during underrun
In rare cases when an underrun occur, all macb buffers where consumed and the netif_queue was stopped infinitely. This happens then the TGO (transfer ongoing) bit in the TSR is set (and UND). It seems like clening up after the underrun makes the driver and the macb hardware end up in an inconsistent state. The result of this is that in the following calls to macb_tx no TX buffers are released -> the netif_queue was stopped, and never woken up again. The solution is to disable the transmitter, if TGO is set, before clening up after the underrun, and re-enable the transmitter when the cleaning up is done. Signed-off-by: Richard Röjfors <richard.rojfors@endian.se> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/macb.c')
-rw-r--r--drivers/net/macb.c8
1 files changed, 8 insertions, 0 deletions
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index a04da4ecaa88..f6c4936e2fa8 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -321,6 +321,10 @@ static void macb_tx(struct macb *bp)
printk(KERN_ERR "%s: TX underrun, resetting buffers\n",
bp->dev->name);
+ /* Transfer ongoing, disable transmitter, to avoid confusion */
+ if (status & MACB_BIT(TGO))
+ macb_writel(bp, NCR, macb_readl(bp, NCR) & ~MACB_BIT(TE));
+
head = bp->tx_head;
/*Mark all the buffer as used to avoid sending a lost buffer*/
@@ -343,6 +347,10 @@ static void macb_tx(struct macb *bp)
}
bp->tx_head = bp->tx_tail = 0;
+
+ /* Enable the transmitter again */
+ if (status & MACB_BIT(TGO))
+ macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TE));
}
if (!(status & MACB_BIT(COMP)))