Commit 5796cf21 authored by Roger Luethi's avatar Roger Luethi Committed by Jeff Garzik

[netdrvr via-rhine] merge bug fixes and new features from 2.4.x kernel:

- better Tx error handling on certain chips
- handle chip-specific collision counters
- use MII lib
- selectable backoff algorithm
parent 41b3a9f2
...@@ -93,14 +93,19 @@ ...@@ -93,14 +93,19 @@
- transmit frame queue message is off by one - fixed - transmit frame queue message is off by one - fixed
- adds IntrNormalSummary to "Something Wicked" exclusion list - adds IntrNormalSummary to "Something Wicked" exclusion list
so normal interrupts will not trigger the message (src: Donald Becker) so normal interrupts will not trigger the message (src: Donald Becker)
(Roger Lahti) (Roger Luethi)
- cosmetic cleanups, remove 3 unused members of struct netdev_private - show confused chip where to continue after Tx error
- location of collision counter is chip specific
- allow selecting backoff algorithm (module parameter)
LK1.1.15 (jgarzik):
- Use new MII lib helper generic_mii_ioctl
*/ */
#define DRV_NAME "via-rhine" #define DRV_NAME "via-rhine"
#define DRV_VERSION "1.1.14" #define DRV_VERSION "1.1.15"
#define DRV_RELDATE "May-3-2002" #define DRV_RELDATE "November-22-2002"
/* A few user-configurable values. /* A few user-configurable values.
...@@ -113,6 +118,9 @@ static int max_interrupt_work = 20; ...@@ -113,6 +118,9 @@ static int max_interrupt_work = 20;
Setting to > 1518 effectively disables this feature. */ Setting to > 1518 effectively disables this feature. */
static int rx_copybreak; static int rx_copybreak;
/* Select a backoff algorithm (Ethernet capture effect) */
static int backoff;
/* Used to pass the media type, etc. /* Used to pass the media type, etc.
Both 'options[]' and 'full_duplex[]' should exist for driver Both 'options[]' and 'full_duplex[]' should exist for driver
interoperability. interoperability.
...@@ -215,11 +223,13 @@ MODULE_LICENSE("GPL"); ...@@ -215,11 +223,13 @@ MODULE_LICENSE("GPL");
MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(max_interrupt_work, "i");
MODULE_PARM(debug, "i"); MODULE_PARM(debug, "i");
MODULE_PARM(rx_copybreak, "i"); MODULE_PARM(rx_copybreak, "i");
MODULE_PARM(backoff, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(full_duplex, "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(max_interrupt_work, "VIA Rhine maximum events handled per interrupt");
MODULE_PARM_DESC(debug, "VIA Rhine debug level (0-7)"); 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(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(options, "VIA Rhine: Bits 0-3: media type, bit 17: full duplex");
MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)"); MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)");
...@@ -236,7 +246,8 @@ II. Board-specific settings ...@@ -236,7 +246,8 @@ II. Board-specific settings
Boards with this chip are functional only in a bus-master PCI slot. 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 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 If this driver is compiled to use PCI memory space operations the EEPROM
must be configured to enable memory ops. must be configured to enable memory ops.
...@@ -388,9 +399,10 @@ enum register_offsets { ...@@ -388,9 +399,10 @@ enum register_offsets {
StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC, StickyHW=0x83, WOLcrClr=0xA4, WOLcgClr=0xA7, PwrcsrClr=0xAC,
}; };
/* Bits in ConfigD (select backoff algorithm (Ethernet capture effect)) */ /* Bits in ConfigD */
enum backoff_bits { enum backoff_bits {
BackOpt=0x01, BackAMD=0x02, BackDEC=0x04, BackRandom=0x08 BackOptional=0x01, BackModify=0x02,
BackCaptureEffect=0x04, BackRandom=0x08
}; };
#ifdef USE_MEM #ifdef USE_MEM
...@@ -404,7 +416,7 @@ int mmio_verify_registers[] = { ...@@ -404,7 +416,7 @@ int mmio_verify_registers[] = {
/* Bits in the interrupt status/mask registers. */ /* Bits in the interrupt status/mask registers. */
enum intr_status_bits { enum intr_status_bits {
IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020, IntrRxDone=0x0001, IntrRxErr=0x0004, IntrRxEmpty=0x0020,
IntrTxDone=0x0002, IntrTxAbort=0x0008, IntrTxUnderrun=0x0010, IntrTxDone=0x0002, IntrTxError=0x0008, IntrTxUnderrun=0x0010,
IntrPCIErr=0x0040, IntrPCIErr=0x0040,
IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200, IntrStatsMax=0x0080, IntrRxEarly=0x0100, IntrMIIChange=0x0200,
IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000, IntrRxOverflow=0x0400, IntrRxDropped=0x0800, IntrRxNoBuf=0x1000,
...@@ -430,24 +442,27 @@ enum mii_status_bits { ...@@ -430,24 +442,27 @@ enum mii_status_bits {
/* The Rx and Tx buffer descriptors. */ /* The Rx and Tx buffer descriptors. */
struct rx_desc { struct rx_desc {
s32 rx_status; s32 rx_status;
u32 desc_length; u32 desc_length; /* Chain flag, Buffer/frame length */
u32 addr; u32 addr;
u32 next_desc; u32 next_desc;
}; };
struct tx_desc { struct tx_desc {
s32 tx_status; s32 tx_status;
u32 desc_length; u32 desc_length; /* Chain flag, Tx Config, Frame length */
u32 addr; u32 addr;
u32 next_desc; u32 next_desc;
}; };
/* Initial value for tx_desc.desc_length, Buffer size goes to bits 0-10 */
#define TXDESC 0x00e08000
enum rx_status_bits { enum rx_status_bits {
RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F
}; };
/* Bits in *_desc.status */ /* Bits in *_desc.*_status */
enum desc_status_bits { enum desc_status_bits {
DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000, DescOwn=0x80000000
}; };
/* Bits in ChipCmd. */ /* Bits in ChipCmd. */
...@@ -519,6 +534,7 @@ static struct net_device_stats *via_rhine_get_stats(struct net_device *dev); ...@@ -519,6 +534,7 @@ static struct net_device_stats *via_rhine_get_stats(struct net_device *dev);
static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int via_rhine_close(struct net_device *dev); static int via_rhine_close(struct net_device *dev);
static inline void clear_tally_counters(long ioaddr); 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) static void wait_for_reset(struct net_device *dev, int chip_id, char *name)
{ {
...@@ -705,6 +721,11 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev, ...@@ -705,6 +721,11 @@ static int __devinit via_rhine_init_one (struct pci_dev *pdev,
writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA); writeb(readb(ioaddr + ConfigA) & 0xFE, ioaddr + ConfigA);
} }
/* Select backoff algorithm */
if (backoff)
writeb(readb(ioaddr + ConfigD) & (0xF0 | backoff),
ioaddr + ConfigD);
dev->irq = pdev->irq; dev->irq = pdev->irq;
np = dev->priv; np = dev->priv;
...@@ -941,7 +962,7 @@ static void alloc_tbufs(struct net_device* dev) ...@@ -941,7 +962,7 @@ static void alloc_tbufs(struct net_device* dev)
for (i = 0; i < TX_RING_SIZE; i++) { for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_skbuff[i] = 0; np->tx_skbuff[i] = 0;
np->tx_ring[i].tx_status = 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); next += sizeof(struct tx_desc);
np->tx_ring[i].next_desc = cpu_to_le32(next); np->tx_ring[i].next_desc = cpu_to_le32(next);
np->tx_buf[i] = &np->tx_bufs[i * PKT_BUF_SZ]; np->tx_buf[i] = &np->tx_bufs[i * PKT_BUF_SZ];
...@@ -957,7 +978,7 @@ static void free_tbufs(struct net_device* dev) ...@@ -957,7 +978,7 @@ static void free_tbufs(struct net_device* dev)
for (i = 0; i < TX_RING_SIZE; i++) { for (i = 0; i < TX_RING_SIZE; i++) {
np->tx_ring[i].tx_status = 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);
np->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */ np->tx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
if (np->tx_skbuff[i]) { if (np->tx_skbuff[i]) {
if (np->tx_skbuff_dma[i]) { if (np->tx_skbuff_dma[i]) {
...@@ -982,7 +1003,7 @@ static void init_registers(struct net_device *dev) ...@@ -982,7 +1003,7 @@ static void init_registers(struct net_device *dev)
writeb(dev->dev_addr[i], ioaddr + StationAddr + i); writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
/* Initialize other registers. */ /* Initialize other registers. */
writew(0x0006, ioaddr + PCIBusConfig); /* Store & forward */ writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */
/* Configure initial FIFO thresholds. */ /* Configure initial FIFO thresholds. */
writeb(0x20, ioaddr + TxConfig); writeb(0x20, ioaddr + TxConfig);
np->tx_thresh = 0x20; np->tx_thresh = 0x20;
...@@ -997,8 +1018,9 @@ static void init_registers(struct net_device *dev) ...@@ -997,8 +1018,9 @@ static void init_registers(struct net_device *dev)
via_rhine_set_rx_mode(dev); via_rhine_set_rx_mode(dev);
/* Enable interrupts by setting the interrupt mask. */ /* Enable interrupts by setting the interrupt mask. */
writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow| IntrRxDropped| writew(IntrRxDone | IntrRxErr | IntrRxEmpty| IntrRxOverflow |
IntrTxDone | IntrTxAbort | IntrTxUnderrun | IntrRxDropped | IntrRxNoBuf | IntrTxAborted |
IntrTxDone | IntrTxError | IntrTxUnderrun |
IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange, IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange,
ioaddr + IntrEnable); ioaddr + IntrEnable);
...@@ -1241,7 +1263,7 @@ static int via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev) ...@@ -1241,7 +1263,7 @@ static int via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev)
} }
np->tx_ring[entry].desc_length = 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 */ /* lock eth irq */
spin_lock_irq (&np->lock); spin_lock_irq (&np->lock);
...@@ -1293,13 +1315,14 @@ static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs ...@@ -1293,13 +1315,14 @@ static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs
IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf)) IntrRxWakeUp | IntrRxEmpty | IntrRxNoBuf))
via_rhine_rx(dev); via_rhine_rx(dev);
if (intr_status & (IntrTxDone | IntrTxAbort | IntrTxUnderrun | if (intr_status & (IntrTxDone | IntrTxError | IntrTxUnderrun |
IntrTxAborted)) IntrTxAborted))
via_rhine_tx(dev); via_rhine_tx(dev);
/* Abnormal error summary/uncommon events handlers. */ /* Abnormal error summary/uncommon events handlers. */
if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange | if (intr_status & (IntrPCIErr | IntrLinkChange | IntrMIIChange |
IntrStatsMax | IntrTxAbort | IntrTxUnderrun)) IntrStatsMax | IntrTxError | IntrTxAborted |
IntrTxUnderrun))
via_rhine_error(dev, intr_status); via_rhine_error(dev, intr_status);
if (--boguscnt < 0) { if (--boguscnt < 0) {
...@@ -1311,7 +1334,7 @@ static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs ...@@ -1311,7 +1334,7 @@ static void via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *rgs
} }
if (debug > 3) 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)); dev->name, readw(ioaddr + IntrStatus));
} }
...@@ -1327,11 +1350,11 @@ static void via_rhine_tx(struct net_device *dev) ...@@ -1327,11 +1350,11 @@ static void via_rhine_tx(struct net_device *dev)
/* find and cleanup dirty tx descriptors */ /* find and cleanup dirty tx descriptors */
while (np->dirty_tx != np->cur_tx) { while (np->dirty_tx != np->cur_tx) {
txstatus = le32_to_cpu(np->tx_ring[entry].tx_status); txstatus = le32_to_cpu(np->tx_ring[entry].tx_status);
if (txstatus & DescOwn)
break;
if (debug > 6) if (debug > 6)
printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n", printk(KERN_DEBUG " Tx scavenge %d status %8.8x.\n",
entry, txstatus); entry, txstatus);
if (txstatus & DescOwn)
break;
if (txstatus & 0x8000) { if (txstatus & 0x8000) {
if (debug > 1) if (debug > 1)
printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
...@@ -1341,10 +1364,22 @@ static void via_rhine_tx(struct net_device *dev) ...@@ -1341,10 +1364,22 @@ static void via_rhine_tx(struct net_device *dev)
if (txstatus & 0x0200) np->stats.tx_window_errors++; if (txstatus & 0x0200) np->stats.tx_window_errors++;
if (txstatus & 0x0100) np->stats.tx_aborted_errors++; if (txstatus & 0x0100) np->stats.tx_aborted_errors++;
if (txstatus & 0x0080) np->stats.tx_heartbeat_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. */ /* Transmitter restarted in 'abnormal' handler. */
} else { } 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_bytes += np->tx_skbuff[entry]->len;
np->stats.tx_packets++; np->stats.tx_packets++;
} }
...@@ -1480,6 +1515,17 @@ static void via_rhine_rx(struct net_device *dev) ...@@ -1480,6 +1515,17 @@ static void via_rhine_rx(struct net_device *dev)
writew(CmdRxDemand | np->chip_cmd, dev->base_addr + ChipCmd); 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) static void via_rhine_error(struct net_device *dev, int intr_status)
{ {
struct netdev_private *np = dev->priv; struct netdev_private *np = dev->priv;
...@@ -1505,19 +1551,23 @@ static void via_rhine_error(struct net_device *dev, int intr_status) ...@@ -1505,19 +1551,23 @@ static void via_rhine_error(struct net_device *dev, int intr_status)
np->stats.rx_missed_errors += readw(ioaddr + RxMissed); np->stats.rx_missed_errors += readw(ioaddr + RxMissed);
clear_tally_counters(ioaddr); clear_tally_counters(ioaddr);
} }
if (intr_status & IntrTxAbort) { if (intr_status & IntrTxError) {
/* Stats counted in Tx-done handler, just restart Tx. */ if (debug > 1)
writew(CmdTxDemand | np->chip_cmd, dev->base_addr + ChipCmd); printk(KERN_INFO "%s: Abort %4.4x, frame dropped.\n",
dev->name, intr_status);
via_restart_tx(dev);
} }
if (intr_status & IntrTxUnderrun) { if (intr_status & IntrTxUnderrun) {
if (np->tx_thresh < 0xE0) if (np->tx_thresh < 0xE0)
writeb(np->tx_thresh += 0x20, ioaddr + TxConfig); writeb(np->tx_thresh += 0x20, ioaddr + TxConfig);
if (debug > 1) if (debug > 1)
printk(KERN_INFO "%s: Transmitter underrun, increasing Tx " printk(KERN_INFO "%s: Transmitter underrun, Tx "
"threshold setting to %2.2x.\n", dev->name, np->tx_thresh); "threshold now %2.2x.\n",
dev->name, np->tx_thresh);
via_restart_tx(dev);
} }
if (intr_status & ~( IntrLinkChange | IntrStatsMax | if (intr_status & ~( IntrLinkChange | IntrStatsMax |
IntrTxAbort | IntrTxAborted | IntrNormalSummary)) { IntrTxError | IntrTxAborted | IntrNormalSummary)) {
if (debug > 1) if (debug > 1)
printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
dev->name, intr_status); dev->name, intr_status);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment