linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] 2.4.20-pre sundance.c update
       [not found]     ` <20020919041403.GA10527@orr.falooley.org>
@ 2002-09-19  4:25       ` Jeff Garzik
  2002-09-19  4:56         ` Jason Lunz
  2002-09-19 13:23         ` Donald Becker
  0 siblings, 2 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19  4:25 UTC (permalink / raw)
  To: netdev
  Cc: Jason Lunz, Richard Gooch, becker, Patrick R. McManus,
	Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 301 bytes --]

Attached is the patch I have against 2.4.20-pre-latest, to start fixing 
from as a baseline.

It still has several flaws that were pointed out, but this is the base 
from which I would like testing and patching to proceed.  (also 
hopefully the flaws are minor in terms of general operation)

	Jeff



[-- Attachment #2: sundance-2.4.patch --]
[-- Type: text/plain, Size: 27933 bytes --]

diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 00:22:26 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 00:22:26 2002
@@ -21,22 +21,30 @@
 	Version 1.01a (jgarzik):
 	- Replace some MII-related magic numbers with constants
 
-	Version 1.01b (D-Link):
+	Version 1.02 (D-Link):
 	- Add new board to PCI ID list
+	- Fix multicast bug
 	
+	Version 1.03 (D-Link):
+	- New Rx scheme, reduce Rx congestion
+	- Option to disable flow control
+	
+	Version 1.04 (D-Link):
+	- Tx timeout recovery
+	- More support for ethtool.
 
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01b"
-#define DRV_RELDATE	"17-Jan-2002"
+#define DRV_VERSION	"1.04"
+#define DRV_RELDATE	"19-Aug-2002"
 
 
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 30;
+static int max_interrupt_work = 0;
 static int mtu;
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    Typical is a 64 element hash table based on the Ethernet CRC.  */
@@ -47,6 +55,8 @@
    This chip can receive into offset buffers, so the Alpha does not
    need a copy-align. */
 static int rx_copybreak;
+static int tx_coalesce=1;
+static int flowctrl=1;
 
 /* media[] specifies the media type the NIC operates at.
 		 autosense	Autosensing active media.
@@ -70,9 +80,10 @@
    bonding and packet priority, and more than 128 requires modifying the
    Tx error recovery.
    Large receive rings merely waste memory. */
-#define TX_RING_SIZE	16
-#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
-#define RX_RING_SIZE	32
+#define TX_RING_SIZE	64
+#define TX_QUEUE_LEN	(TX_RING_SIZE - 1) /* Limit ring entries actually used.  */
+#define RX_RING_SIZE	64
+#define RX_BUDGET	32
 #define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct netdev_desc)
 #define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct netdev_desc)
 
@@ -107,13 +118,17 @@
 #include <linux/init.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
-#include <linux/crc32.h>
 #include <asm/uaccess.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
+#ifndef _LOCAL_CRC32
+#include <linux/crc32.h>
+#else
+#include "crc32.h"
+#endif
 
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
@@ -129,10 +144,12 @@
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
+MODULE_PARM(flowctrl, "i");
 MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
 /*
 				Theory of Operation
 
@@ -207,7 +224,6 @@
 
 */
 
-\f
 
 enum pci_id_flags_bits {
         /* Set PCI command register bits before calling probe1(). */
@@ -290,20 +306,24 @@
 enum alta_offsets {
 	DMACtrl = 0x00,
 	TxListPtr = 0x04,
-	TxDMACtrl = 0x08,
-	TxDescPoll = 0x0a,
+	TxDMABurstThresh = 0x08,
+	TxDMAUrgentThresh = 0x09,
+	TxDMAPollPeriod = 0x0a,
 	RxDMAStatus = 0x0c,
 	RxListPtr = 0x10,
-	RxDMACtrl = 0x14,
-	RxDescPoll = 0x16,
+	RxDMABurstThresh = 0x14,
+	RxDMAUrgentThresh = 0x15,
+	RxDMAPollPeriod = 0x16,
 	LEDCtrl = 0x1a,
 	ASICCtrl = 0x30,
 	EEData = 0x34,
 	EECtrl = 0x36,
-	TxThreshold = 0x3c,
+	TxStartThresh = 0x3c,
+	RxEarlyThresh = 0x3e,
 	FlashAddr = 0x40,
 	FlashData = 0x44,
 	TxStatus = 0x46,
+	TxFrameId = 0x47,
 	DownCounter = 0x18,
 	IntrClear = 0x4a,
 	IntrEnable = 0x4c,
@@ -337,6 +357,16 @@
 	/* Aliased and bogus values! */
 	RxStatus = 0x0c,
 };
+enum ASICCtrl_HiWord_bit {
+	GlobalReset = 0x0001,
+	RxReset = 0x0002,
+	TxReset = 0x0004, 
+	DMAReset = 0x0008,
+	FIFOReset = 0x0010,
+	NetworkReset = 0x0020,
+	HostReset = 0x0040,
+	ResetBusy = 0x0400,
+};
 
 /* Bits in the interrupt status/mask registers. */
 enum intr_status_bits {
@@ -399,19 +429,20 @@
 	struct timer_list timer;	/* Media monitoring timer. */
 	/* Frequently used values: keep some adjacent for cache effect. */
 	spinlock_t lock;
+	spinlock_t rx_lock;					/* Group with Tx control cache line. */
 	int chip_id, drv_flags;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
 	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
-	spinlock_t txlock;					/* Group with Tx control cache line. */
 	struct netdev_desc *last_tx;		/* Last Tx descriptor used. */
 	unsigned int cur_tx, dirty_tx;
-	unsigned int tx_full:1;				/* The Tx queue is full. */
 	/* These values are keep track of the transceiver/media in use. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int medialock:1;			/* Do not sense media. */
+	unsigned int flowctrl:1;
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	unsigned int an_enable:1;
 	unsigned int speed;
+	struct tasklet_struct rx_tasklet;
+	int budget;
 	/* Multicast and receive mode. */
 	spinlock_t mcastlock;				/* SMP lock multicast updates. */
 	u16 mcast_filter[4];
@@ -424,6 +455,9 @@
 
 /* The station address location in the EEPROM. */
 #define EEPROM_SA_OFFSET	0x10
+#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \
+			IntrDrvRqst | IntrTxDone | StatsMax | \
+			LinkChange)
 
 static int  eeprom_read(long ioaddr, int location);
 static int  mdio_read(struct net_device *dev, int phy_id, int location);
@@ -434,9 +468,11 @@
 static void tx_timeout(struct net_device *dev);
 static void init_ring(struct net_device *dev);
 static int  start_tx(struct sk_buff *skb, struct net_device *dev);
+static int reset_tx (struct net_device *dev, int irq);
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
+static void rx_poll(unsigned long data);
+static void refill_rx (struct net_device *dev);
 static void netdev_error(struct net_device *dev, int intr_status);
-static int  netdev_rx(struct net_device *dev);
 static void netdev_error(struct net_device *dev, int intr_status);
 static void set_rx_mode(struct net_device *dev);
 static struct net_device_stats *get_stats(struct net_device *dev);
@@ -502,6 +538,7 @@
 	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
 	np->pci_dev = pdev;
 	spin_lock_init(&np->lock);
+	tasklet_init(&np->rx_tasklet, rx_poll, (unsigned long)dev);
 
 	ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
 	if (!ring_space)
@@ -582,6 +619,12 @@
 				np->an_enable = 1;
 			}
 		}
+		if (tx_coalesce < 1) 
+			tx_coalesce = 1;
+		else if (tx_coalesce > TX_QUEUE_LEN - 1)
+			tx_coalesce = TX_QUEUE_LEN - 1;
+		if (flowctrl == 0)
+			np->flowctrl = 0;
 	}
 
 	/* Fibre PHY? */
@@ -742,7 +785,6 @@
 	return;
 }
 
-\f
 static int netdev_open(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
@@ -779,14 +821,10 @@
 	writew(0, ioaddr + IntrEnable);
 	writew(0, ioaddr + DownCounter);
 	/* Set the chip to poll every N*320nsec. */
-	writeb(100, ioaddr + RxDescPoll);
-	writeb(127, ioaddr + TxDescPoll);
+	writeb(100, ioaddr + RxDMAPollPeriod);
+	writeb(127, ioaddr + TxDMAPollPeriod);
 	netif_start_queue(dev);
 
-	/* Enable interrupts by setting the interrupt mask. */
-	writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
-		   | StatsMax | LinkChange, ioaddr + IntrEnable);
-
 	writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
 
 	if (debug > 2)
@@ -802,6 +840,9 @@
 	np->timer.data = (unsigned long)dev;
 	np->timer.function = &netdev_timer;				/* timer handler */
 	add_timer(&np->timer);
+	
+	/* Enable interrupts by setting the interrupt mask. */
+	writew(DEFAULT_INTR, ioaddr + IntrEnable);
 
 	return 0;
 }
@@ -855,9 +896,12 @@
 {
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
+	long flag;
 
-	printk(KERN_WARNING "%s: Transmit timed out, status %2.2x,"
-		   " resetting...\n", dev->name, readb(ioaddr + TxStatus));
+	printk(KERN_WARNING "%s: Transmit timed out, TxStatus %2.2x "
+		   "TxFrameId %2.2x,"
+		   " resetting...\n", dev->name, readb(ioaddr + TxStatus),
+		   readb(ioaddr + TxFrameId));
 
 	{
 		int i;
@@ -866,22 +910,24 @@
 			printk(" %8.8x", (unsigned int)np->rx_ring[i].status);
 		printk("\n"KERN_DEBUG"  Tx ring %p: ", np->tx_ring);
 		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %4.4x", np->tx_ring[i].status);
+			printk(" %8.8x", np->tx_ring[i].status);
 		printk("\n");
 	}
+	spin_lock_irqsave(&np->lock, flag);
+	reset_tx(dev, 0);
+	spin_unlock_irqrestore(&np->lock, flag);
 
 	/* Perhaps we should reinitialize the hardware here. */
 	dev->if_port = 0;
 	/* Stop and restart the chip's Tx processes . */
 
 	/* Trigger an immediate transmit demand. */
-	writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
-		   | StatsMax | LinkChange, ioaddr + IntrEnable);
+	writew(DEFAULT_INTR, ioaddr + IntrEnable);
 
 	dev->trans_start = jiffies;
 	np->stats.tx_errors++;
 
-	if (!np->tx_full)
+	if (!netif_queue_stopped(dev))
 		netif_wake_queue(dev);
 }
 
@@ -892,7 +938,6 @@
 	struct netdev_private *np = dev->priv;
 	int i;
 
-	np->tx_full = 0;
 	np->cur_rx = np->cur_tx = 0;
 	np->dirty_rx = np->dirty_tx = 0;
 
@@ -929,15 +974,16 @@
 	return;
 }
 
-static int start_tx(struct sk_buff *skb, struct net_device *dev)
+static int
+start_tx (struct sk_buff *skb, struct net_device *dev)
 {
-	struct netdev_private *np = dev->priv;
+	struct netdev_private *np = (struct netdev_private *) dev->priv;
 	struct netdev_desc *txdesc;
 	unsigned entry;
+	long ioaddr = dev->base_addr;
 
 	/* Note: Ordering is important here, set the field with the
 	   "ownership" bit last, and only then increment cur_tx. */
-
 	/* Calculate the next Tx descriptor entry. */
 	entry = np->cur_tx % TX_RING_SIZE;
 	np->tx_skbuff[entry] = skb;
@@ -945,11 +991,17 @@
 
 	txdesc->next_desc = 0;
 	/* Note: disable the interrupt generation here before releasing. */
-	txdesc->status =
-		cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx | DisableAlign);
-	txdesc->frag[0].addr = cpu_to_le32(pci_map_single(np->pci_dev, 
-		skb->data, skb->len, PCI_DMA_TODEVICE));
-	txdesc->frag[0].length = cpu_to_le32(skb->len | LastFrag);
+	if (entry % tx_coalesce == 0) {
+		txdesc->status = cpu_to_le32 ((entry << 2) | 
+				 DescIntrOnTx | DisableAlign);
+	
+	} else {
+		txdesc->status = cpu_to_le32 ((entry << 2) | DisableAlign);
+	}
+	txdesc->frag[0].addr = cpu_to_le32 (pci_map_single (np->pci_dev, skb->data,
+							skb->len,
+							PCI_DMA_TODEVICE));
+	txdesc->frag[0].length = cpu_to_le32 (skb->len | LastFrag);
 	if (np->last_tx)
 		np->last_tx->next_desc = cpu_to_le32(np->tx_ring_dma +
 			entry*sizeof(struct netdev_desc));
@@ -957,24 +1009,63 @@
 	np->cur_tx++;
 
 	/* On some architectures: explicitly flush cache lines here. */
