From: Manfred Spraul <manfred@colorfullife.com>

attached is a forcedeth update from Carl-Daniel. The important change is 
that the driver now handles out of rx buffers - previously that 
condition resulted in an endless irq storm. Additionally the driver now 
detects irq storms and switches to polling if that happens - I'm sure 
there are other error conditions that we don't handle yet properly. The 
third change is automatic detection of bad mac addresses.



 drivers/net/forcedeth.c |  127 ++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 101 insertions(+), 26 deletions(-)

diff -puN drivers/net/forcedeth.c~forcedeth-update-3 drivers/net/forcedeth.c
--- 25/drivers/net/forcedeth.c~forcedeth-update-3	2003-12-14 19:21:12.000000000 -0800
+++ 25-akpm/drivers/net/forcedeth.c	2003-12-14 19:21:12.000000000 -0800
@@ -56,13 +56,18 @@
  * 	0.17: 16 Nov 2003: undo rx buffer size increase. Substract 1 from
  * 			   the tx length.
  * 	0.18: 17 Nov 2003: fix oops due to late initialization of dev_stats
+ * 	0.19: 29 Nov 2003: Handle RxNoBuf, detect & handle invalid mac
+ * 			   addresses, really stop rx if already running
+ * 			   in start_rx, clean up a bit.
+ * 				(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), or if an rx packet arrives by chance.
+ * interrupt (1/2 second on nForce2, 1/100 second on nForce3), or if an
+ * rx packet arrives by chance.
  */
-#define FORCEDETH_VERSION		"0.18"
+#define FORCEDETH_VERSION		"0.19"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -102,14 +107,16 @@ enum {
 #define NVREG_IRQSTAT_MIIEVENT	0x040
 #define NVREG_IRQSTAT_MASK		0x1ff
 	NvRegIrqMask = 0x004,
-#define NVREG_IRQ_UNKNOWN		0x0005
 #define NVREG_IRQ_RX			0x0002
+#define NVREG_IRQ_RX_NOBUF		0x0004
+#define NVREG_IRQ_TX_ERR		0x0008
 #define NVREG_IRQ_TX2			0x0010
 #define NVREG_IRQ_TIMER			0x0020
 #define NVREG_IRQ_LINK			0x0040
 #define NVREG_IRQ_TX1			0x0100
 #define NVREG_IRQMASK_WANTED_1		0x005f
 #define NVREG_IRQMASK_WANTED_2		0x0147
+#define NVREG_IRQ_UNKNOWN		(~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
 
 	NvRegUnknownSetupReg6 = 0x008,
 #define NVREG_UNKSETUP6_VAL		3
@@ -291,6 +298,8 @@ struct ring_desc {
 #define RX_ALLOC_BUFSIZE	(DEFAULT_MTU + 128)
 
 #define OOM_REFILL	(1+HZ/20)
+#define POLL_WAIT	(1+HZ/100)
+
 /*
  * SMP locking:
  * All hardware access under dev->priv->lock, except the performance
@@ -329,6 +338,7 @@ struct fe_priv {
 	dma_addr_t rx_dma[RX_RING];
 	unsigned int rx_buf_sz;
 	struct timer_list oom_kick;
+	struct timer_list nic_poll;
 
 	/*
 	 * tx specific fields.
@@ -340,6 +350,12 @@ struct fe_priv {
 	u16 tx_flags;
 };
 
+/*
+ * Maximum number of loops until we assume that a bit in the irq mask
+ * is stuck. Overridable with module param.
+ */
+static int max_interrupt_work = 5;
+
 static inline struct fe_priv *get_nvpriv(struct net_device *dev)
 {
 	return (struct fe_priv *) dev->priv;
@@ -442,7 +458,7 @@ static void start_rx(struct net_device *
 	dprintk(KERN_DEBUG "%s: start_rx\n", dev->name);
 	/* Already running? Stop it. */
 	if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
-		writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
+		writel(0, base + NvRegReceiverControl);
 		pci_push(base);
 	}
 	writel(np->linkspeed, base + NvRegLinkSpeed);
@@ -562,9 +578,6 @@ static int alloc_rx(struct net_device *d
 					dev->name, refill_rx);
 		refill_rx++;
 	}
-	if (np->refill_rx != refill_rx) {
-		/* FIXME: made progress. Kick hardware */
-	}
 	np->refill_rx = refill_rx;
 	if (np->cur_rx - refill_rx == RX_RING)
 		return 1;
@@ -601,10 +614,6 @@ static int init_ring(struct net_device *
 	for (i = 0; i < RX_RING; i++) {
 		np->rx_ring[i].Flags = 0;
 	}
-	init_timer(&np->oom_kick);
-	np->oom_kick.data = (unsigned long) dev;
-	np->oom_kick.function = &do_rx_refill;	/* timer handler */
-
 	return alloc_rx(dev);
 }
 
@@ -624,13 +633,11 @@ static void drain_tx(struct net_device *
 		}
 	}
 }
