From: Jun S. <ju...@us...> - 2001-11-08 01:46:46
|
Update of /cvsroot/linux-mips/linux/drivers/net In directory usw-pr-cvs1:/tmp/cvs-serv20691/drivers/net Modified Files: nec_candy.c Log Message: Fix tx stall problem in ether driver. Update irq dispatching code. Index: nec_candy.c =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/net/nec_candy.c,v retrieving revision 1.3 retrieving revision 1.4 diff -u -d -r1.3 -r1.4 --- nec_candy.c 2001/10/10 23:26:31 1.3 +++ nec_candy.c 2001/11/08 01:46:43 1.4 @@ -98,6 +98,7 @@ #define WORKAROUND_E7_AFCE #define WORKAROUND_E10_PRM_AMC #define WORKAROUND_E13_TXFC +#define WORKAROUND_E8_TX_STALL /*********************************************************************** * Candy.h macros @@ -824,6 +825,7 @@ } +#if defined(DEBUG_NEC_CANDY) /* * dump all MII registers */ @@ -844,6 +846,7 @@ printk("\t%-10s = 0x%04x\n", "MII_LBREMR", MIIread(dev, MII_LBREMR)); printk("\t%-10s = 0x%04x\n", "MII_PAR", MIIread(dev, MII_PAR)); } +#endif /* * SetMacAddr: set MAC address @@ -1082,6 +1085,84 @@ return; } +/* + * This is called when system boots up and/or after ether chip is reset. + */ +static void +candy_hw_init(struct net_device *dev) +{ + struct candy_private *pp = (struct candy_private *)dev->priv; + candy_regs *p = pp->regs; + + /* + * software reset + */ + candy_out(p->ccr, SRT); + candy_in(p->isr); + + /* + * MAC software reset + */ + candy_out(p->macc2, MCRST); + candy_out(p->macc2, 0x0); + + /* + * MII software reset + */ + candy_out(p->miic, MISRT); + candy_out(p->miic, 0); + + /* + * PHY reset + */ + udelay(500); + MIIwrite(dev, MII_CONTROL, MIICNTL_RESET ); + if ( ! MIIpoll(dev, MII_CONTROL, MIICNTL_RESET, 0, NULL)) { + printk(KERN_WARNING "PHY reset error.\n" ); + } + + /* + * initialize MAC + */ +#ifdef WORKAROUND_E13_TXFC + candy_out(p->macc1, RXFC | CRCEN | PADEN ); +#else + candy_out(p->macc1, TXFC | RXFC | CRCEN | PADEN ); +#endif + candy_out(p->macc2, APD ); + candy_out(p->ipgt, IPGT ); + candy_out(p->ipgr, IPGR1 | IPGR2 ); + candy_out(p->clrt, LCOLW | RETRY ); + candy_out(p->lmax, MAXF ); + SetMacAddr(dev, dev->dev_addr); + candy_out(p->vltp, VLTP ); + candy_out(p->miic, CLKS66 ); + + /* + * set media type (default: auto-negotiation) + */ + SetMediaType(dev, MEDIA_AUTO); + + /* + * initialize DMA / FIFO + */ +#ifdef WORKAROUND_E7_AFCE + candy_out(p->txcfg, DTBS4); +#else + candy_out(p->txcfg, DTBS4 | AFCE); +#endif + candy_out(p->txcfg, DTBS4 ); + candy_out(p->txfc, TPTV | TX_DRTH | TX_FLTH ); + candy_out(p->rxcfg, DRBS4 ); + candy_out(p->rxfc, UWM | LWM | RX_DRTH16W ); + candy_out(p->rxpd, AL ); + + /* + * disable all interrupts until dev is opened later + */ + candy_out(p->msr, 0); +} + static void candy_down(struct net_device *dev) { @@ -1219,6 +1300,73 @@ candy_out(p->ht2, ht2); } +/* + * Apparently candy tx can stall due to various reasons. + * This routine will attempt to recover from tx stall. + */ +static void +candy_error_recover(struct net_device *dev) +{ + struct candy_private *pp = (struct candy_private *)dev->priv; + candy_regs *p = pp->regs; + + spin_lock(&pp->lock); + +#if 0 /* solution 1 */ + candy_close(dev); + candy_hw_init(dev); + candy_open(dev); + + /* this flag is not set by candy_open(). */ + dev->flags |= IFF_UP; +#endif + +#if 1 /* solution 2 */ + netif_stop_queue(dev); + + candy_down(dev); + candy_hw_init(dev); + candy_up(dev); + + netif_wake_queue(dev); + + /* restart transmitting */ + pp->tx_stop = pp->tx_tail; + candy_out(p->txdp, virt_to_phys(&pp->tx_ring[pp->tx_head])); +#endif + + spin_unlock(&pp->lock); +} + +#if defined(WORKAROUND_E8_TX_STALL) +/* + * This implements the workaround described for E-8 + */ +static void +tx_stall_recover(struct net_device *dev) +{ + struct candy_private *pp = (struct candy_private *)dev->priv; + candy_regs *p = pp->regs; + + /* E-8 bug only happens when receiving is on */ + if ((candy_in(p->macc1) & SRXEN) && (candy_in(p->rxcfg) & RXE)) { + + candy_clear_macc1_bits(p->macc1, SRXEN); + candy_clear_bits(p->rxcfg, RXE); + + udelay(20); + + candy_out(p->txdp, virt_to_phys(&pp->tx_ring[pp->tx_head])); + + candy_set_bits(p->rxcfg, RXE); + candy_set_macc1_bits(p->macc1, SRXEN); + + } else { + candy_out(p->txdp, virt_to_phys(&pp->tx_ring[pp->tx_head])); + } +} +#endif + /*********************************************************************** * hardware-independent helper routine *********************************************************************** @@ -1259,6 +1407,7 @@ pp->rx_disable = 0; } +#if defined(DEBUG_VERBOSE_NEC_CANDY) static void candy_check_intr(ulong isr) { @@ -1305,7 +1454,8 @@ if (j & isr) printk("\t%s\n", intr_name[i]); } } - +#endif + static /* inline */ void reclaim_one_rx_desc(struct net_device *dev, char *buf) { @@ -1572,6 +1722,10 @@ dev->name, pp->tx_head, dp->status, isr); #endif +#if defined(WORKAROUND_E8_TX_STALL) + tx_stall_recover(dev); +#endif + pp->stats.tx_errors ++; pp->stats.tx_aborted_errors ++; if (dp->status & TUDR) pp->stats.tx_fifo_errors ++; @@ -1607,7 +1761,6 @@ /* how about checking OWN bit */ if ( !(dp->status & OWN)) { - /* this normally should rarely happen */ DEBUG_VERBOSE(printk(KERN_WARNING "%s: found pkt being sent. Break the loop.\n", dev->name)); break; } @@ -1615,6 +1768,8 @@ /* handle error */ if ( ! (dp->status & TOK)) { handle_tx_error(dev, dp, isr); + /* transmitter should have stopped */ + break; } /* reclaim the descriptor */ @@ -1706,9 +1861,7 @@ if (isr & BUSERR) { printk(KERN_ERR "%s: bus error ... resetting\n", dev->name); - dev->flags &= ~(IFF_UP | IFF_RUNNING); - candy_close(dev); - candy_open(dev); + candy_error_recover(dev); return; } @@ -1812,6 +1965,8 @@ dev->name); pp->stats.tx_dropped ++; spin_unlock_irqrestore(&pp->lock, flags); + /* why the queue was not stopped before we get here? */ + netif_stop_queue(dev); return 1; } @@ -1884,20 +2039,13 @@ static void candy_tx_timeout(struct net_device *dev) { - unsigned long flags; struct candy_private *pp = (struct candy_private *)dev->priv; printk(KERN_ERR "%s : tx_timeout.\n", dev->name); pp->stats.tx_errors ++; - - spin_lock_irqsave(&pp->lock, flags); - candy_down(dev); - candy_up(dev); - - netif_wake_queue(dev); - spin_unlock_irqrestore(&pp->lock, flags); + candy_error_recover(dev); } static void __init @@ -1986,50 +2134,6 @@ /* TODO: maybe we want to make sure the chip is there */ /* - * software reset - */ - candy_out(p->ccr, SRT); - candy_in(p->isr); - - /* - * MAC software reset - */ - candy_out(p->macc2, MCRST); - candy_out(p->macc2, 0x0); - - /* - * MII software reset - */ - candy_out(p->miic, MISRT); - candy_out(p->miic, 0); - - /* - * PHY reset - */ - udelay(500); - MIIwrite(dev, MII_CONTROL, MIICNTL_RESET ); - if ( ! MIIpoll(dev, MII_CONTROL, MIICNTL_RESET, 0, NULL)) { - printk(KERN_WARNING "PHY reset error.\n" ); - } - - /* - * initialize MAC - */ -#ifdef WORKAROUND_E13_TXFC - candy_out(p->macc1, RXFC | CRCEN | PADEN ); -#else - candy_out(p->macc1, TXFC | RXFC | CRCEN | PADEN ); -#endif - candy_out(p->macc2, APD ); - candy_out(p->ipgt, IPGT ); - candy_out(p->ipgr, IPGR1 | IPGR2 ); - candy_out(p->clrt, LCOLW | RETRY ); - candy_out(p->lmax, MAXF ); - SetMacAddr(dev, mac_addr); - candy_out(p->vltp, VLTP ); - candy_out(p->miic, CLKS66 ); - - /* * zero out counters */ candy_out(p->rbyt, 0); @@ -2072,30 +2176,8 @@ candy_out(p->tncl, 0); candy_out(p->tcse, 0); candy_out(p->time, 0); - - /* - * set media type (default: auto-negotiation) - */ - SetMediaType(dev, MEDIA_AUTO); - - /* - * initialize DMA / FIFO - */ -#ifdef WORKAROUND_E7_AFCE - candy_out(p->txcfg, DTBS4); -#else - candy_out(p->txcfg, DTBS4 | AFCE); -#endif - candy_out(p->txcfg, DTBS4 ); - candy_out(p->txfc, TPTV | TX_DRTH | TX_FLTH ); - candy_out(p->rxcfg, DRBS4 ); - candy_out(p->rxfc, UWM | LWM | RX_DRTH16W ); - candy_out(p->rxpd, AL ); - /* - * disable all interrupts until dev is opened later - */ - candy_out(p->msr, 0); + candy_hw_init(dev); return; |