-
-	if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1) {
+	if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 
+			&& !netif_queue_stopped(dev)) {
 		/* do nothing */
 	} else {
-		np->tx_full = 1;
-		netif_stop_queue(dev);
+		netif_stop_queue (dev);
 	}
 	/* Side effect: The read wakes the potentially-idle transmit channel. */
-	if (readl(dev->base_addr + TxListPtr) == 0)
-		writel(np->tx_ring_dma + entry*sizeof(*np->tx_ring),
+	if (readl (dev->base_addr + TxListPtr) == 0)
+		writel (np->tx_ring_dma + entry*sizeof(*np->tx_ring),
 			dev->base_addr + TxListPtr);
 
 	dev->trans_start = jiffies;
 
 	if (debug > 4) {
-		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
-			   dev->name, np->cur_tx, entry);
+		printk (KERN_DEBUG
+			"%s: Transmit frame #%d queued in slot %d.\n",
+			dev->name, np->cur_tx, entry);
 	}
+	if (tx_coalesce > 1)
+		writel (1000, ioaddr + DownCounter);
+	return 0;
+}
+static int
+reset_tx (struct net_device *dev, int irq)
+{
+	struct netdev_private *np = (struct netdev_private*) dev->priv;
+	long ioaddr = dev->base_addr;
+	int i;
+	int frame_id;
+	
+	frame_id = readb(ioaddr + TxFrameId);
+	writew (TxReset | DMAReset | FIFOReset | NetworkReset,
+			ioaddr + ASICCtrl + 2);
+	for (i=50; i > 0; i--) {
+		if ((readw(ioaddr + ASICCtrl + 2) & ResetBusy) == 0)
+			break;
+		mdelay(1);
+	}
+	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
+		int entry = np->dirty_tx % TX_RING_SIZE;
+		struct sk_buff *skb;
+		if (!(np->tx_ring[entry].status & 0x00010000))
+			break;
+		skb = np->tx_skbuff[entry];
+		/* Free the original skb. */
+		pci_unmap_single(np->pci_dev, 
+			np->tx_ring[entry].frag[0].addr, 
+			skb->len, PCI_DMA_TODEVICE);
+		if (irq)
+			dev_kfree_skb_irq (np->tx_skbuff[entry]);
+		else
+			dev_kfree_skb (np->tx_skbuff[entry]);
+
+		np->tx_skbuff[entry] = 0;
+	}
+	writel (np->tx_ring_dma + frame_id * sizeof(*np->tx_ring),
+			dev->base_addr + TxListPtr);
 	return 0;
 }
 
@@ -989,83 +1080,88 @@
 
 	ioaddr = dev->base_addr;
 	np = dev->priv;
-	spin_lock(&np->lock);
 
 	do {
 		int intr_status = readw(ioaddr + IntrStatus);
-		writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
-			IntrDrvRqst | IntrTxDone | IntrTxDMADone | StatsMax | 
-			LinkChange), ioaddr + IntrStatus);
+		writew(intr_status, ioaddr + IntrStatus);
 
 		if (debug > 4)
 			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
 				   dev->name, intr_status);
 
-		if (intr_status == 0)
+		if (!(intr_status & DEFAULT_INTR))
 			break;
 
-		if (intr_status & (IntrRxDone|IntrRxDMADone))
-			netdev_rx(dev);
+		if (intr_status & (IntrRxDMADone)) {
+			writew(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone), 
+					ioaddr + IntrEnable);
+			if (np->budget < 0)
+				np->budget = RX_BUDGET;
+			tasklet_schedule(&np->rx_tasklet);
+		}
 
-		if (intr_status & IntrTxDone) {
+		if (intr_status & (IntrTxDone | IntrDrvRqst)) {
 			int boguscnt = 32;
-			int tx_status = readw(ioaddr + TxStatus);
+			int tx_status = readw (ioaddr + TxStatus);
 			while (tx_status & 0x80) {
 				if (debug > 4)
-					printk("%s: Transmit status is %2.2x.\n",
-						   dev->name, tx_status);
+					printk
+					    ("%s: Transmit status is %2.2x.\n",
+					     dev->name, tx_status);
 				if (tx_status & 0x1e) {
 					np->stats.tx_errors++;
-					if (tx_status & 0x10)  np->stats.tx_fifo_errors++;
+					if (tx_status & 0x10)
+						np->stats.tx_fifo_errors++;
 #ifdef ETHER_STATS
-					if (tx_status & 0x08)  np->stats.collisions16++;
+					if (tx_status & 0x08)
+						np->stats.collisions16++;
 #else
-					if (tx_status & 0x08)  np->stats.collisions++;
+					if (tx_status & 0x08)
+						np->stats.collisions++;
 #endif
-					if (tx_status & 0x04)  np->stats.tx_fifo_errors++;
-					if (tx_status & 0x02)  np->stats.tx_window_errors++;
+					if (tx_status & 0x02)
+						np->stats.tx_window_errors++;
 					/* This reset has not been verified!. */
-					if (tx_status & 0x10) {			/* Reset the Tx. */
-						writew(0x001c, ioaddr + ASICCtrl + 2);
-#if 0					/* Do we need to reset the Tx pointer here? */
-						writel(np->tx_ring_dma
-							+ np->dirty_tx*sizeof(*np->tx_ring),
-							dev->base_addr + TxListPtr);
-#endif
+					if (tx_status & 0x10) {	/* Reset the Tx. */
+						np->stats.tx_fifo_errors++;
+						spin_lock(&np->lock);
+						reset_tx(dev, 1);
+						spin_unlock(&np->lock);
 					}
-					if (tx_status & 0x1e) 		/* Restart the Tx. */
-						writew(TxEnable, ioaddr + MACCtrl1);
+					if (tx_status & 0x1e)	/* Restart the Tx. */
+						writew (TxEnable,
+							ioaddr + MACCtrl1);
 				}
 				/* Yup, this is a documentation bug.  It cost me *hours*. */
-				writew(0, ioaddr + TxStatus);
-				tx_status = readb(ioaddr + TxStatus);
+				writew (0, ioaddr + TxStatus);
+				tx_status = readw (ioaddr + TxStatus);
 				if (--boguscnt < 0)
 					break;
 			}
 		}
+		spin_lock(&np->lock);
 		for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
 			int entry = np->dirty_tx % TX_RING_SIZE;
 			struct sk_buff *skb;
-
-			if ( ! (np->tx_ring[entry].status & 0x00010000))
+			if (!(np->tx_ring[entry].status & 0x00010000))
 				break;
 			skb = np->tx_skbuff[entry];
 			/* Free the original skb. */
 			pci_unmap_single(np->pci_dev, 
 				np->tx_ring[entry].frag[0].addr, 
 				skb->len, PCI_DMA_TODEVICE);
-			dev_kfree_skb_irq(skb);
+			dev_kfree_skb_irq (np->tx_skbuff[entry]);
 			np->tx_skbuff[entry] = 0;
 		}
-		if (np->tx_full
-			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+		spin_unlock(&np->lock);
+		if (netif_queue_stopped(dev) && 
+			np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
 			/* The ring is no longer full, clear tbusy. */
-			np->tx_full = 0;
-			netif_wake_queue(dev);
+			netif_wake_queue (dev);
 		}
 
 		/* Abnormal error summary/uncommon events handlers. */
-		if (intr_status & (IntrDrvRqst | IntrPCIErr | LinkChange | StatsMax))
+		if (intr_status & (IntrPCIErr | LinkChange | StatsMax))
 			netdev_error(dev, intr_status);
 		if (--boguscnt < 0) {
 			get_stats(dev);
@@ -1073,49 +1169,41 @@
 				printk(KERN_WARNING "%s: Too much work at interrupt, "
 				   "status=0x%4.4x / 0x%4.4x.\n",
 				   dev->name, intr_status, readw(ioaddr + IntrClear));
-			/* Re-enable us in 3.2msec. */
-			writew(0, ioaddr + IntrEnable);
-			writew(1000, ioaddr + DownCounter);
-			writew(IntrDrvRqst, ioaddr + IntrEnable);
 			break;
 		}
 	} while (1);
-
 	if (debug > 3)
 		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
 			   dev->name, readw(ioaddr + IntrStatus));
+	if (np->cur_tx - np->dirty_tx > 0 && tx_coalesce > 1)
+		writel(100, ioaddr + DownCounter);
 
-	spin_unlock(&np->lock);
 }
 
-/* This routine is logically part of the interrupt handler, but separated
-   for clarity and better register allocation. */
-static int netdev_rx(struct net_device *dev)
+static void rx_poll(unsigned long data)
 {
+	struct net_device *dev = (struct net_device *)data;
 	struct netdev_private *np = dev->priv;
 	int entry = np->cur_rx % RX_RING_SIZE;
-	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
-
-	if (debug > 4) {
-		printk(KERN_DEBUG " In netdev_rx(), entry %d status %4.4x.\n",
-			   entry, np->rx_ring[entry].status);
-	}
+	int boguscnt = np->budget;
+	long ioaddr = dev->base_addr;
+	int received = 0;
 
 	/* If EOP is set on the next entry, it's a new packet. Send it up. */
 	while (1) {
 		struct netdev_desc *desc = &(np->rx_ring[entry]);
-		u32 frame_status;
+		u32 frame_status = le32_to_cpu(desc->status);
 		int pkt_len;
 
+		if (--boguscnt < 0) {
+			goto not_done;
+		}
 		if (!(desc->status & DescOwn))
 			break;
-		frame_status = le32_to_cpu(desc->status);
 		pkt_len = frame_status & 0x1fff;	/* Chip omits the CRC. */
 		if (debug > 4)
 			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n",
 				   frame_status);
-		if (--boguscnt < 0)
-			break;
 		pci_dma_sync_single(np->pci_dev, desc->frag[0].addr,
 			np->rx_buf_sz, PCI_DMA_FROMDEVICE);
 		
@@ -1136,7 +1224,6 @@
 			}
 		} else {
 			struct sk_buff *skb;
-
 #ifndef final_version
 			if (debug > 4)
 				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
@@ -1164,11 +1251,36 @@
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 		}
-		entry = (++np->cur_rx) % RX_RING_SIZE;
+		entry = (entry + 1) % RX_RING_SIZE;
+		received++;
 	}
