Subject: [PATCH 1/2] [2.6] update forcedeth from 0.20 to 0.22

From: Carl-Daniel Hailfinger <[email-masked]>
Date: Tue, 27 Jan 2004 12:20:57 +0100

Jeff,

the attached patch is against current Linus bk tree and updates forcedeth
from 0.20 to 0.22. This is the version discussed a few days ago.
To address the comments we received on 0.22, we have prepared another
patch to 0.23, which is the next one in this series.

Please consider applying this patch and the next one. The patches are
split up so you can verify more easily that we addressed most of the comments.

Regards,
Carl-Daniel
===== drivers/net/forcedeth.c 1.5 vs edited =====
--- 1.5/drivers/net/forcedeth.c	Fri Jan 16 01:47:29 2004
+++ edited/drivers/net/forcedeth.c	Fri Jan 23 17:00:27 2004
@@ -60,15 +60,23 @@
  * 			   addresses, really stop rx if already running
  * 			   in start_rx, clean up a bit.
  * 				(C) Carl-Daniel Hailfinger
- * 	0.20: 07 Dev 2003: alloc fixes
+ * 	0.20: 07 Dec 2003: alloc fixes
+ * 	0.21: 12 Jan 2004: additional alloc fix, nic polling fix.
+ *	0.22: 19 Jan 2004: reprogram timer to a sane rate, avoid lockup
+ * 			   on close.
+ * 				(C) Carl-Daniel Hailfinger
  *
  * Known bugs:
- * The irq handling is wrong - no tx done interrupts are generated.
- * This means recovery from netif_stop_queue only happens in the hw timer
- * interrupt (1/2 second on nForce2, 1/100 second on nForce3), or if an
- * rx packet arrives by chance.
+ * We suspect that on some hardware no TX done interrupts are generated.
+ * This means recovery from netif_stop_queue only happens if the hw timer
+ * interrupt fires (100 times/second, configurable with NVREG_POLL_DEFAULT)
+ * and the timer is active in the IRQMask, or if a rx packet arrives by chance.
+ * If your hardware reliably generates tx done interrupts, then you can remove
+ * DEV_NEED_TIMERIRQ from the driver_data flags.
+ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
+ * superfluous timer interrupts from the nic.
  */
-#define FORCEDETH_VERSION		"0.19"
+#define FORCEDETH_VERSION		"0.22"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -104,6 +112,7 @@
 #define DEV_NEED_LASTPACKET1	0x0001
 #define DEV_IRQMASK_1		0x0002
 #define DEV_IRQMASK_2		0x0004
+#define DEV_NEED_TIMERIRQ	0x0008
 
 enum {
 	NvRegIrqStatus = 0x000,
@@ -124,7 +133,12 @@
 	NvRegUnknownSetupReg6 = 0x008,
 #define NVREG_UNKSETUP6_VAL		3
 
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
 	NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT	970
 	NvRegMisc1 = 0x080,
 #define NVREG_MISC1_HD		0x02
 #define NVREG_MISC1_FORCE	0x3b0f3c
@@ -1185,6 +1199,7 @@
 	writel(NVREG_RNDSEED_FORCE | (i&NVREG_RNDSEED_MASK), base + NvRegRandomSeed);
 	writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
 	writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
+	writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
 	writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
 	writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID,
 			base + NvRegAdapterControl);
@@ -1248,6 +1263,7 @@
 static int close(struct net_device *dev)
 {
 	struct fe_priv *np = get_nvpriv(dev);
+	u8 *base;
 
 	spin_lock_irq(&np->lock);
 	np->in_shutdown = 1;
@@ -1261,6 +1277,13 @@
 	spin_lock_irq(&np->lock);
 	stop_tx(dev);
 	stop_rx(dev);
+	base = get_hwbase(dev);
+
+	/* disable interrupts on the nic or we will lock up */
+	writel(0, base + NvRegIrqMask);
+	pci_push(base);
+	dprintk(KERN_INFO "%s: Irqmask is zero again\n", dev->name);
+
 	spin_unlock_irq(&np->lock);
 
 	free_irq(dev->irq, dev);
@@ -1393,6 +1416,8 @@
 		np->irqmask = NVREG_IRQMASK_WANTED_1;
 	if (id->driver_data & DEV_IRQMASK_2)
 		np->irqmask = NVREG_IRQMASK_WANTED_2;
+	if (id->driver_data & DEV_NEED_TIMERIRQ)
+		np->irqmask |= NVREG_IRQ_TIMER;
 
 	err = register_netdev(dev);
 	if (err) {
@@ -1450,21 +1475,21 @@
 		.device = 0x1C3,
 		.subvendor = PCI_ANY_ID,
 		.subdevice = PCI_ANY_ID,
-		.driver_data = DEV_IRQMASK_1,
+		.driver_data = DEV_IRQMASK_1|DEV_NEED_TIMERIRQ,
 	},
 	{	/* nForce2 Ethernet Controller */
 		.vendor = PCI_VENDOR_ID_NVIDIA,
 		.device = 0x0066,
 		.subvendor = PCI_ANY_ID,
 		.subdevice = PCI_ANY_ID,
-		.driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2,
+		.driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
 	},
 	{	/* nForce3 Ethernet Controller */
 		.vendor = PCI_VENDOR_ID_NVIDIA,
 		.device = 0x00D6,
 		.subvendor = PCI_ANY_ID,
 		.subdevice = PCI_ANY_ID,
-		.driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2,
+		.driver_data = DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ,
 	},
 	{0,},
 };