Re: problem with via-rhine driver and VT6103 chipset

From: Roger Luethi (rl@hellgate.ch)
Date: Sat Jul 20 2002 - 11:43:21 EST


On Sat, 20 Jul 2002 18:20:44 +0200, damsnet@free.fr wrote:
> I would like to report a bug, appearing in kernels 2.4.18
> to 2.4.19-rc3 (I have not tested with previous ones).
>
> It concerns the VIA-Rhine (ethernet) driver. I use it with
> a VT6103 ethernet adaptator, which seems not to be supported
> by the driver, but almost works.
>
> When I use this driver in 10Mbps mode (not 100Mbps), it resets the
> ethernet card every few seconds and at the ends I have to reboot,
> because the card does not work anymore. These are the messages
> I see with repetition:
>
> NETDEV WATCHDOG: eth0: transmit timed out
> eth0: Transmit timed out, status 0000, PHY status 786d, resetting...
> NETDEV WATCHDOG: eth0: transmit timed out
> eth0: Transmit timed out, status 0000, PHY status 786d, resetting...
> [etc, etc, ...]

Try a recent ac kernel (2.4.19rc1-ac6 or later) or the patch below.

Roger

--- drivers/net/bk_11Jul.c Mon Jul 15 14:53:27 2002
+++ drivers/net/via-rhine.c Mon Jul 15 14:53:53 2002
@@ -93,7 +93,10 @@
         - transmit frame queue message is off by one - fixed
         - adds IntrNormalSummary to "Something Wicked" exclusion list
           so normal interrupts will not trigger the message (src: Donald Becker)
- (Roger Lahti)
+ (Roger Luethi)
+ - show confused chip where to continue after Tx error
+ - location of collision counter is chip specific
+ - allow selecting backoff algorithm (module parameter)
         - cosmetic cleanups, remove 3 unused members of struct netdev_private
 
 */
@@ -113,6 +116,9 @@ static int max_interrupt_work = 20;
    Setting to > 1518 effectively disables this feature. */
 static int rx_copybreak;
 
+/* Select a backoff algorithm (Ethernet capture effect) */
+static int backoff;
+
 /* Used to pass the media type, etc.
    Both 'options[]' and 'full_duplex[]' should exist for driver
    interoperability.
@@ -215,11 +221,13 @@ MODULE_LICENSE("GPL");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(backoff, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM_DESC(max_interrupt_work, "VIA Rhine maximum events handled per interrupt");
 MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)");
 MODULE_PARM_DESC(rx_copybreak, "VIA Rhine copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(backoff, "VIA Rhine: Bits 0-3: backoff algorithm");
 MODULE_PARM_DESC(options, "VIA Rhine: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)");
 
@@ -236,7 +244,8 @@ II. Board-specific settings
 Boards with this chip are functional only in a bus-master PCI slot.
 
 Many operational settings are loaded from the EEPROM to the Config word at
-offset 0x78. This driver assumes that they are correct.
+offset 0x78. For most of these settings, this driver assumes that they are
+correct.
 If this driver is compiled to use PCI memory space operations the EEPROM
 must be configured to enable memory ops.
 
@@ -388,9 +397,10 @@ enum register_offsets {
         StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC,
 };
 
-/* Bits in ConfigD (select backoff algorithm (Ethernet capture effect)) */
+/* Bits in ConfigD */
 enum backoff_bits {
- BackOpt=0x01, BackAMD=0x02, BackDEC=0x04, BackRandom=0x08
+ BackOptional=0x01, BackModify=0x02,
+ BackCaptureEffect=0x04, BackRandom=0x08
 };
 
 #ifdef USE_MEM