+	np->cur_rx = entry;
+	refill_rx (dev);
+	np->budget -= received;
+	writew(DEFAULT_INTR, ioaddr + IntrEnable);
+	return;
+
+not_done:	
+	np->cur_rx = entry;
+	refill_rx (dev);
+	if (!received)
+		received = 1;
+	np->budget -= received;
+	if (np->budget <= 0)
+		np->budget = RX_BUDGET;
+	tasklet_schedule(&np->rx_tasklet);
+	return;
+}
+
+static void refill_rx (struct net_device *dev)
+{
+	struct netdev_private *np = dev->priv;
+	int entry;
+	int cnt = 0;
 
 	/* Refill the Rx ring buffers. */
-	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+	for (;(np->cur_rx - np->dirty_rx + RX_RING_SIZE) % RX_RING_SIZE > 0;
+		np->dirty_rx = (np->dirty_rx + 1) % RX_RING_SIZE) {
 		struct sk_buff *skb;
 		entry = np->dirty_rx % RX_RING_SIZE;
 		if (np->rx_skbuff[entry] == NULL) {
@@ -1186,56 +1298,51 @@
 		np->rx_ring[entry].frag[0].length =
 			cpu_to_le32(np->rx_buf_sz | LastFrag);
 		np->rx_ring[entry].status = 0;
+		cnt++;
 	}
-
-	/* No need to restart Rx engine, it will poll. */
-	return 0;
+	return;
 }
-
 static void netdev_error(struct net_device *dev, int intr_status)
 {
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
 	u16 mii_ctl, mii_advertise, mii_lpa;
 	int speed;
-
-	if (intr_status & IntrDrvRqst) {
-		/* Stop the down counter and turn interrupts back on. */
-		if (debug > 1)
-			printk("%s: Turning interrupts back on.\n", dev->name);
-		writew(0, ioaddr + IntrEnable);
-		writew(0, ioaddr + DownCounter);
-		writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst |
-			   IntrTxDone | StatsMax | LinkChange, ioaddr + IntrEnable);
-		/* Ack buggy InRequest */
-		writew (IntrDrvRqst, ioaddr + IntrStatus);
-	}
+	
 	if (intr_status & LinkChange) {
 		if (np->an_enable) {
 			mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
 			mii_lpa= mdio_read (dev, np->phys[0], MII_LPA);
 			mii_advertise &= mii_lpa;
 			printk (KERN_INFO "%s: Link changed: ", dev->name);
-			if (mii_advertise & ADVERTISE_100FULL)
+			if (mii_advertise & ADVERTISE_100FULL) {
+				np->speed = 100;
 				printk ("100Mbps, full duplex\n");
-			else if (mii_advertise & ADVERTISE_100HALF)
+			} else if (mii_advertise & ADVERTISE_100HALF) {
+				np->speed = 100;
 				printk ("100Mbps, half duplex\n");
-			else if (mii_advertise & ADVERTISE_10FULL)
+			} else if (mii_advertise & ADVERTISE_10FULL) {
+				np->speed = 10;
 				printk ("10Mbps, full duplex\n");
-			else if (mii_advertise & ADVERTISE_10HALF)
+			} else if (mii_advertise & ADVERTISE_10HALF) {
+				np->speed = 10;
 				printk ("10Mbps, half duplex\n");
-			else
+			} else
 				printk ("\n");
 
 		} else {
 			mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
 			speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
+			np->speed = speed;
 			printk (KERN_INFO "%s: Link changed: %dMbps ,",
 				dev->name, speed);
 			printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
 				"full" : "half");
 		}
 		check_duplex (dev);
+		if (np->flowctrl == 0)
+			writew(readw(ioaddr + MACCtrl0) & ~EnbFlowCtrl,
+				ioaddr + MACCtrl0);
 	}
 	if (intr_status & StatsMax) {
 		get_stats(dev);
@@ -1294,11 +1401,16 @@
 		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
 	} else if (dev->mc_count) {
 		struct dev_mc_list *mclist;
-		memset(mc_filter, 0, sizeof(mc_filter));
+		int bit;
+		int index;
+		int crc;
+		memset (mc_filter, 0, sizeof (mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-			 i++, mclist = mclist->next) {
-			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
-					mc_filter);
+		     i++, mclist = mclist->next) {
+			crc = ether_crc_le (ETH_ALEN, mclist->dmi_addr);
+			for (index=0, bit=0; bit < 6; bit++, crc <<= 1)
+				if (crc & 0x80000000) index |= 1 << bit;
+			mc_filter[index/16] |= (1 << (index % 16));
 		}
 		rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys;
 	} else {
@@ -1314,24 +1426,136 @@
 {
 	struct netdev_private *np = dev->priv;
 	u32 ethcmd;
-		
+	long ioaddr = dev->base_addr;
+	
 	if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
 		return -EFAULT;
 
         switch (ethcmd) {
-        case ETHTOOL_GDRVINFO: {
-		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
-		strcpy(info.driver, DRV_NAME);
-		strcpy(info.version, DRV_VERSION);
-		strcpy(info.bus_info, np->pci_dev->slot_name);
-		if (copy_to_user(useraddr, &info, sizeof(info)))
+        	case ETHTOOL_GDRVINFO: {
+			struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+			strcpy(info.driver, DRV_NAME);
+			strcpy(info.version, DRV_VERSION);
+			strcpy(info.bus_info, np->pci_dev->slot_name);
+			memset(&info.fw_version, 0, sizeof(info.fw_version));
+			if (copy_to_user(useraddr, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		}
+		case ETHTOOL_GSET: {
+			struct ethtool_cmd cmd = { ETHTOOL_GSET };
+			if (readl (ioaddr + ASICCtrl) & 0x80) {
+				/* fiber device */
+				cmd.supported = SUPPORTED_Autoneg | 
+							SUPPORTED_FIBRE;
+				cmd.advertising= ADVERTISED_Autoneg |
+							ADVERTISED_FIBRE;
+				cmd.port = PORT_FIBRE;
+				cmd.transceiver = XCVR_INTERNAL;	
+			} else {
+				/* copper device */
+				cmd.supported = SUPPORTED_10baseT_Half | 
+					SUPPORTED_10baseT_Full |
+				       	SUPPORTED_100baseT_Half	|
+				       	SUPPORTED_100baseT_Full | 
+					SUPPORTED_Autoneg | 
+					SUPPORTED_MII;
+				cmd.advertising = ADVERTISED_10baseT_Half |
+						ADVERTISED_10baseT_Full |
+				       		ADVERTISED_100baseT_Half |
+						ADVERTISED_100baseT_Full | 
+						ADVERTISED_Autoneg |
+				       		ADVERTISED_MII;
+				cmd.port = PORT_MII;
+				cmd.transceiver = XCVR_INTERNAL;
+			}
+			if (readb(ioaddr + MIICtrl) & 0x80) {
+				cmd.speed = np->speed;
+				cmd.duplex = np->full_duplex ? 
+						    DUPLEX_FULL : DUPLEX_HALF;
+			} else {
+				cmd.speed = -1;
+				cmd.duplex = -1;
+			}
+			if ( np->an_enable)
+				cmd.autoneg = AUTONEG_ENABLE;
+			else
+				cmd.autoneg = AUTONEG_DISABLE;
+			
+			cmd.phy_address = np->phys[0];
+
+			if (copy_to_user(useraddr, &cmd,
+					sizeof(cmd)))
+				return -EFAULT;
+			return 0;				   
+		}
+		case ETHTOOL_SSET: {
+			struct ethtool_cmd cmd;
+			if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+				return -EFAULT;
+			netif_carrier_off(dev);
+			if (cmd.autoneg == AUTONEG_ENABLE) {
+				if (np->an_enable)
+					return 0;
+				else {
+					np->an_enable = 1;
+					/* Reset PHY */
+					mdio_write (dev, np->phys[0], MII_BMCR,
+						       		BMCR_RESET);
+					mdelay (300);
+					/* Start auto negotiation */
+					mdio_write (dev, np->phys[0], MII_BMCR,
+					      	BMCR_ANENABLE|BMCR_ANRESTART);
+					return 0;	
+				}	
+			} else {
+				/* Reset PHY */
+				mdio_write (dev, np->phys[0], MII_BMCR, 
+								BMCR_RESET);
+				mdelay (300);
+				np->an_enable = 0;
+				switch(cmd.speed + cmd.duplex){
+				
+					case SPEED_10 + DUPLEX_HALF:
+						np->speed = 10;
+						np->full_duplex = 0;
+						break;
+					case SPEED_10 + DUPLEX_FULL:
+						np->speed = 10;
+						np->full_duplex = 1;
+						break;
+					case SPEED_100 + DUPLEX_HALF:
+						np->speed = 100;
+						np->full_duplex = 0;
+						break;
+					case SPEED_100 + DUPLEX_FULL:
+						np->speed = 100;
+						np->full_duplex = 1;
+						break;
+				
+				default:
+					return -EINVAL;	
+				}
+		mdio_write (dev, np->phys[0], MII_BMCR,
+				((np->speed == 100) ? BMCR_SPEED100 : 0) | 
+				((np->full_duplex) ? BMCR_FULLDPLX : 0) );
+
+			}
+		return 0;		   
+		}
+#ifdef	ETHTOOL_GLINK
+		case ETHTOOL_GLINK:{
+		struct ethtool_value link = { ETHTOOL_GLINK };
+		link.data = readb(ioaddr + MIICtrl) & 0x80;
+		if (copy_to_user(useraddr, &link, sizeof(link)))
 			return -EFAULT;
 		return 0;
-	}
+		}			   
+#endif
+		default:
+		return -EOPNOTSUPP;
 
         }
-	
-	return -EOPNOTSUPP;
 }
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
@@ -1342,17 +1566,14 @@
 	case SIOCETHTOOL:
 		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
 	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
-	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
 		data->phy_id = ((struct netdev_private *)dev->priv)->phys[0] & 0x1f;
 		/* Fall Through */
 
 	case SIOCGMIIREG:		/* Read MII PHY register. */
-	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
 		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
 		return 0;
 
 	case SIOCSMIIREG:		/* Write MII PHY register. */
-	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
@@ -1480,3 +1701,5 @@
 
 module_init(sundance_init);
 module_exit(sundance_exit);
+
+

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] 2.4.20-pre sundance.c update
  2002-09-19  4:25       ` [PATCH] 2.4.20-pre sundance.c update Jeff Garzik
@ 2002-09-19  4:56         ` Jason Lunz
  2002-09-19  5:11           ` Jeff Garzik
  2002-09-19 13:23         ` Donald Becker
  1 sibling, 1 reply; 18+ messages in thread
From: Jason Lunz @ 2002-09-19  4:56 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Richard Gooch, becker, Patrick R. McManus,
	Linux Kernel Mailing List

On Thu, Sep 19, 2002 at 12:25AM -0400, Jeff Garzik wrote:
> It still has several flaws that were pointed out, but this is the base 
> from which I would like testing and patching to proceed.  (also 
> hopefully the flaws are minor in terms of general operation)

what's the point of moving rx handling into rx_poll then running it in a
tasklet? I've tested an older variant of that scheme from D-Link and it
doesn't perform as well as my patch. It looks to me like an attempt to
keep this version synced with the NAPI version of the driver, but it
doesn't actually work very well.

The functional part of my patch was just taking the tx handling from
d-link's driver and ditching the rx part.  That and merging in the
cleanups from Becker's driver; most notably ignoring the broken
IntrRxDone bit.

Jason

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] 2.4.20-pre sundance.c update
  2002-09-19  4:56         ` Jason Lunz
@ 2002-09-19  5:11           ` Jeff Garzik
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19  5:11 UTC (permalink / raw)
  To: Jason Lunz
  Cc: netdev, Richard Gooch, becker, Patrick R. McManus,
	Linux Kernel Mailing List

Jason Lunz wrote:
> On Thu, Sep 19, 2002 at 12:25AM -0400, Jeff Garzik wrote:
> 
>>It still has several flaws that were pointed out, but this is the base 
>>from which I would like testing and patching to proceed.  (also 
>>hopefully the flaws are minor in terms of general operation)
> 
> 
> what's the point of moving rx handling into rx_poll then running it in a
> tasklet? I've tested an older variant of that scheme from D-Link and it
> doesn't perform as well as my patch. It looks to me like an attempt to
> keep this version synced with the NAPI version of the driver, but it
> doesn't actually work very well.

This is a merge and test point.  The whole interrupt handler path is 
getting updated after this.  (but thanks for the feedback, it is noted)


> The functional part of my patch was just taking the tx handling from
> d-link's driver and ditching the rx part.  That and merging in the
> cleanups from Becker's driver; most notably ignoring the broken
> IntrRxDone bit.


Maybe you could show me that in broken-out patches :)


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH] 2.4.20-pre sundance.c update
  2002-09-19  4:25       ` [PATCH] 2.4.20-pre sundance.c update Jeff Garzik
  2002-09-19  4:56         ` Jason Lunz
@ 2002-09-19 13:23         ` Donald Becker
  2002-09-19 17:12           ` PATCH: sundance #2 Jeff Garzik
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
  1 sibling, 2 replies; 18+ messages in thread
From: Donald Becker @ 2002-09-19 13:23 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Jason Lunz, Richard Gooch, Patrick R. McManus,
	Linux Kernel Mailing List

On Thu, 19 Sep 2002, Jeff Garzik wrote:

> Attached is the patch I have against 2.4.20-pre-latest, to start fixing 
> from as a baseline.

diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
> --- a/drivers/net/sundance.c	Thu Sep 19 00:22:26 2002
> +++ b/drivers/net/sundance.c	Thu Sep 19 00:22:26 2002
...
>  /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
> -static int max_interrupt_work = 30;
> +static int max_interrupt_work = 0;

Please don't change the semantics of module parameters.  All of my PCI
network drivers have used this name for years with the same semantics.
Arbitrarily changing is A Bad Thing.

> static int mtu;

Get rid of this.  See the MTU setting code in my driver release.

>     bonding and packet priority, and more than 128 requires modifying the
>     Tx error recovery.
>     Large receive rings merely waste memory. */
> -#define TX_RING_SIZE	16
> -#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
> -#define RX_RING_SIZE	32
> +#define TX_RING_SIZE	64
> +#define TX_QUEUE_LEN	(TX_RING_SIZE - 1) /* Limit ring entries actually used.  */

Did you miss reading the comment?  A Tx ring size of 64 is a bad idea.
Increasing the Rx ring size is fine, considering that most current
machines have plenty of memory and the kernel hasn't always been good
about keeping skbuffs available.

+MODULE_PARM(flowctrl, "i");

Flow control should be negotiated, not set.

> -	TxThreshold = 0x3c,
> +	TxStartThresh = 0x3c,
> +	RxEarlyThresh = 0x3e,

[[ Many other name changes omitted ]]

Why change the symbolic names for the registers?

> 	/* Frequently used values: keep some adjacent for cache effect. */
> 	spinlock_t lock;
> +	spinlock_t rx_lock;					/* Group with Tx control cache line. */