-static void drain_ring(struct net_device *dev)
+
+static void drain_rx(struct net_device *dev)
 {
 	struct fe_priv *np = get_nvpriv(dev);
 	int i;
-
-	drain_tx(dev);
-
 	for (i = 0; i < RX_RING; i++) {
 		np->rx_ring[i].Flags = 0;
 		wmb();
@@ -644,6 +651,12 @@ static void drain_ring(struct net_device
 	}
 }
 
+static void drain_ring(struct net_device *dev)
+{
+	drain_tx(dev);
+	drain_rx(dev);
+}
+
 /*
  * start_xmit: dev->hard_start_xmit function
  * Called with dev->xmit_lock held.
@@ -725,6 +738,7 @@ static void tx_done(struct net_device *d
 	if (np->next_tx - np->nic_tx < TX_LIMIT_START)
 		netif_wake_queue(dev);
 }
+
 /*
  * tx_timeout: dev->tx_timeout function
  * Called with dev->xmit_lock held.
@@ -734,8 +748,7 @@ static void tx_timeout(struct net_device
 	struct fe_priv *np = get_nvpriv(dev);
 	u8 *base = get_hwbase(dev);
 
-	dprintk(KERN_DEBUG "%s: Got tx_timeout.\n", dev->name);
-	dprintk(KERN_DEBUG "%s: irq: %08x\n", dev->name,
+	dprintk(KERN_DEBUG "%s: Got tx_timeout. irq: %08x\n", dev->name,
 			readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK);
 
 	spin_lock_irq(&np->lock);
@@ -748,6 +761,7 @@ static void tx_timeout(struct net_device
 
 	/* 3) if there are dead entries: clear everything */
 	if (np->next_tx != np->nic_tx) {
+		printk(KERN_DEBUG "%s: tx_timeout: dead entries!\n", dev->name);
 		drain_tx(dev);
 		np->next_tx = np->nic_tx = 0;
 		writel((u32) (np->ring_addr + RX_RING*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
@@ -769,7 +783,7 @@ static void rx_process(struct net_device
 		int len;
 		int i;
 		if (np->cur_rx - np->refill_rx >= RX_RING)
-			break;	/* ring empty - do not continue */
+			break;	/* we scanned the whole ring - do not continue */
 
 		i = np->cur_rx % RX_RING;
 		prd = &np->rx_ring[i];
@@ -779,7 +793,8 @@ static void rx_process(struct net_device
 		if (prd->Flags & cpu_to_le16(NV_RX_AVAIL))
 			break;	/* still owned by hardware, */
 
-		/* the packet is for us - immediately tear down the pci mapping, and
+		/*
+		 * the packet is for us - immediately tear down the pci mapping, and
 		 * prefetch the first cacheline of the packet.
 		 */
 		pci_unmap_single(np->pci_dev, np->rx_dma[i],
@@ -1003,10 +1018,11 @@ static irqreturn_t nic_irq(int foo, void
 	struct fe_priv *np = get_nvpriv(dev);
 	u8 *base = get_hwbase(dev);
 	u32 events;
+	int i;
 
 	dprintk(KERN_DEBUG "%s: nic_irq\n", dev->name);
 
-	for (;;) {
+	for (i=0; ; i++) {
 		events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
 		writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
 		pci_push(base);
@@ -1014,14 +1030,13 @@ static irqreturn_t nic_irq(int foo, void
 		if (!(events & np->irqmask))
 			break;
 
-		/* FIXME: only call the required processing functions */
-		if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX2)) {
+		if (events & (NVREG_IRQ_TX1|NVREG_IRQ_TX2|NVREG_IRQ_TX_ERR)) {
 			spin_lock(&np->lock);
 			tx_done(dev);
 			spin_unlock(&np->lock);
 		}
 
-		if (events & NVREG_IRQ_RX) {
+		if (events & (NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF)) {
 			rx_process(dev);
 			if (alloc_rx(dev)) {
 				spin_lock(&np->lock);
@@ -1036,14 +1051,48 @@ static irqreturn_t nic_irq(int foo, void
 			link_irq(dev);
 			spin_unlock(&np->lock);
 		}
+		if (events & (NVREG_IRQ_TX_ERR)) {
+			dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n",
+						dev->name, events);
+		}
 		if (events & (NVREG_IRQ_UNKNOWN)) {
-			printk("%s: received irq with unknown source 0x%x.\n", dev->name, events);
+			printk(KERN_DEBUG "%s: received irq with unknown events 0x%x. Please report\n",
+						dev->name, events);
+ 		}
+		if (i > max_interrupt_work) {
+			spin_lock(&np->lock);
+			/* disable interrupts on the nic */
+			writel(0, base + NvRegIrqMask);
+			pci_push(base);
+
+			if (!np->in_shutdown)
+				mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
+			printk(KERN_DEBUG "%s: too many iterations (%d) in nic_irq.\n", dev->name, i);
+			spin_unlock(&np->lock);
+			break;
 		}
-		/* FIXME: general errors, link change interrupts */
+
 	}
 	dprintk(KERN_DEBUG "%s: nic_irq completed\n", dev->name);
 
-	return IRQ_HANDLED;
+	return IRQ_RETVAL(i);
+}
+
+static void do_nic_poll(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *) data;
+	struct fe_priv *np = get_nvpriv(dev);
+	u8 *base = get_hwbase(dev);
+
+	disable_irq(dev->irq);
+	/*
+	 * reenable interrupts on the nic, we have to do this before calling
+	 * nic_irq because that may decide to do otherwise
+	 */
+	writel(np->irqmask, base + NvRegIrqMask);
+	pci_push(base);
+	nic_irq((int) 0, (void *) data, (struct pt_regs *) NULL);
+	enable_irq(dev->irq);
 }
 
 static int open(struct net_device *dev)
@@ -1203,6 +1252,7 @@ static int close(struct net_device *dev)
 	synchronize_irq(dev->irq);
 
 	del_timer_sync(&np->oom_kick);
+	del_timer_sync(&np->nic_poll);
 
 	netif_stop_queue(dev);
 	spin_lock_irq(&np->lock);
@@ -1238,6 +1288,13 @@ static int __devinit probe_nic(struct pc
 	SET_MODULE_OWNER(dev);
 	SET_NETDEV_DEV(dev, &pci_dev->dev);
 
+	init_timer(&np->oom_kick);
+	np->oom_kick.data = (unsigned long) dev;
+	np->oom_kick.function = &do_rx_refill;	/* timer handler */
+	init_timer(&np->nic_poll);
+	np->nic_poll.data = (unsigned long) dev;
+	np->nic_poll.function = &do_nic_poll;	/* timer handler */
+
 	err = pci_enable_device(pci_dev);
 	if (err) {
 		printk(KERN_INFO "forcedeth: pci_enable_dev failed: %d\n", err);
@@ -1313,6 +1370,21 @@ static int __devinit probe_nic(struct pc
 	dev->dev_addr[4] = (np->orig_mac[0] >>  8) & 0xff;
 	dev->dev_addr[5] = (np->orig_mac[0] >>  0) & 0xff;
 
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		/*
+		 * Bad mac address. At least one bios sets the mac address
+		 * to 01:23:45:67:89:ab
+		 */
+		printk(KERN_ERR "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
+			dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+			dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+		printk(KERN_ERR "Please complain to your hardware vendor. Switching to a random MAC.\n");
+		dev->dev_addr[0] = 0x00;
+		dev->dev_addr[1] = 0x00;
+		dev->dev_addr[2] = 0x6c;
+		get_random_bytes(&dev->dev_addr[3], 3);
+	}
+
 	dprintk(KERN_DEBUG "%s: MAC Address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,
 			dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
 			dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
@@ -1410,6 +1482,9 @@ static void __exit exit_nic(void)
 	pci_unregister_driver(&driver);
 }
 
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
+
 MODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
 MODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
 MODULE_LICENSE("GPL");

_