@@ -404,7 +414,7 @@ int mmio_verify_registers[] = {
 /* Bits in the interrupt status/mask registers. */
 enum intr_status_bits {
         IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
- IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010,
+ IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0010,
         IntrPCIErr=0x0040,
         IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200,
         IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
@@ -430,24 +440,27 @@ enum mii_status_bits {
 /* The Rx and Tx buffer descriptors. */
 struct rx_desc {
         s32 rx_status;
- u32 desc_length;
+ u32 desc_length; /* Chain flag, Buffer/frame length */
         u32 addr;
         u32 next_desc;
 };
 struct tx_desc {
         s32 tx_status;
- u32 desc_length;
+ u32 desc_length; /* Chain flag, Tx Config, Frame length */
         u32 addr;
         u32 next_desc;
 };
 
+/* Initial value for tx_desc.desc_length, Buffer size goes to bits 0-10 */
+#define TXDESC 0x00e08000
+
 enum rx_status_bits {
         RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
 };
 
-/* Bits in *_desc.status */
+/* Bits in *_desc.*_status */
 enum desc_status_bits {
- DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,
+ DescOwn=0x80000000
 };
 
 /* Bits in ChipCmd. */
@@ -519,6 +532,7 @@ static struct net_device_stats *via_rhin
 static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int via_rhine_close(struct net_device *dev);
 static inline void clear_tally_counters(long ioaddr);
+static inline void via_restart_tx(struct net_device *dev);
 
 static void wait_for_reset(struct net_device *dev, int chip_id, char *name)
 {
@@ -705,6 +719,11 @@ static int __devinit via_rhine_init_one
                 writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA);
         }
 
+ /* Select backoff algorithm */
+ if (backoff)
+ writeb(readb(ioaddr + ConfigD) & (0xF0 | backoff),
+ ioaddr + ConfigD);
+
         dev->irq = pdev->irq;
 
         np = dev->priv;
@@ -939,7 +958,7 @@ static void alloc_tbufs(struct net_devic
         for (i = 0; i < TX_RING_SIZE; i++) {
                 np->tx_skbuff[i] = 0;
                 np->tx_ring[i].tx_status = 0;
- np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+ np->tx_ring[i].desc_length = cpu_to_le32(TXDESC);
                 next += sizeof(struct tx_desc);
                 np->tx_ring[i].next_desc = cpu_to_le32(next);
                 np->tx_buf[i] = &np->tx_bufs[i * PKT_BUF_SZ];
@@ -955,7 +974,7 @@ static void free_tbufs(struct net_device
 
         for (i = 0; i < TX_RING_SIZE; i++) {
                 np->tx_ring[i].tx_status = 0;
- np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000);
+ np->tx_ring[i].desc_length = cpu_to_le32(TXDESC);
                 np->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
                 if (np->tx_skbuff[i]) {
                         if (np->tx_skbuff_dma[i]) {
@@ -980,7 +999,7 @@ static void init_registers(struct net_de
                 writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
         /* Initialize other registers. */
- writew(0x0006, ioaddr + PCIBusConfig); /* Store & forward */
+ writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */
         /* Configure initial FIFO thresholds. */
         writeb(0x20, ioaddr + TxConfig);
         np->tx_thresh = 0x20;
@@ -995,8 +1014,9 @@ static void init_registers(struct net_de
         via_rhine_set_rx_mode(dev);
 
         /* Enable interrupts by setting the interrupt mask. */
- writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped|
- IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow |
+ IntrRxDropped | IntrRxNoBuf | IntrTxAborted |
+ IntrTxDone | IntrTxError | IntrTxUnderrun |
                    IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange,
                    ioaddr + IntrEnable);
 
@@ -1239,7 +1259,7 @@ static int via_rhine_start_tx(struct sk_
         }
 
         np->tx_ring[entry].desc_length =
- cpu_to_le32(0x00E08000 | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
+ cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN));
 
         /* lock eth irq */
         spin_lock_irq (&np->lock);
@@ -1291,13 +1311,14 @@ static void via_rhine_interrupt(int irq,
                                                    IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
                         via_rhine_rx(dev);
 
- if (intr_status & (IntrTxDone | IntrTxAbort | IntrTxUnderrun |
+ if (intr_status & (IntrTxDone | IntrTxError | IntrTxUnderrun |
                                                    IntrTxAborted))
                         via_rhine_tx(dev);
 
                 /* Abnormal error summary/uncommon events handlers. */
                 if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
- IntrStatsMax | IntrTxAbort | IntrTxUnderrun))
+ IntrStatsMax | IntrTxError | IntrTxAborted |
+ IntrTxUnderrun))
                         via_rhine_error(dev, intr_status);
 
                 if (--boguscnt < 0) {
@@ -1309,7 +1330,7 @@ static void via_rhine_interrupt(int irq,
         }
 
         if (debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+ printk(KERN_DEBUG "%s: exiting interrupt, status=%4.4x.\n",
                            dev->name, readw(ioaddr + IntrStatus));
 }
 
@@ -1325,11 +1346,11 @@ static void via_rhine_tx(struct net_devi
         /* find and cleanup dirty tx descriptors */
         while (np->dirty_tx != np->cur_tx) {
                 txstatus = le32_to_cpu(np->tx_ring[entry].tx_status);
- if (txstatus & DescOwn)
- break;
                 if (debug > 6)
                         printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n",
                                    entry, txstatus);
+ if (txstatus & DescOwn)
+ break;
                 if (txstatus & 0x8000) {
                         if (debug > 1)
                                 printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
@@ -1339,10 +1360,22 @@ static void via_rhine_tx(struct net_devi
                         if (txstatus & 0x0200) np->stats.tx_window_errors++;
                         if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
                         if (txstatus & 0x0080) np->stats.tx_heartbeat_errors++;
- if (txstatus & 0x0002) np->stats.tx_fifo_errors++;
+ if (((np->chip_id == VT86C100A) && txstatus & 0x0002) ||
+ (txstatus & 0x0800) || (txstatus & 0x1000)) {
+ np->stats.tx_fifo_errors++;
+ np->tx_ring[entry].tx_status = cpu_to_le32(DescOwn);
+ break; /* Keep the skb - we try again */
+ }
                         /* Transmitter restarted in 'abnormal' handler. */
                 } else {
- np->stats.collisions += (txstatus >> 3) & 15;
+ if (np->chip_id == VT86C100A)
+ np->stats.collisions += (txstatus >> 3) & 0x0F;
+ else
+ np->stats.collisions += txstatus & 0x0F;
+ if (debug > 6)
+ printk(KERN_DEBUG "collisions: %1.1x:%1.1x\n",
+ (txstatus >> 3) & 0xF,
+ txstatus & 0xF);
                         np->stats.tx_bytes += np->tx_skbuff[entry]->len;
                         np->stats.tx_packets++;
                 }
@@ -1478,6 +1511,17 @@ static void via_rhine_rx(struct net_devi
         writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
 }
 
+static inline void via_restart_tx(struct net_device *dev) {
+ struct netdev_private *np = dev->priv;
+ int entry = np->dirty_tx % TX_RING_SIZE;
+
+ /* We know better than the chip where it should continue */
+ writel(np->tx_ring_dma + entry * sizeof(struct tx_desc),
+ dev->base_addr + TxRingPtr);
+
+ writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+}
+
 static void via_rhine_error(struct net_device *dev, int intr_status)
 {
         struct netdev_private *np = dev->priv;
@@ -1503,19 +1547,23 @@ static void via_rhine_error(struct net_d
                 np->stats.rx_missed_errors += readw(ioaddr + RxMissed);
                 clear_tally_counters(ioaddr);
         }
- if (intr_status & IntrTxAbort) {
- /* Stats counted in Tx-done handler, just restart Tx. */
- writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd);
+ if (intr_status & IntrTxError) {
+ if (debug > 1)
+ printk(KERN_INFO "%s: Abort %4.4x, frame dropped.\n",
+ dev->name, intr_status);
+ via_restart_tx(dev);
         }
         if (intr_status & IntrTxUnderrun) {
                 if (np->tx_thresh < 0xE0)
                         writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
                 if (debug > 1)
- printk(KERN_INFO "%s: Transmitter underrun, increasing Tx "
- "threshold setting to %2.2x.\n", dev->name, np->tx_thresh);
+ printk(KERN_INFO "%s: Transmitter underrun, Tx "
+ "threshold now %2.2x.\n",
+ dev->name, np->tx_thresh);
+ via_restart_tx(dev);
         }
         if (intr_status & ~( IntrLinkChange | IntrStatsMax |
- IntrTxAbort | IntrTxAborted | IntrNormalSummary)) {
+ IntrTxError | IntrTxAborted | IntrNormalSummary)) {
                 if (debug > 1)
                         printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
                            dev->name, intr_status);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue Jul 23 2002 - 22:00:33 EST