Uhmmm, again, read the comment.  This comment was originally with the
'txlock' variable

 	} else if (dev->mc_count) {
 		struct dev_mc_list *mclist;
-		memset(mc_filter, 0, sizeof(mc_filter));
+		int bit;
+		int index;
+		int crc;
+		memset (mc_filter, 0, sizeof (mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-			 i++, mclist = mclist->next) {
-			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
-					mc_filter);
+		     i++, mclist = mclist->next) {
+			crc = ether_crc_le (ETH_ALEN, mclist->dmi_addr);
+			for (index=0, bit=0; bit < 6; bit++, crc <<= 1)
+				if (crc & 0x80000000) index |= 1 << bit;
+			mc_filter[index/16] |= (1 << (index % 16));
 		}

Uhhmmm, perhaps calling a function to do the wrong-endian CRC and then
bit-reversing the result is not an improved way to do this?  That's why
there needs to be two Ethernet CRC functions -- so that you don't
need to bit bit-reverse the result.


-- 
Donald Becker				becker@scyld.com
Scyld Computing Corporation		http://www.scyld.com
410 Severn Ave. Suite 210		Second Generation Beowulf Clusters
Annapolis MD 21403			410-990-9993


^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #2
  2002-09-19 13:23         ` Donald Becker
@ 2002-09-19 17:12           ` Jeff Garzik
  2002-09-19 17:29             ` Donald Becker
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
  1 sibling, 1 reply; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 17:12 UTC (permalink / raw)
  To: Donald Becker
  Cc: netdev, Jason Lunz, Richard Gooch, Patrick R. McManus,
	Linux Kernel Mailing List, edward_peng

[-- Attachment #1: Type: text/plain, Size: 323 bytes --]

Thanks to Donald for his comments.  This patch addresses the first of 
his two emails.

This patch is _cumulative_ with the last one I sent (sundance 1.04), so 
do not discard that one.

Again additional testing is appreciated.  Keep the feedback coming, 
there will be more sundance bugfixes (patch #3, #4, etc.)

	Jeff



[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 7711 bytes --]

diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 13:05:53 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 13:05:53 2002
@@ -18,26 +18,34 @@
 	http://www.scyld.com/network/sundance.html
 
 
-	Version 1.01a (jgarzik):
+	Version LK1.01a (jgarzik):
 	- Replace some MII-related magic numbers with constants
 
-	Version 1.02 (D-Link):
+	Version LK1.02 (D-Link):
 	- Add new board to PCI ID list
 	- Fix multicast bug
 	
-	Version 1.03 (D-Link):
+	Version LK1.03 (D-Link):
 	- New Rx scheme, reduce Rx congestion
 	- Option to disable flow control
 	
-	Version 1.04 (D-Link):
+	Version LK1.04 (D-Link):
 	- Tx timeout recovery
 	- More support for ethtool.
 
+	Version LK1.04a (jgarzik):
+	- Remove unused/constant members from struct pci_id_info
+	(which then allows removal of 'drv_flags' from private struct)
+	- If no phy is found, fail to load that board
+	- Always start phy id scan at id 1 to avoid problems (Donald Becker)
+	- Autodetect where mii_preable_required is needed,
+	default to not needed.  (Donald Becker)
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.04"
-#define DRV_RELDATE	"19-Aug-2002"
+#define DRV_VERSION	"1.01+LK1.04a"
+#define DRV_RELDATE	"19-Sep-2002"
 
 
 /* The user-configurable values.
@@ -72,6 +80,12 @@
 */
 #define MAX_UNITS 8	
 static char *media[MAX_UNITS];
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with older tranceivers, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static int mii_preamble_required = 0;
+
 /* Operational parameters that are set at compile time. */
 
 /* Keep the ring sizes a power of two for compile efficiency.
@@ -145,11 +159,14 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
 MODULE_PARM(flowctrl, "i");
+MODULE_PARM(mii_preamble_required, "i");
 MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
+MODULE_PARM_DESC(mii_preamble_required, "Set to send a preamble before MII management transactions");
+
 /*
 				Theory of Operation
 
@@ -225,20 +242,6 @@
 */
 
 
-enum pci_id_flags_bits {
-        /* Set PCI command register bits before calling probe1(). */
-        PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
-        /* Read and map the single following PCI BAR. */
-        PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
-        PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
-};
-enum chip_capability_flags {CanHaveMII=1, };
-#ifdef USE_IO_OPS
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO  | PCI_ADDR0)
-#else
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
-#endif
-
 static struct pci_device_id sundance_pci_tbl[] __devinitdata = {
 	{0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},
 	{0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1},
@@ -250,31 +253,20 @@
 };
 MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
 
+enum {
+	netdev_io_size = 128
+};
+
 struct pci_id_info {
         const char *name;
-        struct match_info {
-                int     pci, pci_mask, subsystem, subsystem_mask;
-                int revision, revision_mask;                            /* Only 8 bits. */
-        } id;
-        enum pci_id_flags_bits pci_flags;
-        int io_size;                            /* Needed for I/O region check or ioremap(). */
-        int drv_flags;                          /* Driver use, intended as capability flags. */
 };
 static struct pci_id_info pci_id_tbl[] = {
-	{"D-Link DFE-550TX FAST Ethernet Adapter", {0x10021186, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
-	{"D-Link DFE-550FX 100Mbps Fiber-optics Adapter",
-	 {0x10031186, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
-	{"D-Link DFE-580TX 4 port Server Adapter", {0x10121186, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
-	{"D-Link DFE-530TXS FAST Ethernet Adapter", {0x10021186, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
-	{"D-Link DL10050-based FAST Ethernet Adapter",
-	 {0x10021186, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
-	{"Sundance Technology Alta", {0x020113F0, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
+	{"D-Link DFE-550TX FAST Ethernet Adapter"},
+	{"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"},
+	{"D-Link DFE-580TX 4 port Server Adapter"},
+	{"D-Link DFE-530TXS FAST Ethernet Adapter"},
+	{"D-Link DL10050-based FAST Ethernet Adapter"},
+	{"Sundance Technology Alta"},
 	{0,},			/* 0 terminated list. */
 };
 
@@ -430,7 +422,7 @@
 	/* Frequently used values: keep some adjacent for cache effect. */
 	spinlock_t lock;
 	spinlock_t rx_lock;					/* Group with Tx control cache line. */
-	int chip_id, drv_flags;
+	int chip_id;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
 	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
 	struct netdev_desc *last_tx;		/* Last Tx descriptor used. */
@@ -447,7 +439,6 @@
 	spinlock_t mcastlock;				/* SMP lock multicast updates. */
 	u16 mcast_filter[4];
 	/* MII transceiver section. */
-	int mii_cnt;						/* MII device addresses. */
 	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[MII_CNT];		/* MII device addresses, only first one used. */
 	struct pci_dev *pci_dev;
@@ -521,7 +512,7 @@
 	ioaddr = pci_resource_start(pdev, 0);
 #else
 	ioaddr = pci_resource_start(pdev, 1);
-	ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
+	ioaddr = (long) ioremap (ioaddr, netdev_io_size);
 	if (!ioaddr)
 		goto err_out_res;
 #endif
@@ -535,7 +526,6 @@
 
 	np = dev->priv;
 	np->chip_id = chip_idx;
-	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
 	np->pci_dev = pdev;
 	spin_lock_init(&np->lock);
 	tasklet_init(&np->rx_tasklet, rx_poll, (unsigned long)dev);
@@ -579,21 +569,28 @@
 	if (1) {
 		int phy, phy_idx = 0;
 		np->phys[0] = 1;		/* Default setting */
-		for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) {
-			int mii_status = mdio_read(dev, phy, 1);
+		mii_preamble_required++;
+		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
+			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
-				np->advertising = mdio_read(dev, phy, 4);
+				np->advertising = mdio_read(dev, phy, MII_ADVERTISE);
+				if ((mii_status & 0x0040) == 0)
+					mii_preamble_required++;
 				printk(KERN_INFO "%s: MII PHY found at address %d, status "
 					   "0x%4.4x advertising %4.4x.\n",
 					   dev->name, phy, mii_status, np->advertising);
 			}
 		}
-		np->mii_cnt = phy_idx;
-		if (phy_idx == 0)
-			printk(KERN_INFO "%s: No MII transceiver found!, ASIC status %x\n",
+		mii_preamble_required--;
+
+		if (phy_idx == 0) {
+			printk(KERN_INFO "%s: No MII transceiver found, aborting.  ASIC status %x\n",
 				   dev->name, readl(ioaddr + ASICCtrl));
+			goto err_out_unmap_rx;
+		}
 	}
+
 	/* Parse override configuration */
 	np->an_enable = 1;
 	if (card_idx < MAX_UNITS) {
@@ -700,11 +697,6 @@
 	The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
 	met by back-to-back 33Mhz PCI cycles. */
 #define mdio_delay() readb(mdio_addr)
-
-/* Set iff a MII transceiver on any interface requires mdio preamble.
-   This only set with older tranceivers, so the extra
-   code size of a per-interface flag is not worthwhile. */
-static const char mii_preamble_required = 1;
 
 enum mii_reg_bits {
 	MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004,

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #2
  2002-09-19 17:12           ` PATCH: sundance #2 Jeff Garzik
@ 2002-09-19 17:29             ` Donald Becker
  2002-09-19 18:13               ` Jeff Garzik
  2002-09-19 18:18               ` PATCH: sundance #3 Jeff Garzik
  0 siblings, 2 replies; 18+ messages in thread
From: Donald Becker @ 2002-09-19 17:29 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Jason Lunz, Richard Gooch, Patrick R. McManus,
	Linux Kernel Mailing List, edward_peng

On Thu, 19 Sep 2002, Jeff Garzik wrote:

> Thanks to Donald for his comments.  This patch addresses the first of 
> his two emails.
> 
> This patch is _cumulative_ with the last one I sent (sundance 1.04), so 
> do not discard that one.
> 
> Again additional testing is appreciated.  Keep the feedback coming, 
> there will be more sundance bugfixes (patch #3, #4, etc.)

+	- If no phy is found, fail to load that board
+	- Always start phy id scan at id 1 to avoid problems (Donald Becker)
+	- Autodetect where mii_preable_required is needed,
+	default to not needed.  (Donald Becker)
...
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with older tranceivers, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static int mii_preamble_required = 0;

You can get rid of this as a module option, and make it a per-interface
setting. 
The transceiver on the Kendin chip requires this (rather old-fashioned)
access method, while none of the previous Sundance-based boards with
external transceivers did.

I added it as a module parameter as a back-up over-ride, but I'm certain
that the automatic detection works.

This is a module parameter because I recently had a bad experience
with a specific 3Com 3c905B chip rev.  It claimed to not need
transceiver preamble, but would not work without it.

> 				Theory of Operation

Whoever changed the transmit path should update the TOO.  

-	{"Sundance Technology Alta", {0x020113F0, 0xffffffff,},
-	 PCI_IOTYPE, 128, CanHaveMII},
+	{"D-Link DFE-550TX FAST Ethernet Adapter"},
+	{"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"},

Yeah, you should probably throw away the rest of the changes.
You are probably going to want to keep the drv_flags field.  I know
that all of the current chips have the same flag (CanHaveMII), but...



-- 
Donald Becker				becker@scyld.com
Scyld Computing Corporation		http://www.scyld.com
410 Severn Ave. Suite 210		Second Generation Beowulf Clusters
Annapolis MD 21403			410-990-9993


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #2
  2002-09-19 17:29             ` Donald Becker
@ 2002-09-19 18:13               ` Jeff Garzik
  2002-09-19 18:18               ` PATCH: sundance #3 Jeff Garzik
  1 sibling, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 18:13 UTC (permalink / raw)
  To: Donald Becker
  Cc: netdev, Jason Lunz, Richard Gooch, Patrick R. McManus,
	Linux Kernel Mailing List, edward_peng

Donald Becker wrote:
> +/* Set iff a MII transceiver on any interface requires mdio preamble.
> +   This only set with older tranceivers, so the extra
> +   code size of a per-interface flag is not worthwhile. */
> +static int mii_preamble_required = 0;
> 
> You can get rid of this as a module option, and make it a per-interface
> setting. 
> The transceiver on the Kendin chip requires this (rather old-fashioned)
> access method, while none of the previous Sundance-based boards with
> external transceivers did.
> 
> I added it as a module parameter as a back-up over-ride, but I'm certain
> that the automatic detection works.

Good enough for me...


>>				Theory of Operation
> 
> 
> Whoever changed the transmit path should update the TOO.  

noted



> -	{"Sundance Technology Alta", {0x020113F0, 0xffffffff,},
> -	 PCI_IOTYPE, 128, CanHaveMII},
> +	{"D-Link DFE-550TX FAST Ethernet Adapter"},
> +	{"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"},
> 
> Yeah, you should probably throw away the rest of the changes.
> You are probably going to want to keep the drv_flags field.  I know
> that all of the current chips have the same flag (CanHaveMII), but...


That's probably a style area that you and I will disagree on... :)

	Jeff



^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #3
  2002-09-19 17:29             ` Donald Becker
  2002-09-19 18:13               ` Jeff Garzik
@ 2002-09-19 18:18               ` Jeff Garzik
  1 sibling, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 18:18 UTC (permalink / raw)
  To: Donald Becker
  Cc: netdev, Jason Lunz, Richard Gooch, Patrick R. McManus,
	Linux Kernel Mailing List, edward_peng

[-- Attachment #1: Type: text/plain, Size: 110 bytes --]

The obvious sundance #3 patch follows...  cumulative to the 
sundance-1.04 base patch, and sundance #2 patch.

[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 5293 bytes --]

diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 14:16:21 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 14:16:21 2002
@@ -41,10 +41,17 @@
 	- Autodetect where mii_preable_required is needed,
 	default to not needed.  (Donald Becker)
 
+	Version LK1.04b:
+	- Remove mii_preamble_required module parameter (Donald Becker)
+	- Add per-interface mii_preamble_required (setting is autodetected)
+	  (Donald Becker)
+	- Remove unnecessary cast from void pointer
+	- Re-align comments in private struct
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01+LK1.04a"
+#define DRV_VERSION	"1.01+LK1.04b"
 #define DRV_RELDATE	"19-Sep-2002"
 
 
@@ -81,10 +88,6 @@
 #define MAX_UNITS 8	
 static char *media[MAX_UNITS];
 
-/* Set iff a MII transceiver on any interface requires mdio preamble.
-   This only set with older tranceivers, so the extra
-   code size of a per-interface flag is not worthwhile. */
-static int mii_preamble_required = 0;
 
 /* Operational parameters that are set at compile time. */
 
@@ -159,13 +162,11 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
 MODULE_PARM(flowctrl, "i");
-MODULE_PARM(mii_preamble_required, "i");
 MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
-MODULE_PARM_DESC(mii_preamble_required, "Set to send a preamble before MII management transactions");
 
 /*
 				Theory of Operation
@@ -418,17 +419,17 @@
         dma_addr_t tx_ring_dma;
         dma_addr_t rx_ring_dma;
 	struct net_device_stats stats;
-	struct timer_list timer;	/* Media monitoring timer. */
+	struct timer_list timer;		/* Media monitoring timer. */
 	/* Frequently used values: keep some adjacent for cache effect. */
 	spinlock_t lock;
-	spinlock_t rx_lock;					/* Group with Tx control cache line. */
+	spinlock_t rx_lock;			/* Group with Tx control cache line. */
 	int chip_id;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
-	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
+	unsigned int rx_buf_sz;			/* Based on MTU+slack. */
 	struct netdev_desc *last_tx;		/* Last Tx descriptor used. */
 	unsigned int cur_tx, dirty_tx;
 	/* These values are keep track of the transceiver/media in use. */
-	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
+	unsigned int full_duplex:1;		/* Full-duplex operation requested. */
 	unsigned int flowctrl:1;
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	unsigned int an_enable:1;
@@ -436,10 +437,11 @@
 	struct tasklet_struct rx_tasklet;
 	int budget;
 	/* Multicast and receive mode. */
-	spinlock_t mcastlock;				/* SMP lock multicast updates. */
+	spinlock_t mcastlock;			/* SMP lock multicast updates. */
 	u16 mcast_filter[4];
 	/* MII transceiver section. */
-	u16 advertising;					/* NWay media advertisement */
+	int mii_preamble_required;
+	u16 advertising;			/* NWay media advertisement */
 	unsigned char phys[MII_CNT];		/* MII device addresses, only first one used. */
 	struct pci_dev *pci_dev;
 };
@@ -569,20 +571,20 @@
 	if (1) {
 		int phy, phy_idx = 0;
 		np->phys[0] = 1;		/* Default setting */
-		mii_preamble_required++;
+		np->mii_preamble_required++;
 		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
 			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
 				np->advertising = mdio_read(dev, phy, MII_ADVERTISE);
 				if ((mii_status & 0x0040) == 0)
-					mii_preamble_required++;
+					np->mii_preamble_required++;
 				printk(KERN_INFO "%s: MII PHY found at address %d, status "
 					   "0x%4.4x advertising %4.4x.\n",
 					   dev->name, phy, mii_status, np->advertising);
 			}
 		}
-		mii_preamble_required--;
+		np->mii_preamble_required--;
 
 		if (phy_idx == 0) {
 			printk(KERN_INFO "%s: No MII transceiver found, aborting.  ASIC status %x\n",
@@ -722,11 +724,12 @@
 
 static int mdio_read(struct net_device *dev, int phy_id, int location)
 {
+	struct netdev_private *np = dev->priv;
 	long mdio_addr = dev->base_addr + MIICtrl;
 	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
 	int i, retval = 0;
 
-	if (mii_preamble_required)
+	if (np->mii_preamble_required)
 		mdio_sync(mdio_addr);
 
 	/* Shift the read command bits out. */
@@ -751,11 +754,12 @@
 
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value)
 {
+	struct netdev_private *np = dev->priv;
 	long mdio_addr = dev->base_addr + MIICtrl;
 	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
 	int i;
 
-	if (mii_preamble_required)
+	if (np->mii_preamble_required)
 		mdio_sync(mdio_addr);
 
 	/* Shift the command bits out. */
@@ -969,7 +973,7 @@
 static int
 start_tx (struct sk_buff *skb, struct net_device *dev)
 {
-	struct netdev_private *np = (struct netdev_private *) dev->priv;
+	struct netdev_private *np = dev->priv;
 	struct netdev_desc *txdesc;
 	unsigned entry;
 	long ioaddr = dev->base_addr;

^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #4
  2002-09-19 13:23         ` Donald Becker
  2002-09-19 17:12           ` PATCH: sundance #2 Jeff Garzik
@ 2002-09-19 19:30           ` Jeff Garzik
  2002-09-19 20:51             ` PATCH: sundance #4a Jason Lunz
                               ` (3 more replies)
  1 sibling, 4 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 19:30 UTC (permalink / raw)
  To: netdev, Linux Kernel Mailing List
  Cc: Donald Becker, Jason Lunz, Richard Gooch, Patrick R. McManus,
	edward_peng

[-- Attachment #1: Type: text/plain, Size: 359 bytes --]

while I was in there, and because it was easy, I ripped out the 
hand-coded ethtool stuff, and modernized a bit.  note that the lines 
that don't appear to have changes are lines which have been stripped of 
trailing whitespace.

Next, the issues that Jason pointed out with the hand-rolled RX polling, 
and the stuff Donald pointed out in his first message.

[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 21173 bytes --]

diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile	Thu Sep 19 15:17:05 2002
+++ b/drivers/net/Makefile	Thu Sep 19 15:17:05 2002
@@ -70,7 +70,7 @@
 obj-$(CONFIG_AIRONET4500_CS)	+= aironet4500_proc.o
 
 obj-$(CONFIG_WINBOND_840) += mii.o
-obj-$(CONFIG_SUNDANCE) += sundance.o
+obj-$(CONFIG_SUNDANCE) += sundance.o mii.o
 obj-$(CONFIG_HAMACHI) += hamachi.o
 obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o
 obj-$(CONFIG_SEEQ8005) += seeq8005.o
diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 15:17:05 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 15:17:05 2002
@@ -24,11 +24,11 @@
 	Version LK1.02 (D-Link):
 	- Add new board to PCI ID list
 	- Fix multicast bug
-	
+
 	Version LK1.03 (D-Link):
 	- New Rx scheme, reduce Rx congestion
 	- Option to disable flow control
-	
+
 	Version LK1.04 (D-Link):
 	- Tx timeout recovery
 	- More support for ethtool.
@@ -48,10 +48,15 @@
 	- Remove unnecessary cast from void pointer
 	- Re-align comments in private struct
 
+	Version LK1.04c:
+	- Support bitmapped message levels (NETIF_MSG_xxx), and the
+	  two ethtool ioctls that get/set them
+	- Don't hand-code MII ethtool support, use standard API/lib
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01+LK1.04b"
+#define DRV_VERSION	"1.01+LK1.04c"
 #define DRV_RELDATE	"19-Sep-2002"
 
 
@@ -85,7 +90,7 @@
 		 3	 	100Mbps half duplex.
 		 4	 	100Mbps full duplex.
 */
-#define MAX_UNITS 8	
+#define MAX_UNITS 8
 static char *media[MAX_UNITS];
 
 
@@ -353,7 +358,7 @@
 enum ASICCtrl_HiWord_bit {
 	GlobalReset = 0x0001,
 	RxReset = 0x0002,
-	TxReset = 0x0004, 
+	TxReset = 0x0004,
 	DMAReset = 0x0008,
 	FIFOReset = 0x0010,
 	NetworkReset = 0x0020,
@@ -423,13 +428,13 @@
 	/* Frequently used values: keep some adjacent for cache effect. */
 	spinlock_t lock;
 	spinlock_t rx_lock;			/* Group with Tx control cache line. */
+	int msg_enable;
 	int chip_id;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
 	unsigned int rx_buf_sz;			/* Based on MTU+slack. */
 	struct netdev_desc *last_tx;		/* Last Tx descriptor used. */
 	unsigned int cur_tx, dirty_tx;
 	/* These values are keep track of the transceiver/media in use. */
-	unsigned int full_duplex:1;		/* Full-duplex operation requested. */
 	unsigned int flowctrl:1;
 	unsigned int default_port:4;		/* Last dev->if_port value. */
 	unsigned int an_enable:1;
@@ -440,8 +445,8 @@
 	spinlock_t mcastlock;			/* SMP lock multicast updates. */
 	u16 mcast_filter[4];
 	/* MII transceiver section. */
+	struct mii_if_info mii_if;
 	int mii_preamble_required;
-	u16 advertising;			/* NWay media advertisement */
 	unsigned char phys[MII_CNT];		/* MII device addresses, only first one used. */
 	struct pci_dev *pci_dev;
 };
@@ -472,7 +477,7 @@
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct net_device *dev);
 
-\f
+
 
 static int __devinit sundance_probe1 (struct pci_dev *pdev,
 				      const struct pci_device_id *ent)
@@ -527,8 +532,9 @@
 	dev->irq = irq;
 
 	np = dev->priv;
-	np->chip_id = chip_idx;
 	np->pci_dev = pdev;
+	np->chip_id = chip_idx;
+	np->msg_enable = (1 << debug) - 1;
 	spin_lock_init(&np->lock);
 	tasklet_init(&np->rx_tasklet, rx_poll, (unsigned long)dev);
 
@@ -544,6 +550,10 @@
 	np->rx_ring = (struct netdev_desc *)ring_space;
 	np->rx_ring_dma = ring_dma;
 
+	np->mii_if.dev = dev;
+	np->mii_if.mdio_read = mdio_read;
+	np->mii_if.mdio_write = mdio_write;
+
 	/* The chip-specific entries in the device structure. */
 	dev->open = &netdev_open;
 	dev->hard_start_xmit = &start_tx;
@@ -576,12 +586,12 @@
 			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
-				np->advertising = mdio_read(dev, phy, MII_ADVERTISE);
+				np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE);
 				if ((mii_status & 0x0040) == 0)
 					np->mii_preamble_required++;
 				printk(KERN_INFO "%s: MII PHY found at address %d, status "
 					   "0x%4.4x advertising %4.4x.\n",
-					   dev->name, phy, mii_status, np->advertising);
+					   dev->name, phy, mii_status, np->mii_if.advertising);
 			}
 		}
 		np->mii_preamble_required--;
