patch-2.2.17 linux/drivers/net/3c59x.c
Next file: linux/drivers/net/Config.in
Previous file: linux/drivers/macintosh/via-pmu.c
Back to the patch index
Back to the overall index
- Lines: 509
- Date:
Mon Sep 4 18:39:18 2000
- Orig file:
v2.2.16/drivers/net/3c59x.c
- Orig date:
Mon Sep 4 18:37:25 2000
diff -u --recursive --new-file v2.2.16/drivers/net/3c59x.c linux/drivers/net/3c59x.c
@@ -42,11 +42,29 @@
- In vortex_error, do_tx_reset and vortex_tx_timeout(Vortex): clear
tbusy and force a BH rerun to better recover from errors.
+ 24Jun00 <2.2.16> andrewm
+ - Better handling of shared interrupts
+ - Reset the transmitter in vortex_error() on both maxcollisions and Tx reclaim error
+ - Split the ISR into vortex_interrupt and boomerang_interrupt. This is
+ to fix once-and-for-all the dubious testing of vortex status bits on
+ boomerang/hurricane/cyclone/tornado NICs.
+ - Fixed crash under OOM during vortex_open() (Mark Hemment)
+ - Fix Rx cessation problem during OOM (help from Mark Hemment)
+
+ 01Aug00 <2.2.17-pre14> andrewm
+ - Added 3c556 support (Fred Maciel)
+
+ 16Aug00 <2.2.17-pre17> andrem
+ - In vortex_error: don't reset the Tx after txReclaim or maxCollisions errors
+ - In vortex_error(do_tx_reset): only reset dev->tbusy for vortex-style NICs.
+ - In vortex_open(), set vp->tx_full to zero (else we get errors if the device was
+ closed with a full Tx ring).
+
- See http://www.uow.edu.au/~andrewm/linux/#3c59x-2.2 for more details.
*/
-static char *version =
-"3c59x.c:v0.99H 27May00 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+static char version[] =
+"3c59x.c 16Aug00 Donald Becker and others http://www.scyld.com/network/vortex.html\n";
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
@@ -316,6 +334,8 @@
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
{"3c555 Laptop Hurricane", 0x10B7, 0x5055, 0xffff,
PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE, 128, vortex_probe1},
+ {"3c556 10/100 Mini PCI Adapter", 0x10B7, 0x6055, 0xffff,
+ PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_CB_FNS, 128, vortex_probe1},
{"3c575 Boomerang CardBus", 0x10B7, 0x5057, 0xffff,
PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII, 64, vortex_probe1},
{"3CCFE575 Cyclone CardBus", 0x10B7, 0x5157, 0xffff,
@@ -559,6 +579,7 @@
static int vortex_rx(struct device *dev);
static int boomerang_rx(struct device *dev);
static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void boomerang_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int vortex_close(struct device *dev);
static void update_stats(long ioaddr, struct device *dev);
static struct net_device_stats *vortex_get_stats(struct device *dev);
@@ -566,6 +587,8 @@
static int vortex_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+/* #define dev_alloc_skb dev_alloc_skb_debug */
+
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
/* Option count limit only -- unlimited interfaces are supported. */
#define MAX_UNITS 8
@@ -620,6 +643,8 @@
dev = vortex_probe1(bus, devfn, NULL, io, irq, chip_idx, MAX_UNITS+1);
if (dev) {
dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+ if (!node)
+ return NULL;
strcpy(node->dev_name, dev->name);
node->major = node->minor = 0;
node->next = NULL;
@@ -859,6 +884,8 @@
/* Make certain the descriptor lists are aligned. */
{
void *mem = kmalloc(sizeof(*vp) + 15, GFP_KERNEL);
+ if (!mem)
+ return NULL;
vp = (void *)(((long)mem + 15) & ~15);
vp->priv_addr = mem;
}
@@ -903,7 +930,11 @@
#ifdef CARDBUS
outw(0x230 + i, ioaddr + Wn0EepromCmd);
#else
- outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+ if (pci_tbl[chip_idx].device_id == 0x6055) {
+ outw(0x230 + i, ioaddr + Wn0EepromCmd);
+ } else {
+ outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+ }
#endif
/* Pause for at least 162 us. for the read to take place. */
for (timer = 10; timer >= 0; timer--) {
@@ -947,7 +978,11 @@
printk("%s: CardBus functions mapped %8.8x->%p (PCMCIA committee"
" brain-damage).\n", dev->name, fn_st_addr, vp->cb_fn_base);
EL3WINDOW(2);
- outw(0x10 | inw(ioaddr + Wn2_ResetOptions), ioaddr + Wn2_ResetOptions);
+ if (pci_tbl[chip_idx].device_id == 0x6055) {
+ outw(0x4010 | inw(ioaddr + Wn2_ResetOptions), ioaddr + Wn2_ResetOptions);
+ } else {
+ outw(0x10 | inw(ioaddr + Wn2_ResetOptions), ioaddr + Wn2_ResetOptions);
+ }
}
/* Extract our information from the EEPROM data. */
@@ -1029,12 +1064,13 @@
request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);
/* The 3c59x-specific entries in the device structure. */
- dev->open = &vortex_open;
- dev->hard_start_xmit = &vortex_start_xmit;
- dev->stop = &vortex_close;
- dev->get_stats = &vortex_get_stats;
- dev->do_ioctl = &vortex_ioctl;
- dev->set_multicast_list = &set_rx_mode;
+ dev->open = vortex_open;
+ dev->hard_start_xmit = vp->full_bus_master_tx ?
+ boomerang_start_xmit : vortex_start_xmit;
+ dev->stop = vortex_close;
+ dev->get_stats = vortex_get_stats;
+ dev->do_ioctl = vortex_ioctl;
+ dev->set_multicast_list = set_rx_mode;
return dev;
}
@@ -1046,7 +1082,9 @@
long ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
unsigned int config;
- int i;
+ int i, retval;
+
+ MOD_INC_USE_COUNT;
/* Before initializing select the active media port. */
EL3WINDOW(3);
@@ -1068,12 +1106,6 @@
} else
dev->if_port = vp->default_media;
- init_timer(&vp->timer);
- vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
- vp->timer.data = (unsigned long)dev;
- vp->timer.function = &vortex_timer; /* timer handler */
- add_timer(&vp->timer);
-
if (vortex_debug > 1)
printk(KERN_DEBUG "%s: Initial media type %s.\n",
dev->name, media_tbl[dev->if_port].name);
@@ -1123,8 +1155,11 @@
outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
/* Use the now-standard shared IRQ implementation. */
- if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) {
- return -EAGAIN;
+ if ((retval = request_irq(dev->irq, vp->full_bus_master_tx ?
+ &boomerang_interrupt : &vortex_interrupt,
+ SA_SHIRQ, dev->name, dev))) {
+ printk(KERN_ERR "%s: Cannot allocate IRQ #%d\n", dev->name, dev->irq);
+ goto out;
}
if (vortex_debug > 1) {
@@ -1189,12 +1224,20 @@
vp->rx_ring[i].addr = virt_to_bus(skb->data);
#endif
}
+ if (i != RX_RING_SIZE) {
+ int j;
+ for (j = 0; j < RX_RING_SIZE; j++) {
+ if (vp->rx_skbuff[j])
+ DEV_FREE_SKB(vp->rx_skbuff[j]);
+ }
+ retval = -ENOMEM;
+ goto out_free_irq;
+ }
/* Wrap the ring. */
vp->rx_ring[i-1].next = cpu_to_le32(virt_to_bus(&vp->rx_ring[0]));
outl(virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
}
if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */
- dev->hard_start_xmit = &boomerang_start_xmit;
vp->cur_tx = vp->dirty_tx = 0;
outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold); /* Room for a packet. */
/* Clear the Tx ring. */
@@ -1208,6 +1251,7 @@
dev->tbusy = 0;
dev->start = 1;
+ vp->tx_full = 0;
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
@@ -1228,9 +1272,18 @@
if (vp->cb_fn_base) /* The PCMCIA people are idiots. */
writel(0x8000, vp->cb_fn_base + 4);
- MOD_INC_USE_COUNT;
+ init_timer(&vp->timer);
+ vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
+ vp->timer.data = (unsigned long)dev;
+ vp->timer.function = &vortex_timer; /* timer handler */
+ add_timer(&vp->timer);
return 0;
+
+out_free_irq:
+ free_irq(dev->irq, dev);
+out:
+ return retval;
}
static void vortex_timer(unsigned long data)
@@ -1368,7 +1421,10 @@
unsigned long flags;
__save_flags(flags);
__cli();
- vortex_interrupt(dev->irq, dev, 0);
+ if (vp->full_bus_master_tx)
+ boomerang_interrupt(dev->irq, dev, 0);
+ else
+ vortex_interrupt(dev->irq, dev, 0);
__restore_flags(flags);
}
}
@@ -1443,7 +1499,7 @@
if (tx_status & 0x14) vp->stats.tx_fifo_errors++;
if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
outb(0, ioaddr + TxStatus);
- if (tx_status & 0x30)
+ if (tx_status & 0x30) /* txJabber or txUnderrun */
do_tx_reset = 1;
else /* Merely re-enable the transmitter. */
outw(TxEnable, ioaddr + EL3_CMD);
@@ -1505,13 +1561,14 @@
if (do_tx_reset) {
int j;
outw(TxReset, ioaddr + EL3_CMD);
- for (j = 200; j >= 0 ; j--)
+ for (j = 4000; j >= 0 ; j--)
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(TxEnable, ioaddr + EL3_CMD);
- clear_bit(0, (void*)&dev->tbusy);
- if (!vp->full_bus_master_tx)
+ if (!vp->full_bus_master_tx) {
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
+ }
}
}
@@ -1602,10 +1659,10 @@
int i;
if (vortex_debug > 3)
- printk(KERN_DEBUG "%s: Trying to send a packet, Tx index %d.\n",
+ printk(KERN_DEBUG "%s: Trying to send a boomerang packet, Tx index %d.\n",
dev->name, vp->cur_tx);
if (vp->tx_full) {
- if (vortex_debug >0)
+ if (vortex_debug > 0)
printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n",
dev->name);
return 1;
@@ -1619,7 +1676,7 @@
spin_lock_irqsave(&vp->lock, flags);
outw(DownStall, ioaddr + EL3_CMD);
/* Wait for the stall to complete. */
- for (i = 4000; i >= 0 ; i--)
+ for (i = 4000; i >= 0; i--)
if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
break;
prev_entry->next = cpu_to_le32(virt_to_bus(&vp->tx_ring[entry]));
@@ -1645,19 +1702,28 @@
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
+
+/*
+ * This is the ISR for the vortex series chips.
+ * full_bus_master_tx == 0 && full_bus_master_rx == 0
+ */
+
static void vortex_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct device *dev = dev_id;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
long ioaddr;
- int latency, status;
+ int status;
int work_done = max_interrupt_work;
- spin_lock(&vp->lock);
-
ioaddr = dev->base_addr;
- latency = inb(ioaddr + Timer);
+ spin_lock(&vp->lock);
status = inw(ioaddr + EL3_STATUS);
+ if ((status & IntLatch) == 0) {
+ if (vortex_debug > 5)
+ printk(KERN_DEBUG "%s: no vortex interrupt pending\n", dev->name);
+ goto no_int; /* Happens during shared interrupts */
+ }
if (status & IntReq) {
status |= vp->deferred;
@@ -1665,18 +1731,16 @@
}
if (vortex_debug > 4)
- printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n",
- dev->name, status, latency);
+ printk(KERN_DEBUG "%s: vortex_interrupt, status %4.4x, latency %d ticks.\n",
+ dev->name, status, inb(ioaddr + Timer));
+
do {
if (vortex_debug > 5)
printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n",
dev->name, status);
+
if (status & RxComplete)
vortex_rx(dev);
- if (status & UpComplete) {
- outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
- boomerang_rx(dev);
- }
if (status & TxAvailable) {
if (vortex_debug > 5)
@@ -1687,6 +1751,93 @@
mark_bh(NET_BH);
}
+ if (status & DMADone) {
+ if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) {
+ outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+ DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */
+ if (inw(ioaddr + TxFree) > 1536) {
+ clear_bit(0, (void*)&dev->tbusy);
+ mark_bh(NET_BH);
+ } else /* Interrupt when FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+ }
+ }
+
+ /* Check for all uncommon interrupts at once. */
+ if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) {
+ if (status == 0xffff)
+ break;
+ vortex_error(dev, status);
+ }
+
+ if (--work_done < 0) {
+ printk(KERN_WARNING "%s: Too much work in interrupt, status "
+ "%4.4x.\n", dev->name, status);
+ /* Disable all pending interrupts. */
+ do {
+ vp->deferred |= status;
+ outw(SetStatusEnb | (~vp->deferred & vp->status_enable),
+ ioaddr + EL3_CMD);
+ outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
+ } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch);
+ /* The timer will reenable interrupts. */
+ mod_timer(&vp->timer, RUN_AT(1));
+ break;
+ }
+
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+ } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+ if (vortex_debug > 4)
+ printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
+ dev->name, status);
+
+no_int:
+ spin_unlock(&vp->lock);
+}
+
+/*
+ * This is the ISR for the boomerang/cyclone/hurricane/tornado series chips.
+ * full_bus_master_tx == 1 && full_bus_master_rx == 1
+ */
+
+static void boomerang_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = dev_id;
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ long ioaddr;
+ int status;
+ int work_done = max_interrupt_work;
+
+ ioaddr = dev->base_addr;
+ spin_lock(&vp->lock);
+ status = inw(ioaddr + EL3_STATUS);
+ if ((status & IntLatch) == 0) {
+ if (vortex_debug > 5)
+ printk(KERN_DEBUG "%s: no boomerang interrupt pending\n", dev->name);
+ goto no_int; /* Happens during shared interrupts */
+ }
+
+ if (status & IntReq) {
+ status |= vp->deferred;
+ vp->deferred = 0;
+ }
+
+ if (vortex_debug > 4)
+ printk(KERN_DEBUG "%s: interrupt, status %04x, latency %d, cur_rx %d, dirty_rx %d\n",
+ dev->name, status, inb(ioaddr + Timer), vp->cur_rx, vp->dirty_rx);
+
+ do {
+ if (vortex_debug > 5)
+ printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+
+ if (status & UpComplete) {
+ outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ boomerang_rx(dev);
+ }
+
if (status & DownComplete) {
unsigned int dirty_tx = vp->dirty_tx;
@@ -1705,22 +1856,12 @@
vp->dirty_tx = dirty_tx;
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
if (vp->tx_full && (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
- vp->tx_full= 0;
+ vp->tx_full = 0;
clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
}
- if (status & DMADone) {
- if (inw(ioaddr + Wn7_MasterStatus) & 0x1000) {
- outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
- DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */
- if (inw(ioaddr + TxFree) > 1536) {
- clear_bit(0, (void*)&dev->tbusy);
- mark_bh(NET_BH);
- } else /* Interrupt when FIFO has room for max-sized packet. */
- outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
- }
- }
+
/* Check for all uncommon interrupts at once. */
if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq)) {
if (status == 0xffff)
@@ -1729,25 +1870,18 @@
}
if (--work_done < 0) {
- if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) {
- /* Just ack these and return. */
- outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD);
- } else {
- printk(KERN_WARNING "%s: Too much work in interrupt, status "
- "%4.4x.\n", dev->name, status);
- /* Disable all pending interrupts. */
- do {
- vp->deferred |= status;
- outw(SetStatusEnb | (~vp->deferred & vp->status_enable),
- ioaddr + EL3_CMD);
- outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
- } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch);
- /* The timer will reenable interrupts. */
- del_timer(&vp->timer);
- vp->timer.expires = RUN_AT(1);
- add_timer(&vp->timer);
- break;
- }
+ printk(KERN_WARNING "%s: Too much work in interrupt, status "
+ "%4.4x.\n", dev->name, status);
+ /* Disable all pending interrupts. */
+ do {
+ vp->deferred |= status;
+ outw(SetStatusEnb | (~vp->deferred & vp->status_enable),
+ ioaddr + EL3_CMD);
+ outw(AckIntr | (vp->deferred & 0x7ff), ioaddr + EL3_CMD);
+ } while ((status = inw(ioaddr + EL3_CMD)) & IntLatch);
+ /* The timer will reenable interrupts. */
+ mod_timer(&vp->timer, RUN_AT(1));
+ break;
}
/* Acknowledge the IRQ. */
@@ -1755,14 +1889,22 @@
if (vp->cb_fn_base) /* The PCMCIA people are idiots. */
writel(0x8000, vp->cb_fn_base + 4);
- } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+ } while ((status = inw(ioaddr + EL3_STATUS)) & IntLatch);
+
+ /*
+ * If we have totally run out to rx skb's due to persistent OOM,
+ * we can use the Tx interrupt to retry the allocation. Dirty
+ * but expedient
+ */
+ if ((vp->cur_rx - vp->dirty_rx) == RX_RING_SIZE)
+ boomerang_rx(dev);
if (vortex_debug > 4)
printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
dev->name, status);
+no_int:
spin_unlock(&vp->lock);
- return;
}
static int vortex_rx(struct device *dev)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)