bugfix for LC82C168 tulip hang
PATCH 1/1 bugfix for LC82C168 tulip hang
I just found a fatal flaw in the tulip driver for the LC82C168 (pnic) chipset:
The driver assumes that CSR11 is a hardware timer, but the LC82C168 doesn't have that timer :-(
Thus no callback, and nic is hung.
Jeff, could you include the patch into your tree and forward it to Alan?
The patch is against 2.4.1, it fixes all pnic bugs I'm aware of:
- missing update of the FastEthernet bit in check_duplex()
- do not start the internal autonegotiation if an mii is connected, that prevents successful communication with the mii.
- hw timer doesn't exist
--
Manfred
// $Header$
// Kernel Version:
// VERSION = 2 // PATCHLEVEL = 4 // SUBLEVEL = 1 // EXTRAVERSION =
drivers/net/tulip/interrupt.c +26 -11
@@ -23,7 +23,7 @@ -static int tulip_refill_rx (struct net_device *dev) +int tulip_refill_rx (struct net_device *dev) {struct tulip_private *tp = (struct tulip_private *)dev->priv;int entry;
@@ -332,10 +332,17 @@/* Josip Loncaric at ICASE did extensive experimentation to develop a good interrupt mitigation setting.*/ outl (0x8b240000 , ioaddr +CSR11 ); - }else { + }else if (tp->chip_id ==LC82C168 ) { +/* the LC82C168 doesn't have a hw timer.*/ +outl (0x00 , ioaddr +CSR7 ); + tp->ttimer =1 ; +mod_timer (&tp->timer,RUN_AT (HZ /50 )); + }else {/* Mask all interrupting sources, set timer to re-enable. */ outl (((~csr5) &0x0001ebef ) | AbnormalIntr | TimerInt, ioaddr +CSR7 ); +printk (KERN_ERR "interrupts got masked, new csr7 is %d.\n" , + ((~csr5) &0x0001ebef ) | AbnormalIntr | TimerInt);outl (0x0012 , ioaddr +CSR11 ); }break ;
@@ -353,16 +360,24 @@/* check if the card is in suspend mode */ entry = tp->dirty_rx %RX_RING_SIZE ;if (tp->rx_buffers[entry].skb ==NULL ) { -if (tulip_debug >1 ) -printk (KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n" , dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx); -if (tp->ttimer ==0 || (inl (ioaddr +CSR11 ) &0xffff ) ==0 ) { -if (tulip_debug >1 ) -printk (KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n" , dev->name, tp->nir); -outl (tulip_tbl[tp->chip_id].valid_intrs | TimerInt, - ioaddr +CSR7 ); -outl (TimerInt, ioaddr +CSR5 ); -outl (12 , ioaddr +CSR11 ); +if (tulip_debug >1 ) { +printk (KERN_WARNING "%s: in rx suspend mode: (%lu) (tp->cur_rx = %u, ttimer = %d, rx = %d) go/stay in suspend mode\n" , + dev->name, tp->nir, tp->cur_rx, tp->ttimer, rx); + } +if (tp->chip_id ==LC82C168 ) { +outl (0x00 , ioaddr +CSR7 ); tp->ttimer =1 ; +mod_timer (&tp->timer,RUN_AT (HZ /50 )); + }else { +if (tp->ttimer ==0 || (inl (ioaddr +CSR11 ) &0xffff ) ==0 ) { +if (tulip_debug >1 ) +printk (KERN_WARNING "%s: in rx suspend mode: (%lu) set timer\n" , dev->name, tp->nir); +outl (tulip_tbl[tp->chip_id].valid_intrs | TimerInt, + ioaddr +CSR7 ); +outl (TimerInt, ioaddr +CSR5 ); +outl (12 , ioaddr +CSR11 ); + tp->ttimer =1 ; + } } }
drivers/net/tulip/media.c +56 -0
59 lines not on this page · raw message
drivers/net/tulip/pnic.c +24 -3
@@ -62,6 +62,11 @@ dev->name, phy_reg, csr5);if (inl (ioaddr +CSR5 ) & TPLnkFail) {outl ((inl (ioaddr +CSR7 ) & ~TPLnkFail) | TPLnkPass, ioaddr +CSR7 ); +/* If we use an external MII, then we mustn't use the +* internal negotiation. +*/ +if (tulip_media_cap[dev->if_port] & MediaIsMII) +return ;if (! tp->nwayset || jiffies - dev->trans_start >1 *HZ ) { tp->csr6 =0x00420000 | (tp->csr6 &0x0000fdff );tulip_outl_csr (tp, tp->csr6,CSR6 );
@@ -70,11 +75,18 @@ dev->trans_start = jiffies; } }else if (inl (ioaddr +CSR5 ) & TPLnkPass) { -pnic_do_nway (dev); +if (tulip_media_cap[dev->if_port] & MediaIsMII) { +spin_lock (&tp->lock); +pnic_check_duplex (dev); +spin_unlock (&tp->lock); + }else { +pnic_do_nway (dev); + }outl ((inl (ioaddr +CSR7 ) & ~TPLnkPass) | TPLnkFail, ioaddr +CSR7 ); } } +int tulip_refill_rx (struct net_device *dev);void pnic_timer (unsigned long data) {
@@ -82,10 +94,19 @@struct tulip_private *tp = (struct tulip_private *)dev->priv;long ioaddr = dev->base_addr;int next_tick =60 *HZ ; - + +if (tp->ttimer !=0 ) { +disable_irq (dev->irq); +tulip_refill_rx (dev); +enable_irq (dev->irq); +outl (tulip_tbl[tp->chip_id].valid_intrs, ioaddr +CSR7 ); + tp->ttimer =0 ; + }if (tulip_media_cap[dev->if_port] & MediaIsMII) { -if (tulip_check_duplex (dev) >0 ) +spin_lock_irq (&tp->lock); +if (pnic_check_duplex (dev) >0 ) next_tick =3 *HZ ; +spin_unlock_irq (&tp->lock); }else {int csr12 =inl (ioaddr +CSR12 );int new_csr6 = tp->csr6 & ~0x40C40200 ;