@@ -591,6 +601,8 @@
 				   dev->name, readl(ioaddr + ASICCtrl));
 			goto err_out_unmap_rx;
 		}
+
+		np->mii_if.phy_id = np->phys[0];
 	}
 
 	/* Parse override configuration */
@@ -601,24 +613,24 @@
 			if (strcmp (media[card_idx], "100mbps_fd") == 0 ||
 			    strcmp (media[card_idx], "4") == 0) {
 				np->speed = 100;
-				np->full_duplex = 1;
+				np->mii_if.full_duplex = 1;
 			} else if (strcmp (media[card_idx], "100mbps_hd") == 0
 				   || strcmp (media[card_idx], "3") == 0) {
 				np->speed = 100;
-				np->full_duplex = 0;
+				np->mii_if.full_duplex = 0;
 			} else if (strcmp (media[card_idx], "10mbps_fd") == 0 ||
 				   strcmp (media[card_idx], "2") == 0) {
 				np->speed = 10;
-				np->full_duplex = 1;
+				np->mii_if.full_duplex = 1;
 			} else if (strcmp (media[card_idx], "10mbps_hd") == 0 ||
 				   strcmp (media[card_idx], "1") == 0) {
 				np->speed = 10;
-				np->full_duplex = 0;
+				np->mii_if.full_duplex = 0;
 			} else {
 				np->an_enable = 1;
 			}
 		}
-		if (tx_coalesce < 1) 
+		if (tx_coalesce < 1)
 			tx_coalesce = 1;
 		else if (tx_coalesce > TX_QUEUE_LEN - 1)
 			tx_coalesce = TX_QUEUE_LEN - 1;
@@ -631,7 +643,7 @@
 		/* Default 100Mbps Full */
 		if (np->an_enable) {
 			np->speed = 100;
-			np->full_duplex = 1;
+			np->mii_if.full_duplex = 1;
 			np->an_enable = 0;
 		}
 	}
