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

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)