@@ -643,19 +655,19 @@
 	if (!np->an_enable) {
 		mii_ctl = 0;
 		mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
-		mii_ctl |= (np->full_duplex) ? BMCR_FULLDPLX : 0;
+		mii_ctl |= (np->mii_if.full_duplex) ? BMCR_FULLDPLX : 0;
 		mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl);
 		printk (KERN_INFO "Override speed=%d, %s duplex\n",
-			np->speed, np->full_duplex ? "Full" : "Half");
+			np->speed, np->mii_if.full_duplex ? "Full" : "Half");
 
 	}
 
 	/* Perhaps move the reset here? */
 	/* Reset the chip to erase previous misconfiguration. */
-	if (debug > 1)
+	if (netif_msg_hw(np))
 		printk("ASIC Control is %x.\n", readl(ioaddr + ASICCtrl));
 	writew(0x007f, ioaddr + ASICCtrl + 2);
-	if (debug > 1)
+	if (netif_msg_hw(np))
 		printk("ASIC Control is now %x.\n", readl(ioaddr + ASICCtrl));
 
 	card_idx++;
@@ -677,7 +689,7 @@
 	return -ENODEV;
 }
 
-\f
+
 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
 static int __devinit eeprom_read(long ioaddr, int location)
 {
@@ -793,7 +805,7 @@
 	if (i)
 		return i;
 
-	if (debug > 1)
+	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
 			   dev->name, dev->irq);
 
@@ -823,7 +835,7 @@
 
 	writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
 
-	if (debug > 2)
+	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
 			   "MAC Control %x, %4.4x %4.4x.\n",
 			   dev->name, readl(ioaddr + RxStatus), readb(ioaddr + TxStatus),
@@ -836,7 +848,7 @@
 	np->timer.data = (unsigned long)dev;
 	np->timer.function = &netdev_timer;				/* timer handler */
 	add_timer(&np->timer);
-	
+
 	/* Enable interrupts by setting the interrupt mask. */
 	writew(DEFAULT_INTR, ioaddr + IntrEnable);
 
@@ -848,21 +860,22 @@
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 	int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA);
-	int negotiated = mii_lpa & np->advertising;
+	int negotiated = mii_lpa & np->mii_if.advertising;
 	int duplex;
-	
+
 	/* Force media */
 	if (!np->an_enable || mii_lpa == 0xffff) {
-		if (np->full_duplex)
+		if (np->mii_if.full_duplex)
 			writew (readw (ioaddr + MACCtrl0) | EnbFullDuplex,
 				ioaddr + MACCtrl0);
 		return;
 	}
+
 	/* Autonegotiation */
 	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
-	if (np->full_duplex != duplex) {
-		np->full_duplex = duplex;
-		if (debug)
+	if (np->mii_if.full_duplex != duplex) {
+		np->mii_if.full_duplex = duplex;
+		if (netif_msg_link(np))
 			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d "
 				   "negotiated capability %4.4x.\n", dev->name,
 				   duplex ? "full" : "half", np->phys[0], negotiated);
@@ -877,7 +890,7 @@
 	long ioaddr = dev->base_addr;
 	int next_tick = 10*HZ;
 
-	if (debug > 3) {
+	if (netif_msg_timer(np)) {
 		printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, "
 			   "Tx %x Rx %x.\n",
 			   dev->name, readw(ioaddr + IntrEnable),
@@ -941,7 +954,7 @@
 
 	/* Initialize all Rx descriptors. */
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		np->rx_ring[i].next_desc = cpu_to_le32(np->rx_ring_dma + 
+		np->rx_ring[i].next_desc = cpu_to_le32(np->rx_ring_dma +
 			((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring));
 		np->rx_ring[i].status = 0;
 		np->rx_ring[i].frag[0].length = 0;
@@ -957,7 +970,7 @@
 		skb->dev = dev;		/* Mark as being used by this device. */
 		skb_reserve(skb, 2);	/* 16 byte align the IP header. */
 		np->rx_ring[i].frag[0].addr = cpu_to_le32(
-			pci_map_single(np->pci_dev, skb->tail, np->rx_buf_sz, 
+			pci_map_single(np->pci_dev, skb->tail, np->rx_buf_sz,
 				PCI_DMA_FROMDEVICE));
 		np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag);
 	}
@@ -988,9 +1001,9 @@
 	txdesc->next_desc = 0;
 	/* Note: disable the interrupt generation here before releasing. */
 	if (entry % tx_coalesce == 0) {
-		txdesc->status = cpu_to_le32 ((entry << 2) | 
+		txdesc->status = cpu_to_le32 ((entry << 2) |
 				 DescIntrOnTx | DisableAlign);
-	
+
 	} else {
 		txdesc->status = cpu_to_le32 ((entry << 2) | DisableAlign);
 	}
@@ -1005,7 +1018,7 @@
 	np->cur_tx++;
 
 	/* On some architectures: explicitly flush cache lines here. */
-	if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 
+	if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1
 			&& !netif_queue_stopped(dev)) {
 		/* do nothing */
 	} else {
@@ -1018,7 +1031,7 @@
 
 	dev->trans_start = jiffies;
 
-	if (debug > 4) {
+	if (netif_msg_tx_queued(np)) {
 		printk (KERN_DEBUG
 			"%s: Transmit frame #%d queued in slot %d.\n",
 			dev->name, np->cur_tx, entry);
@@ -1034,7 +1047,7 @@
 	long ioaddr = dev->base_addr;
 	int i;
 	int frame_id;
-	
+
 	frame_id = readb(ioaddr + TxFrameId);
 	writew (TxReset | DMAReset | FIFOReset | NetworkReset,
 			ioaddr + ASICCtrl + 2);
@@ -1050,8 +1063,8 @@
 			break;
 		skb = np->tx_skbuff[entry];
 		/* Free the original skb. */
-		pci_unmap_single(np->pci_dev, 
-			np->tx_ring[entry].frag[0].addr, 
+		pci_unmap_single(np->pci_dev,
+			np->tx_ring[entry].frag[0].addr,
 			skb->len, PCI_DMA_TODEVICE);
 		if (irq)
 			dev_kfree_skb_irq (np->tx_skbuff[entry]);
@@ -1081,7 +1094,7 @@
 		int intr_status = readw(ioaddr + IntrStatus);
 		writew(intr_status, ioaddr + IntrStatus);
 
-		if (debug > 4)
+		if (netif_msg_intr(np))
 			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
 				   dev->name, intr_status);
 
@@ -1089,7 +1102,7 @@
 			break;
 
 		if (intr_status & (IntrRxDMADone)) {
-			writew(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone), 
+			writew(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone),
 					ioaddr + IntrEnable);
 			if (np->budget < 0)
 				np->budget = RX_BUDGET;
@@ -1100,7 +1113,7 @@
 			int boguscnt = 32;
 			int tx_status = readw (ioaddr + TxStatus);
 			while (tx_status & 0x80) {
-				if (debug > 4)
+				if (netif_msg_tx_done(np))
 					printk
 					    ("%s: Transmit status is %2.2x.\n",
 					     dev->name, tx_status);
@@ -1138,14 +1151,14 @@
 				break;
 			skb = np->tx_skbuff[entry];
 			/* Free the original skb. */
-			pci_unmap_single(np->pci_dev, 
-				np->tx_ring[entry].frag[0].addr, 
+			pci_unmap_single(np->pci_dev,
+				np->tx_ring[entry].frag[0].addr,
 				skb->len, PCI_DMA_TODEVICE);
 			dev_kfree_skb_irq (np->tx_skbuff[entry]);
 			np->tx_skbuff[entry] = 0;
 		}
 		spin_unlock(&np->lock);
-		if (netif_queue_stopped(dev) && 
+		if (netif_queue_stopped(dev) &&
 			np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
 			/* The ring is no longer full, clear tbusy. */
 			netif_wake_queue (dev);
@@ -1156,14 +1169,14 @@
 			netdev_error(dev, intr_status);
 		if (--boguscnt < 0) {
 			get_stats(dev);
-			if (debug > 1) 
+			if (netif_msg_hw(np))
 				printk(KERN_WARNING "%s: Too much work at interrupt, "
 				   "status=0x%4.4x / 0x%4.4x.\n",
 				   dev->name, intr_status, readw(ioaddr + IntrClear));
 			break;
 		}
 	} while (1);
-	if (debug > 3)
+	if (netif_msg_intr(np))
 		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
 			   dev->name, readw(ioaddr + IntrStatus));
 	if (np->cur_tx - np->dirty_tx > 0 && tx_coalesce > 1)
@@ -1192,15 +1205,15 @@
 		if (!(desc->status & DescOwn))
 			break;
 		pkt_len = frame_status & 0x1fff;	/* Chip omits the CRC. */
-		if (debug > 4)
+		if (netif_msg_rx_status(np))
 			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n",
 				   frame_status);
 		pci_dma_sync_single(np->pci_dev, desc->frag[0].addr,
 			np->rx_buf_sz, PCI_DMA_FROMDEVICE);
-		
+
 		if (frame_status & 0x001f4000) {
 			/* There was a error. */
-			if (debug > 2)
+			if (netif_msg_rx_err(np))
 				printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
 					   frame_status);
 			np->stats.rx_errors++;
@@ -1216,7 +1229,7 @@
 		} else {
 			struct sk_buff *skb;
 #ifndef final_version
-			if (debug > 4)
+			if (netif_msg_rx_status(np))
 				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
 					   ", bogus_cnt %d.\n",
 					   pkt_len, boguscnt);
@@ -1230,9 +1243,9 @@
 				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
 			} else {
-				pci_unmap_single(np->pci_dev, 
+				pci_unmap_single(np->pci_dev,
 					desc->frag[0].addr,
-					np->rx_buf_sz, 
+					np->rx_buf_sz,
 					PCI_DMA_FROMDEVICE);
 				skb_put(skb = np->rx_skbuff[entry], pkt_len);
 				np->rx_skbuff[entry] = NULL;
@@ -1251,7 +1264,7 @@
 	writew(DEFAULT_INTR, ioaddr + IntrEnable);
 	return;
 
-not_done:	
+not_done:
 	np->cur_rx = entry;
 	refill_rx (dev);
 	if (!received)
@@ -1282,7 +1295,7 @@
 			skb->dev = dev;		/* Mark as being used by this device. */
 			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 			np->rx_ring[entry].frag[0].addr = cpu_to_le32(
-				pci_map_single(np->pci_dev, skb->tail, 
+				pci_map_single(np->pci_dev, skb->tail,
 					np->rx_buf_sz, PCI_DMA_FROMDEVICE));
 		}
 		/* Perhaps we need not reset this field. */
@@ -1299,7 +1312,7 @@
 	struct netdev_private *np = dev->priv;
 	u16 mii_ctl, mii_advertise, mii_lpa;
 	int speed;
-	
+
 	if (intr_status & LinkChange) {
 		if (np->an_enable) {
 			mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
@@ -1417,12 +1430,12 @@
 {
 	struct netdev_private *np = dev->priv;
 	u32 ethcmd;
-	long ioaddr = dev->base_addr;
-	
+
 	if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
 		return -EFAULT;
 
         switch (ethcmd) {
+		/* get constant driver settings/info */
         	case ETHTOOL_GDRVINFO: {
 			struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
 			strcpy(info.driver, DRV_NAME);
@@ -1433,116 +1446,60 @@
 				return -EFAULT;
 			return 0;
 		}
-		case ETHTOOL_GSET: {
-			struct ethtool_cmd cmd = { ETHTOOL_GSET };
-			if (readl (ioaddr + ASICCtrl) & 0x80) {
-				/* fiber device */
-				cmd.supported = SUPPORTED_Autoneg | 
-							SUPPORTED_FIBRE;
-				cmd.advertising= ADVERTISED_Autoneg |
-							ADVERTISED_FIBRE;
-				cmd.port = PORT_FIBRE;
-				cmd.transceiver = XCVR_INTERNAL;	
-			} else {
-				/* copper device */
-				cmd.supported = SUPPORTED_10baseT_Half | 
-					SUPPORTED_10baseT_Full |
-				       	SUPPORTED_100baseT_Half	|
-				       	SUPPORTED_100baseT_Full | 
-					SUPPORTED_Autoneg | 
-					SUPPORTED_MII;
-				cmd.advertising = ADVERTISED_10baseT_Half |
-						ADVERTISED_10baseT_Full |
-				       		ADVERTISED_100baseT_Half |
-						ADVERTISED_100baseT_Full | 
-						ADVERTISED_Autoneg |
-				       		ADVERTISED_MII;
-				cmd.port = PORT_MII;
-				cmd.transceiver = XCVR_INTERNAL;
-			}
-			if (readb(ioaddr + MIICtrl) & 0x80) {
-				cmd.speed = np->speed;
-				cmd.duplex = np->full_duplex ? 
-						    DUPLEX_FULL : DUPLEX_HALF;
-			} else {
-				cmd.speed = -1;
-				cmd.duplex = -1;
-			}
-			if ( np->an_enable)
-				cmd.autoneg = AUTONEG_ENABLE;
-			else
-				cmd.autoneg = AUTONEG_DISABLE;
-			
-			cmd.phy_address = np->phys[0];
 
-			if (copy_to_user(useraddr, &cmd,
-					sizeof(cmd)))
+		/* get media settings */
+		case ETHTOOL_GSET: {
+			struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+			spin_lock_irq(&np->lock);
+			mii_ethtool_gset(&np->mii_if, &ecmd);
+			spin_unlock_irq(&np->lock);
+			if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
 				return -EFAULT;
-			return 0;				   
+			return 0;
 		}
+		/* set media settings */
 		case ETHTOOL_SSET: {
-			struct ethtool_cmd cmd;
-			if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+			int r;
+			struct ethtool_cmd ecmd;
+			if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
 				return -EFAULT;
-			netif_carrier_off(dev);
-			if (cmd.autoneg == AUTONEG_ENABLE) {
-				if (np->an_enable)
-					return 0;
-				else {
-					np->an_enable = 1;
-					/* Reset PHY */
-					mdio_write (dev, np->phys[0], MII_BMCR,
-						       		BMCR_RESET);
-					mdelay (300);
-					/* Start auto negotiation */
-					mdio_write (dev, np->phys[0], MII_BMCR,
-					      	BMCR_ANENABLE|BMCR_ANRESTART);
-					return 0;	
-				}	
-			} else {
-				/* Reset PHY */
-				mdio_write (dev, np->phys[0], MII_BMCR, 
-								BMCR_RESET);
-				mdelay (300);
-				np->an_enable = 0;
-				switch(cmd.speed + cmd.duplex){
-				
-					case SPEED_10 + DUPLEX_HALF:
-						np->speed = 10;
-						np->full_duplex = 0;
-						break;
-					case SPEED_10 + DUPLEX_FULL:
-						np->speed = 10;
-						np->full_duplex = 1;
-						break;
-					case SPEED_100 + DUPLEX_HALF:
-						np->speed = 100;
-						np->full_duplex = 0;
-						break;
-					case SPEED_100 + DUPLEX_FULL:
-						np->speed = 100;
-						np->full_duplex = 1;
-						break;
-				
-				default:
-					return -EINVAL;	
-				}
-		mdio_write (dev, np->phys[0], MII_BMCR,
-				((np->speed == 100) ? BMCR_SPEED100 : 0) | 
-				((np->full_duplex) ? BMCR_FULLDPLX : 0) );
+			spin_lock_irq(&np->lock);
+			r = mii_ethtool_sset(&np->mii_if, &ecmd);
+			spin_unlock_irq(&np->lock);
+			return r;
+		}
+
+		/* restart autonegotiation */
+		case ETHTOOL_NWAY_RST: {
+			return mii_nway_restart(&np->mii_if);
+		}
+
+		/* get link status */
+		case ETHTOOL_GLINK: {
+			struct ethtool_value edata = {ETHTOOL_GLINK};
+			edata.data = mii_link_ok(&np->mii_if);
+			if (copy_to_user(useraddr, &edata, sizeof(edata)))
+				return -EFAULT;
+			return 0;
+		}
 
-			}
-		return 0;		   
+		/* get message-level */
+		case ETHTOOL_GMSGLVL: {
+			struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+			edata.data = np->msg_enable;
+			if (copy_to_user(useraddr, &edata, sizeof(edata)))
+				return -EFAULT;
+			return 0;
 		}
-#ifdef	ETHTOOL_GLINK
-		case ETHTOOL_GLINK:{
-		struct ethtool_value link = { ETHTOOL_GLINK };
-		link.data = readb(ioaddr + MIICtrl) & 0x80;
-		if (copy_to_user(useraddr, &link, sizeof(link)))
-			return -EFAULT;
-		return 0;
-		}			   
-#endif
+		/* set message-level */
+		case ETHTOOL_SMSGLVL: {
+			struct ethtool_value edata;
+			if (copy_from_user(&edata, useraddr, sizeof(edata)))
+				return -EFAULT;
+			np->msg_enable = edata.data;
+			return 0;
+		}
+
 		default:
 		return -EOPNOTSUPP;
 
@@ -1583,7 +1540,7 @@
 
 	netif_stop_queue(dev);
 
-	if (debug > 1) {
+	if (netif_msg_ifdown(np)) {
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x "
 			   "Rx %4.4x Int %2.2x.\n",
 			   dev->name, readb(ioaddr + TxStatus),
@@ -1599,7 +1556,7 @@
 	writew(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1);
 
 #ifdef __i386__
-	if (debug > 2) {
+	if (netif_msg_hw(np)) {
 		printk("\n"KERN_DEBUG"  Tx ring at %8.8x:\n",
 			   (int)(np->tx_ring_dma));
 		for (i = 0; i < TX_RING_SIZE; i++)
@@ -1626,8 +1583,8 @@
 		np->rx_ring[i].frag[0].addr = 0xBADF00D0; /* An invalid address. */
 		skb = np->rx_skbuff[i];
 		if (skb) {
-			pci_unmap_single(np->pci_dev, 
-				np->rx_ring[i].frag[0].addr, np->rx_buf_sz, 
+			pci_unmap_single(np->pci_dev,
+				np->rx_ring[i].frag[0].addr, np->rx_buf_sz,
 				PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(skb);
 			np->rx_skbuff[i] = 0;
@@ -1636,7 +1593,7 @@
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		skb = np->tx_skbuff[i];
 		if (skb) {
-			pci_unmap_single(np->pci_dev, 
+			pci_unmap_single(np->pci_dev,
 				np->tx_ring[i].frag[0].addr, skb->len,
 				PCI_DMA_TODEVICE);
 			dev_kfree_skb(skb);
@@ -1650,15 +1607,15 @@
 static void __devexit sundance_remove1 (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
-	
+
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	if (dev) {
 		struct netdev_private *np = dev->priv;
 
 		unregister_netdev(dev);
-        	pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring, 
+        	pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring,
 			np->rx_ring_dma);
-	        pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring, 
+	        pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring,
 			np->tx_ring_dma);
 		pci_release_regions(pdev);
 #ifndef USE_IO_OPS

^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #4a
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
@ 2002-09-19 20:51             ` Jason Lunz
  2002-09-19 21:09               ` Jeff Garzik
  2002-09-19 20:52             ` PATCH: sundance #4b Jason Lunz
                               ` (2 subsequent siblings)
  3 siblings, 1 reply; 18+ messages in thread
From: Jason Lunz @ 2002-09-19 20:51 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

[-- Attachment #1: Type: text/plain, Size: 132 bytes --]


If you're going to bail when reading the ASIC fails, you need to
unregister the dev before you return or Bad Things happen.

Jason

[-- Attachment #2: sundance-4a --]
[-- Type: text/plain, Size: 326 bytes --]

--- sundance-garzik.c	Thu Sep 19 16:45:57 2002
+++ sundance-unreg.c	Thu Sep 19 16:48:22 2002
@@ -599,6 +599,7 @@
 		if (phy_idx == 0) {
 			printk(KERN_INFO "%s: No MII transceiver found, aborting.  ASIC status %x\n",
 				   dev->name, readl(ioaddr + ASICCtrl));
+			unregister_netdev(dev);
 			goto err_out_unmap_rx;
 		}
 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #4b
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
  2002-09-19 20:51             ` PATCH: sundance #4a Jason Lunz
@ 2002-09-19 20:52             ` Jason Lunz
  2002-09-19 21:14               ` Jeff Garzik
  2002-09-19 21:03             ` PATCH: sundance #5 (variable per-interface MTU support) Jason Lunz
  2002-09-19 21:35             ` PATCH: [my] sundance #5 Jeff Garzik
  3 siblings, 1 reply; 18+ messages in thread
From: Jason Lunz @ 2002-09-19 20:52 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

[-- Attachment #1: Type: text/plain, Size: 145 bytes --]


The aforementioned failure happens unless USE_IO_OPS is defined. It
should be the default, as without it the driver doesn't work at all.

Jason

[-- Attachment #2: sundance-4b --]
[-- Type: text/plain, Size: 326 bytes --]

--- sundance-unreg.c	Thu Sep 19 16:48:22 2002
+++ sundance-ioops.c	Thu Sep 19 16:48:24 2002
@@ -247,6 +247,10 @@
 
 */
 
+/* Work-around for Kendin chip bugs. */
+#ifndef USE_MEM_OPS
+#define USE_IO_OPS 1
+#endif
 
 static struct pci_device_id sundance_pci_tbl[] __devinitdata = {
 	{0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},

^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #5 (variable per-interface MTU support)
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
  2002-09-19 20:51             ` PATCH: sundance #4a Jason Lunz
  2002-09-19 20:52             ` PATCH: sundance #4b Jason Lunz
@ 2002-09-19 21:03             ` Jason Lunz
  2002-09-19 21:19               ` Jeff Garzik
  2002-09-19 22:28               ` Donald Becker
  2002-09-19 21:35             ` PATCH: [my] sundance #5 Jeff Garzik
  3 siblings, 2 replies; 18+ messages in thread
From: Jason Lunz @ 2002-09-19 21:03 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

[-- Attachment #1: Type: text/plain, Size: 78 bytes --]


This is a straightforward merge of variable mtu from donald's driver.

Jason

[-- Attachment #2: sundance-5 --]
[-- Type: text/plain, Size: 2987 bytes --]

--- sundance-ioops.c	Thu Sep 19 16:58:44 2002
+++ sundance-mtu.c	Thu Sep 19 17:00:30 2002
@@ -65,7 +65,6 @@
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 0;
-static int mtu;
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    Typical is a 64 element hash table based on the Ethernet CRC.  */
 static int multicast_filter_limit = 32;
@@ -162,13 +161,11 @@
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(mtu, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
 MODULE_PARM(flowctrl, "i");
 MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
-MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
@@ -333,7 +330,7 @@
 	MACCtrl0 = 0x50,
 	MACCtrl1 = 0x52,
 	StationAddr = 0x54,
-	MaxTxSize = 0x5A,
+	MaxFrameSize = 0x5A,
 	RxMode = 0x5c,
 	MIICtrl = 0x5e,
 	MulticastFilter0 = 0x60,
@@ -461,6 +458,7 @@
 			IntrDrvRqst | IntrTxDone | StatsMax | \
 			LinkChange)
 
+static int  change_mtu(struct net_device *dev, int new_mtu);
 static int  eeprom_read(long ioaddr, int location);
 static int  mdio_read(struct net_device *dev, int phy_id, int location);
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
@@ -567,11 +565,9 @@
 	dev->do_ioctl = &netdev_ioctl;
 	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->change_mtu = &change_mtu;
 	pci_set_drvdata(pdev, dev);
 
-	if (mtu)
-		dev->mtu = mtu;
-
 	i = register_netdev(dev);
 	if (i)
 		goto err_out_unmap_rx;
@@ -694,6 +690,15 @@
 	return -ENODEV;
 }
 
+static int change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > 8191)) /* Set by RxDMAFrameLen */
+		return -EINVAL;
+	if (netif_running(dev))
+		return -EBUSY;
+	dev->mtu = new_mtu;
+	return 0;
+}
 
 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
 static int __devinit eeprom_read(long ioaddr, int location)
@@ -823,6 +828,10 @@
 		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
 	/* Initialize other registers. */
+	writew(dev->mtu + 14, ioaddr + MaxFrameSize);
+	if (dev->mtu > 2047)
+		writel(readl(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl);
+
 	/* Configure the PCI bus bursts and FIFO thresholds. */
 
 	if (dev->if_port == 0)
@@ -955,7 +964,7 @@
 	np->cur_rx = np->cur_tx = 0;
 	np->dirty_rx = np->dirty_tx = 0;
 
-	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 36);
 
 	/* Initialize all Rx descriptors. */
 	for (i = 0; i < RX_RING_SIZE; i++) {

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #4a
  2002-09-19 20:51             ` PATCH: sundance #4a Jason Lunz
@ 2002-09-19 21:09               ` Jeff Garzik
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 21:09 UTC (permalink / raw)
  To: Jason Lunz
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

applied by hand -- created goto label err_out_unregister instead



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #4b
  2002-09-19 20:52             ` PATCH: sundance #4b Jason Lunz
@ 2002-09-19 21:14               ` Jeff Garzik
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 21:14 UTC (permalink / raw)
  To: Jason Lunz
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

applied, then changed USE_MEM_OPS to a CONFIG_xxx option



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #5 (variable per-interface MTU support)
  2002-09-19 21:03             ` PATCH: sundance #5 (variable per-interface MTU support) Jason Lunz
@ 2002-09-19 21:19               ` Jeff Garzik
  2002-09-19 22:28               ` Donald Becker
  1 sibling, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 21:19 UTC (permalink / raw)
  To: Jason Lunz
  Cc: netdev, Linux Kernel Mailing List, Donald Becker, Richard Gooch,
	Patrick R. McManus, edward_peng

applied



^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: [my] sundance #5
  2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
                               ` (2 preceding siblings ...)
  2002-09-19 21:03             ` PATCH: sundance #5 (variable per-interface MTU support) Jason Lunz
@ 2002-09-19 21:35             ` Jeff Garzik
  2002-09-20  0:18               ` PATCH: sundance #6 Jeff Garzik
  3 siblings, 1 reply; 18+ messages in thread
From: Jeff Garzik @ 2002-09-19 21:35 UTC (permalink / raw)
  To: netdev, Linux Kernel Mailing List
  Cc: Donald Becker, Jason Lunz, Richard Gooch, Patrick R. McManus,
	edward_peng

[-- Attachment #1: Type: text/plain, Size: 137 bytes --]

next sundance patch in the series, updated with Jason's patches

Note, with the patching of Configure.help this is a 2.4-specific patch.

[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 6682 bytes --]

diff -Nru a/Documentation/Configure.help b/Documentation/Configure.help
--- a/Documentation/Configure.help	Thu Sep 19 17:32:40 2002
+++ b/Documentation/Configure.help	Thu Sep 19 17:32:41 2002
@@ -11799,6 +11799,14 @@
   say M here and read <file:Documentation/modules.txt>.  The module
   will be called sundance.o.
 
+Sundance Alta memory-mapped I/O support
+CONFIG_SUNDANCE_MMIO
+  Enable memory-mapped I/O for interaction with Sundance NIC registers.
+  Do NOT enable this by default, PIO (enabled when MMIO is disabled)
+  is known to solve bugs on certain chips.
+
+  If unsure, say N.
+
 Sun3/Sun3x on-board LANCE support
 CONFIG_SUN3LANCE
   Most Sun3 and Sun3x motherboards (including the 3/50, 3/60 and 3/80)
diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in
--- a/drivers/net/Config.in	Thu Sep 19 17:32:40 2002
+++ b/drivers/net/Config.in	Thu Sep 19 17:32:40 2002
@@ -192,6 +192,7 @@
       dep_tristate '    SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 $CONFIG_PCI
       dep_tristate '    SMC EtherPower II' CONFIG_EPIC100 $CONFIG_PCI
       dep_tristate '    Sundance Alta support' CONFIG_SUNDANCE $CONFIG_PCI
+      dep_mbool '      Use MMIO instead of PIO' CONFIG_SUNDANCE_MMIO $CONFIG_SUNDANCE
       if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then
          tristate '    TI ThunderLAN support' CONFIG_TLAN
       fi
diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 17:32:40 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 17:32:40 2002
@@ -33,10 +33,11 @@
 	- Tx timeout recovery
 	- More support for ethtool.
 
-	Version LK1.04a (jgarzik):
+	Version LK1.04a:
 	- Remove unused/constant members from struct pci_id_info
 	(which then allows removal of 'drv_flags' from private struct)
-	- If no phy is found, fail to load that board
+	(jgarzik)
+	- If no phy is found, fail to load that board (jgarzik)
 	- Always start phy id scan at id 1 to avoid problems (Donald Becker)
 	- Autodetect where mii_preable_required is needed,
 	default to not needed.  (Donald Becker)
@@ -45,18 +46,25 @@
 	- Remove mii_preamble_required module parameter (Donald Becker)
 	- Add per-interface mii_preamble_required (setting is autodetected)
 	  (Donald Becker)
-	- Remove unnecessary cast from void pointer
-	- Re-align comments in private struct
+	- Remove unnecessary cast from void pointer (jgarzik)
+	- Re-align comments in private struct (jgarzik)
 
-	Version LK1.04c:
+	Version LK1.04c (jgarzik):
 	- Support bitmapped message levels (NETIF_MSG_xxx), and the
 	  two ethtool ioctls that get/set them
 	- Don't hand-code MII ethtool support, use standard API/lib
 
+	Version LK1.04d:
+	- Merge from Donald Becker's sundance.c: (Jason Lunz)
+		* proper support for variably-sized MTUs
+		* default to PIO, to fix chip bugs
+	- Add missing unregister_netdev (Jason Lunz)
+	- Add CONFIG_SUNDANCE_MMIO config option (jgarzik)
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01+LK1.04c"
+#define DRV_VERSION	"1.01+LK1.04d"
 #define DRV_RELDATE	"19-Sep-2002"
 
 
@@ -65,7 +73,6 @@
 static int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 0;
-static int mtu;
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    Typical is a 64 element hash table based on the Ethernet CRC.  */
 static int multicast_filter_limit = 32;
@@ -162,13 +169,11 @@
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(mtu, "i");
 MODULE_PARM(debug, "i");
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "s");
 MODULE_PARM(flowctrl, "i");
 MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
-MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]");
@@ -247,6 +252,10 @@
 
 */
 
+/* Work-around for Kendin chip bugs. */
+#ifndef CONFIG_SUNDANCE_MMIO
+#define USE_IO_OPS 1
+#endif
 
 static struct pci_device_id sundance_pci_tbl[] __devinitdata = {
 	{0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},
@@ -329,7 +338,7 @@
 	MACCtrl0 = 0x50,
 	MACCtrl1 = 0x52,
 	StationAddr = 0x54,
-	MaxTxSize = 0x5A,
+	MaxFrameSize = 0x5A,
 	RxMode = 0x5c,
 	MIICtrl = 0x5e,
 	MulticastFilter0 = 0x60,
@@ -457,6 +466,7 @@
 			IntrDrvRqst | IntrTxDone | StatsMax | \
 			LinkChange)
 
+static int  change_mtu(struct net_device *dev, int new_mtu);
 static int  eeprom_read(long ioaddr, int location);
 static int  mdio_read(struct net_device *dev, int phy_id, int location);
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
@@ -563,11 +573,9 @@
 	dev->do_ioctl = &netdev_ioctl;
 	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->change_mtu = &change_mtu;
 	pci_set_drvdata(pdev, dev);
 
-	if (mtu)
-		dev->mtu = mtu;
-
 	i = register_netdev(dev);
 	if (i)
 		goto err_out_unmap_rx;
@@ -599,7 +607,7 @@
 		if (phy_idx == 0) {
 			printk(KERN_INFO "%s: No MII transceiver found, aborting.  ASIC status %x\n",
 				   dev->name, readl(ioaddr + ASICCtrl));
-			goto err_out_unmap_rx;
+			goto err_out_unregister;
 		}
 
 		np->mii_if.phy_id = np->phys[0];
@@ -673,6 +681,8 @@
 	card_idx++;
 	return 0;
 
+err_out_unregister:
+	unregister_netdev(dev);
 err_out_unmap_rx:
         pci_free_consistent(pdev, RX_TOTAL_SIZE, np->rx_ring, np->rx_ring_dma);
 err_out_unmap_tx:
@@ -689,6 +699,15 @@
 	return -ENODEV;
 }
 
+static int change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > 8191)) /* Set by RxDMAFrameLen */
+		return -EINVAL;
+	if (netif_running(dev))
+		return -EBUSY;
+	dev->mtu = new_mtu;
+	return 0;
+}
 
 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */
 static int __devinit eeprom_read(long ioaddr, int location)
@@ -818,6 +837,10 @@
 		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
 	/* Initialize other registers. */
+	writew(dev->mtu + 14, ioaddr + MaxFrameSize);
+	if (dev->mtu > 2047)
+		writel(readl(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl);
+
 	/* Configure the PCI bus bursts and FIFO thresholds. */
 
 	if (dev->if_port == 0)
@@ -950,7 +973,7 @@
 	np->cur_rx = np->cur_tx = 0;
 	np->dirty_rx = np->dirty_tx = 0;
 
-	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 36);
 
 	/* Initialize all Rx descriptors. */
 	for (i = 0; i < RX_RING_SIZE; i++) {

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: PATCH: sundance #5 (variable per-interface MTU support)
  2002-09-19 21:03             ` PATCH: sundance #5 (variable per-interface MTU support) Jason Lunz
  2002-09-19 21:19               ` Jeff Garzik
@ 2002-09-19 22:28               ` Donald Becker
  1 sibling, 0 replies; 18+ messages in thread
From: Donald Becker @ 2002-09-19 22:28 UTC (permalink / raw)
  To: Jason Lunz
  Cc: Jeff Garzik, netdev, Linux Kernel Mailing List, Richard Gooch,
	Patrick R. McManus, edward_peng

On Thu, 19 Sep 2002, Jason Lunz wrote:

> This is a straightforward merge of variable mtu from donald's driver.

-	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 36);

Errrmm, not quite right.

Try
	np->rx_buf_sz = (dev->mtu <= 1520 ? PKT_BUF_SZ : dev->mtu + 16);

The idea is that all ethernet-like drivers always allocate skbuffs of
the same size, PKT_BUF_SZ (1536=3*512), unless a jumbo MTU forces a
larger size. 

Specificially, using VLANs (+4 bytes on the frame size) on some interfaces
should not result in a mix of allocation sizes.  Most VLAN-like
encapsulation should add fewer than (1536-1518 = 18) 18 extra bytes.

BTW, always leave a few extra bytes at the end of the data buffer.
You never know when some chip might decide to dribble an extra word or
two, or include the CRC because someone frobbed the driver.

-- 
Donald Becker				becker@scyld.com
Scyld Computing Corporation		http://www.scyld.com
410 Severn Ave. Suite 210		Second Generation Beowulf Clusters
Annapolis MD 21403			410-990-9993


^ permalink raw reply	[flat|nested] 18+ messages in thread

* PATCH: sundance #6
  2002-09-19 21:35             ` PATCH: [my] sundance #5 Jeff Garzik
@ 2002-09-20  0:18               ` Jeff Garzik
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Garzik @ 2002-09-20  0:18 UTC (permalink / raw)
  To: netdev, Linux Kernel Mailing List
  Cc: Donald Becker, Jason Lunz, Richard Gooch, Patrick R. McManus,
	edward_peng

[-- Attachment #1: Type: text/plain, Size: 59 bytes --]

The obvious sundance update from the earlier discussion...

[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 685 bytes --]

diff -Nru a/drivers/net/sundance.c b/drivers/net/sundance.c
--- a/drivers/net/sundance.c	Thu Sep 19 20:16:23 2002
+++ b/drivers/net/sundance.c	Thu Sep 19 20:16:23 2002
@@ -60,6 +60,7 @@
 		* default to PIO, to fix chip bugs
 	- Add missing unregister_netdev (Jason Lunz)
 	- Add CONFIG_SUNDANCE_MMIO config option (jgarzik)
+	- Better rx buf size calculation (Donald Becker)
 
 */
 
@@ -973,7 +974,7 @@
 	np->cur_rx = np->cur_tx = 0;
 	np->dirty_rx = np->dirty_tx = 0;
 
-	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 36);
+	np->rx_buf_sz = (dev->mtu <= 1520 ? PKT_BUF_SZ : dev->mtu + 16);
 
 	/* Initialize all Rx descriptors. */
 	for (i = 0; i < RX_RING_SIZE; i++) {

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2002-09-20  0:13 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20020828185612.GA14342@reflexsecurity.com>
     [not found] ` <20020828231333.GA15183@reflexsecurity.com>
     [not found]   ` <200209190353.g8J3r5q28456@vindaloo.ras.ucalgary.ca>
     [not found]     ` <20020919041403.GA10527@orr.falooley.org>
2002-09-19  4:25       ` [PATCH] 2.4.20-pre sundance.c update Jeff Garzik
2002-09-19  4:56         ` Jason Lunz
2002-09-19  5:11           ` Jeff Garzik
2002-09-19 13:23         ` Donald Becker
2002-09-19 17:12           ` PATCH: sundance #2 Jeff Garzik
2002-09-19 17:29             ` Donald Becker
2002-09-19 18:13               ` Jeff Garzik
2002-09-19 18:18               ` PATCH: sundance #3 Jeff Garzik
2002-09-19 19:30           ` PATCH: sundance #4 Jeff Garzik
2002-09-19 20:51             ` PATCH: sundance #4a Jason Lunz
2002-09-19 21:09               ` Jeff Garzik
2002-09-19 20:52             ` PATCH: sundance #4b Jason Lunz
2002-09-19 21:14               ` Jeff Garzik
2002-09-19 21:03             ` PATCH: sundance #5 (variable per-interface MTU support) Jason Lunz
2002-09-19 21:19               ` Jeff Garzik
2002-09-19 22:28               ` Donald Becker
2002-09-19 21:35             ` PATCH: [my] sundance #5 Jeff Garzik
2002-09-20  0:18               ` PATCH: sundance #6 Jeff Garzik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).