All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jason Lunz <lunz@gtf.org>
To: netdev@oss.sgi.com
Cc: becker@scyld.com, jgarzik@mandrakesoft.com,
	"Patrick R. McManus" <mcmanus@ducksong.com>
Subject: Re: [PATCH] 2.4.20-pre sundance.c cleanups
Date: Wed, 28 Aug 2002 19:13:33 -0400	[thread overview]
Message-ID: <20020828231333.GA15183@reflexsecurity.com> (raw)
In-Reply-To: <20020828185612.GA14342@reflexsecurity.com>



I forgot to mention that the last patch, like every other sundance
driver I've ever tried, has transmit timeouts under heavy tx load. So I
merged in only the tx changes from Edward Peng's sundance.c v1.03 (i
ignored the rest of it because it moved rx handling to a tasklet in what
looks like an emulation of a NAPI dev->poll function).

This is the first sundance driver I've used that handles both heavy RX
and TX load (where heavy is ~90kpps) without the card resetting on
transmit timeout. It's also the first in-kernel sundance.c that
recognizes a D-Link 580-TX 4-port ethercard.

It remains to be seen whether this driver still works with older
sundance cards; i'd appreciate it if anyone can test that.

Jason


--- sundance-kernel.c	Tue Aug 27 15:30:08 2002
+++ sundance-kernel-cleanup-b.c	Wed Aug 28 19:03:39 2002
@@ -1,6 +1,6 @@
 /* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */
 /*
-	Written 1999-2000 by Donald Becker.
+	Written 1999-2002 by Donald Becker.
 
 	This software may be used and distributed according to the terms of
 	the GNU General Public License (GPL), incorporated herein by reference.
@@ -23,13 +23,27 @@
 
 	Version 1.01b (D-Link):
 	- Add new board to PCI ID list
-	
+
+	Version 1.01c (Jason Lunz):
+	- merged changes from Donald Becker's sundance.c v1.09:
+	  . use IO ops by default (needed for D-Link 580TX)
+	  . autodetect need for mii_preamble_required
+	  . add per-adapter mtu change support
+	  . update driver status in SIOCSMIIREG ioctl
+	  . ignore IntrRxDone (buggy on some chipsets)
+	- minor cleanups
+
+	Version 1.01d (Jason Lunz):
+	- merged changes from Edward Peng's (of D-Link) sundance.c v1.03:
+	  . increase tx ring size
+	  . tx interrupt coalescing
+	  . support for flow control
 
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01b"
-#define DRV_RELDATE	"17-Jan-2002"
+#define DRV_VERSION	"1.01d"
+#define DRV_RELDATE	"28-Aug-2002"
 
 
 /* The user-configurable values.
@@ -37,7 +51,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 = 30;
-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;
@@ -47,7 +60,10 @@
    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;
 
+#define MAX_UNITS 8		/* More are supported, limit only on options */
 /* media[] specifies the media type the NIC operates at.
 		 autosense	Autosensing active media.
 		 10mbps_hd 	10Mbps half duplex.
@@ -60,18 +76,34 @@
 		 3	 	100Mbps half duplex.
 		 4	 	100Mbps full duplex.
 */
-#define MAX_UNITS 8	
 static char *media[MAX_UNITS];
+/* Used to pass the media type, etc.
+   Both 'options[]' and 'full_duplex[]' should exist for driver
+   interoperability.
+   The media type is usually passed in 'options[]'.
+    The default is autonegotation for speed and duplex.
+	This should rarely be overridden.
+    Use option values 0x10/0x20 for 10Mbps, 0x100,0x200 for 100Mbps.
+    Use option values 0x10 and 0x100 for forcing half duplex fixed speed.
+    Use option values 0x20 and 0x200 for forcing full duplex operation.
+*/
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* 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.
    The compiler will convert <unsigned>'%'<2^N> into a bit mask.
    Making the Tx ring too large decreases the effectiveness of channel
-   bonding and packet priority, and more than 128 requires modifying the
-   Tx error recovery.
+   bonding and packet priority, and more than 31 requires modifying the
+   Tx status handling error recovery.
    Large receive rings merely waste memory. */
-#define TX_RING_SIZE	16
-#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
+#define TX_RING_SIZE	64
+#define TX_QUEUE_LEN	(TX_RING_SIZE - 1) /* Limit ring entries actually used.  */
 #define RX_RING_SIZE	32
 #define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct netdev_desc)
 #define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct netdev_desc)
@@ -125,23 +157,33 @@
 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_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
-MODULE_PARM_DESC(mtu, "Sundance Alta MTU (all boards)");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(multicast_filter_limit, "i");
+MODULE_PARM(mii_preamble_required, "i");
+MODULE_PARM(flowctrl, "i");
 MODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)");
+MODULE_PARM_DESC(media, "Sundance Alta force fixed speed+duplex");
+MODULE_PARM_DESC(options, "Sundance Alta force transceiver type or fixed speed+duplex");
+MODULE_PARM_DESC(max_interrupt_work, "Sundance Alta maximum events handled per interrupt");
 MODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(multicast_filter_limit, "Sundance Alta multicast addresses before switching to Rx-all-multicast");
+MODULE_PARM_DESC(mii_preamble_required, "Sundance Alta force sending a preamble before MII management transactions");
+MODULE_PARM_DESC(flowctrl, "Sundance Alta flow control (0|1, default 1)");
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
 This driver is designed for the Sundance Technologies "Alta" ST201 chip.
+The Kendin KS8723 is the same design with an integrated transceiver.
 
 II. Board-specific settings
 
+This is an all-in-one chip, so there are no board-specific settings.
+
 III. Driver operation
 
 IIIa. Ring buffers
@@ -200,8 +242,9 @@
 IVb. References
 
 The Sundance ST201 datasheet, preliminary version.
-http://cesdis.gsfc.nasa.gov/linux/misc/100mbps.html
-http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+The Kendin KS8723 datasheet, preliminary version.
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
 
 IVc. Errata
 
@@ -209,6 +252,11 @@
 
 \f
 
+/* Work-around for Kendin chip bugs. */
+#ifndef USE_MEM_OPS
+#define USE_IO_OPS 1
+#endif
+
 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,
@@ -311,7 +359,7 @@
 	MACCtrl0 = 0x50,
 	MACCtrl1 = 0x52,
 	StationAddr = 0x54,
-	MaxTxSize = 0x5A,
+	MaxFrameSize = 0x5A,
 	RxMode = 0x5c,
 	MIICtrl = 0x5e,
 	MulticastFilter0 = 0x60,
@@ -402,21 +450,20 @@
 	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. */
+	unsigned int flowctrl:1;
 	/* 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 default_port:4;		/* Last dev->if_port value. */
-	unsigned int an_enable:1;
-	unsigned int speed;
 	/* Multicast and receive mode. */
 	spinlock_t mcastlock;				/* SMP lock multicast updates. */
 	u16 mcast_filter[4];
 	/* MII transceiver section. */
 	int mii_cnt;						/* MII device addresses. */
+	int link_status;
 	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[MII_CNT];		/* MII device addresses, only first one used. */
 	struct pci_dev *pci_dev;
@@ -425,6 +472,7 @@
 /* The station address location in the EEPROM. */
 #define EEPROM_SA_OFFSET	0x10
 
+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);
@@ -437,11 +485,10 @@
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
 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);
-static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int  netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  netdev_close(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
 
 \f
 
@@ -455,9 +502,9 @@
 	int irq;
 	int i;
 	long ioaddr;
-	u16 mii_ctl;
 	void *ring_space;
 	dma_addr_t ring_dma;
+	int option = card_idx < MAX_UNITS ? options[card_idx] : 0;
 
 
 /* when built into the kernel, we only print version if device is found */
@@ -524,11 +571,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;
@@ -542,77 +587,79 @@
 	if (1) {
 		int phy, phy_idx = 0;
 		np->phys[0] = 1;		/* Default setting */
+		mii_preamble_required++;
 		for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) {
 			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
 				np->advertising = mdio_read(dev, phy, 4);
+				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);
 			}
 		}
+		mii_preamble_required--;
 		np->mii_cnt = phy_idx;
 		if (phy_idx == 0)
 			printk(KERN_INFO "%s: No MII transceiver found!, ASIC status %x\n",
 				   dev->name, readl(ioaddr + ASICCtrl));
 	}
+
+	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;
+	}
+
 	/* Parse override configuration */
-	np->an_enable = 1;
 	if (card_idx < MAX_UNITS) {
 		if (media[card_idx] != NULL) {
-			np->an_enable = 0;
 			if (strcmp (media[card_idx], "100mbps_fd") == 0 ||
 			    strcmp (media[card_idx], "4") == 0) {
-				np->speed = 100;
-				np->full_duplex = 1;
+				option |= 0x200;
 			} else if (strcmp (media[card_idx], "100mbps_hd") == 0
 				   || strcmp (media[card_idx], "3") == 0) {
-				np->speed = 100;
-				np->full_duplex = 0;
+				option |= 0x100;
 			} else if (strcmp (media[card_idx], "10mbps_fd") == 0 ||
 				   strcmp (media[card_idx], "2") == 0) {
-				np->speed = 10;
-				np->full_duplex = 1;
+				option |= 0x20;
 			} else if (strcmp (media[card_idx], "10mbps_hd") == 0 ||
 				   strcmp (media[card_idx], "1") == 0) {
-				np->speed = 10;
-				np->full_duplex = 0;
-			} else {
-				np->an_enable = 1;
+				option |= 0x10;
 			}
 		}
 	}
 
-	/* Fibre PHY? */
-	if (readl (ioaddr + ASICCtrl) & 0x80) {
-		/* Default 100Mbps Full */
-		if (np->an_enable) {
-			np->speed = 100;
-			np->full_duplex = 1;
-			np->an_enable = 0;
-		}
+	/* Fibre PHY? Default 100Mbps Full */
+	if((readl(ioaddr + ASICCtrl) & 0x80) && (0 == (option & 0x3ff))) {
+		option |= 0x200;
 	}
-	/* Reset PHY */
-	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET);
-	mdelay (300);
-	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
-	/* Force media type */
-	if (!np->an_enable) {
-		mii_ctl = 0;
-		mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
-		mii_ctl |= (np->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");
 
+	/* Allow forcing the media type. */
+	if (option > 0) {
+		if (option & 0x220)
+			np->full_duplex = 1;
+		np->default_port = option & 0x3ff;
+		if (np->default_port & 0x330) {
+			np->medialock = 1;
+			printk(KERN_INFO "  Forcing %dMbps %s-duplex operation.\n",
+				   (option & 0x300 ? 100 : 10),
+				   np->full_duplex ? "full" : "half");
+			mdio_write(dev, np->phys[0], MII_BMCR,
+					(option & 0x330) ? BMCR_SPEED100 : 0 |
+					(np->full_duplex ? BMCR_FULLDPLX : 0));
+		}
 	}
 
-	/* Perhaps move the reset here? */
 	/* Reset the chip to erase previous misconfiguration. */
 	if (debug > 1)
 		printk("ASIC Control is %x.\n", readl(ioaddr + ASICCtrl));
-	writew(0x007f, ioaddr + ASICCtrl + 2);
+	writel(0x007f0000 | readl(ioaddr + ASICCtrl), ioaddr + ASICCtrl);
 	if (debug > 1)
 		printk("ASIC Control is now %x.\n", readl(ioaddr + ASICCtrl));
 
@@ -636,10 +683,21 @@
 }
 
 \f
+
+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)
 {
-	int boguscnt = 1000;		/* Typical 190 ticks. */
+	int boguscnt = 2000;		/* Typical 190 ticks. */
 	writew(0x0200 | (location & 0xff), ioaddr + EECtrl);
 	do {
 		if (! (readw(ioaddr + EECtrl) & 0x8000)) {
@@ -658,11 +716,6 @@
 	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,
 };
@@ -761,6 +814,11 @@
 
 	init_ring(dev);
 
+	if (dev->if_port == 0)
+		dev->if_port = np->default_port;
+
+	np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+
 	writel(np->rx_ring_dma, ioaddr + RxListPtr);
 	/* The Tx list pointer is written as packets are queued. */
 
@@ -768,26 +826,27 @@
 		writeb(dev->dev_addr[i], ioaddr + StationAddr + i);
 
 	/* Initialize other registers. */
-	/* Configure the PCI bus bursts and FIFO thresholds. */
-
-	if (dev->if_port == 0)
-		dev->if_port = np->default_port;
-
-	np->mcastlock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+	np->link_status = readb(ioaddr + MIICtrl) & 0xE0;
+	writew((np->full_duplex || (np->link_status & 0x20)) ? 0x120 : 0,
+		   ioaddr + MACCtrl0);
+	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. */
 	set_rx_mode(dev);
 	writew(0, ioaddr + IntrEnable);
 	writew(0, ioaddr + DownCounter);
 	/* Set the chip to poll every N*320nsec. */
 	writeb(100, ioaddr + RxDescPoll);
 	writeb(127, ioaddr + TxDescPoll);
-	netif_start_queue(dev);
 
 	/* Enable interrupts by setting the interrupt mask. */
-	writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+	writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
 		   | StatsMax | LinkChange, ioaddr + IntrEnable);
 
 	writew(StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
+	netif_start_queue(dev);
 
 	if (debug > 2)
 		printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
@@ -815,21 +874,23 @@
 	int duplex;
 	
 	/* Force media */
-	if (!np->an_enable || mii_lpa == 0xffff) {
+	if (np->medialock || mii_lpa == 0xffff) {
 		if (np->full_duplex)
 			writew (readw (ioaddr + MACCtrl0) | EnbFullDuplex,
 				ioaddr + MACCtrl0);
 		return;
 	}
 	/* Autonegotiation */
-	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+	duplex = (negotiated & LPA_100FULL) ||
+		(negotiated & (LPA_100FULL | LPA_100HALF | LPA_10FULL)) == LPA_10FULL;
 	if (np->full_duplex != duplex) {
 		np->full_duplex = duplex;
 		if (debug)
 			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);
-		writew(duplex ? 0x20 : 0, ioaddr + MACCtrl0);
+		writew(duplex ? (readw(ioaddr + MACCtrl0) | EnbFullDuplex) : 0,
+				ioaddr + MACCtrl0);
 	}
 }
 
@@ -875,13 +936,13 @@
 	/* Stop and restart the chip's Tx processes . */
 
 	/* Trigger an immediate transmit demand. */
-	writew(IntrRxDone | IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
+	writew(IntrRxDMADone | IntrPCIErr | IntrDrvRqst | IntrTxDone
 		   | StatsMax | LinkChange, ioaddr + IntrEnable);
 
 	dev->trans_start = jiffies;
 	np->stats.tx_errors++;
 
-	if (!np->tx_full)
+	if(!netif_queue_stopped(dev))
 		netif_wake_queue(dev);
 }
 
@@ -892,11 +953,10 @@
 	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;
 
-	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++) {
@@ -904,7 +964,7 @@
 			((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring));
 		np->rx_ring[i].status = 0;
 		np->rx_ring[i].frag[0].length = 0;
-		np->rx_skbuff[i] = 0;
+		np->rx_skbuff[i] = NULL;
 	}
 
 	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
@@ -923,7 +983,7 @@
 	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 
 	for (i = 0; i < TX_RING_SIZE; i++) {
-		np->tx_skbuff[i] = 0;
+		np->tx_skbuff[i] = NULL;
 		np->tx_ring[i].status = 0;
 	}
 	return;
@@ -945,8 +1005,12 @@
 
 	txdesc->next_desc = 0;
 	/* Note: disable the interrupt generation here before releasing. */
-	txdesc->status =
-		cpu_to_le32((entry<<2) | DescIntrOnDMADone | DescIntrOnTx | DisableAlign);
+	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);
@@ -958,10 +1022,10 @@
 
 	/* 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);
 	}
 	/* Side effect: The read wakes the potentially-idle transmit channel. */
@@ -972,9 +1036,13 @@
 	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 len %d queued in slot %d.\n",
+			   dev->name, np->cur_tx, skb->len, entry);
 	}
+
+	if(tx_coalesce > 1)
+		writel(1000, dev->base_addr + DownCounter);
+
 	return 0;
 }
 
@@ -993,21 +1061,21 @@
 
 	do {
 		int intr_status = readw(ioaddr + IntrStatus);
-		writew(intr_status & (IntrRxDone | IntrRxDMADone | IntrPCIErr |
-			IntrDrvRqst | IntrTxDone | IntrTxDMADone | StatsMax | 
-			LinkChange), ioaddr + IntrStatus);
+		if ((intr_status & ~IntrRxDone) == 0 || intr_status == 0xffff)
+			break;
+
+		writew(intr_status & (IntrRxDMADone | IntrPCIErr |
+					IntrDrvRqst | IntrTxDone | IntrTxDMADone |
+					StatsMax | LinkChange), ioaddr + IntrStatus);
 
 		if (debug > 4)
 			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
 				   dev->name, intr_status);
 
-		if (intr_status == 0)
-			break;
-
-		if (intr_status & (IntrRxDone|IntrRxDMADone))
+		if (intr_status & IntrRxDMADone)
 			netdev_rx(dev);
 
-		if (intr_status & IntrTxDone) {
+		if (intr_status & (IntrTxDone | IntrDrvRqst)) {
 			int boguscnt = 32;
 			int tx_status = readw(ioaddr + TxStatus);
 			while (tx_status & 0x80) {
@@ -1026,7 +1094,8 @@
 					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);
+						writel(0x001c0000 | readl(ioaddr + ASICCtrl),
+							   ioaddr + ASICCtrl);
 #if 0					/* Do we need to reset the Tx pointer here? */
 						writel(np->tx_ring_dma
 							+ np->dirty_tx*sizeof(*np->tx_ring),
@@ -1038,7 +1107,7 @@
 				}
 				/* Yup, this is a documentation bug.  It cost me *hours*. */
 				writew(0, ioaddr + TxStatus);
-				tx_status = readb(ioaddr + TxStatus);
+				tx_status = readw(ioaddr + TxStatus);
 				if (--boguscnt < 0)
 					break;
 			}
@@ -1057,26 +1126,22 @@
 			dev_kfree_skb_irq(skb);
 			np->tx_skbuff[entry] = 0;
 		}
-		if (np->tx_full
-			&& np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
+		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);
 		}
 
 		/* 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);
 			if (debug > 1) 
 				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);
@@ -1085,6 +1150,9 @@
 		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);
 }
 
@@ -1196,46 +1264,31 @@
 {
 	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)
-				printk ("100Mbps, full duplex\n");
-			else if (mii_advertise & ADVERTISE_100HALF)
-				printk ("100Mbps, half duplex\n");
-			else if (mii_advertise & ADVERTISE_10FULL)
-				printk ("10Mbps, full duplex\n");
-			else if (mii_advertise & ADVERTISE_10HALF)
-				printk ("10Mbps, half duplex\n");
+		int new_status = readb(ioaddr + MIICtrl) & 0xE0;
+		u16 mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
+		u16 mii_lpa = mdio_read (dev, np->phys[0], MII_LPA);
+
+		printk(KERN_ERR "%s: Link changed: Autonegotiation advertising "
+				"%dMbps %s duplex, partner %dMbps %s duplex.\n",
+				dev->name,
+				(mii_advertise & BMCR_SPEED100) ? 100 : 10,
+				(mii_advertise & BMCR_FULLDPLX) ? "full" : "half",
+				(mii_lpa & BMCR_SPEED100) ? 100 : 10,
+				(mii_lpa & BMCR_FULLDPLX) ? "full" : "half");
+		if ((np->link_status ^ new_status) & 0x80) {
+			/* need to check if this is even remotely correct */
+			if (new_status & 0x80)
+				netif_carrier_on(dev);
 			else
-				printk ("\n");
-
-		} else {
-			mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
-			speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
-			printk (KERN_INFO "%s: Link changed: %dMbps ,",
-				dev->name, speed);
-			printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
-				"full" : "half");
+				netif_carrier_off(dev);
 		}
-		check_duplex (dev);
+		np->link_status = new_status;
+		check_duplex(dev);
+		if(np->flowctrl == 0)
+			writew(readw(ioaddr + MACCtrl0) & ~EnbFlowCtrl,
+					ioaddr + MACCtrl0);
 	}
 	if (intr_status & StatsMax) {
 		get_stats(dev);
@@ -1253,6 +1306,9 @@
 	struct netdev_private *np = dev->priv;
 	int i;
 
+	if (readw(ioaddr + TxOctetsHigh) == 0xffff)
+		return &np->stats;
+
 	/* We should lock this segment of code for SMP eventually, although
 	   the vulnerability window is very small and statistics are
 	   non-critical. */
@@ -1329,6 +1385,38 @@
 		return 0;
 	}
 
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+
+		ecmd.supported = SUPPORTED_Autoneg;
+
+		spin_lock_irq(&np->lock);
+		if((readl(dev->base_addr + ASICCtrl) & 0x80)) {
+			ecmd.supported |= SUPPORTED_FIBRE;
+		} else {
+			ecmd.supported |= (SUPPORTED_100baseT_Half |
+					  SUPPORTED_100baseT_Full |
+					  SUPPORTED_10baseT_Half |
+					  SUPPORTED_10baseT_Full |
+					  SUPPORTED_MII);
+		}
+
+		ecmd.advertising = np->advertising;
+		ecmd.speed = 0;
+		ecmd.duplex = np->full_duplex;
+		ecmd.port = 0;
+		ecmd.phy_address = np->phys[0];
+		ecmd.transceiver = 0;
+		ecmd.autoneg = !np->medialock;
+		ecmd.maxtxpkt = 0;
+		ecmd.maxrxpkt = 0;
+
+		spin_unlock_irq(&np->lock);
+		if(copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+
         }
 	
 	return -EOPNOTSUPP;
@@ -1336,6 +1424,7 @@
 
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
+	struct netdev_private *np = dev->priv;
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
 
 	switch(cmd) {
@@ -1343,7 +1432,7 @@
 		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;
+		data->phy_id = np->phys[0] & 0x1f;
 		/* Fall Through */
 
 	case SIOCGMIIREG:		/* Read MII PHY register. */
@@ -1355,6 +1444,17 @@
 	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
+		if(data->phy_id == np->phys[0]) {
+			switch(data->reg_num) {
+			case 0:
+				np->medialock = (data->val_in & (BMCR_RESET|BMCR_ANENABLE)) ? 0 : 1;
+				if(np->medialock)
+					np->full_duplex = (data->val_in & BMCR_FULLDPLX) ? 1 : 0;
+				break;
+			case 4: np->advertising = data->val_in; break;
+			}
+			/* Perhaps check_duplex(dev), depending on chip semantics. */
+		}
 		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
 		return 0;
 	default:
@@ -1418,7 +1518,7 @@
 				np->rx_ring[i].frag[0].addr, np->rx_buf_sz, 
 				PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(skb);
-			np->rx_skbuff[i] = 0;
+			np->rx_skbuff[i] = NULL;
 		}
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
@@ -1428,7 +1528,7 @@
 				np->tx_ring[i].frag[0].addr, skb->len,
 				PCI_DMA_TODEVICE);
 			dev_kfree_skb(skb);
-			np->tx_skbuff[i] = 0;
+			np->tx_skbuff[i] = NULL;
 		}
 	}
 

  reply	other threads:[~2002-08-28 23:13 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-08-28 18:56 [PATCH] 2.4.20-pre sundance.c cleanups Jason Lunz
2002-08-28 23:13 ` Jason Lunz [this message]
2002-09-19  3:53   ` Richard Gooch
2002-09-19  4:14     ` Jason Lunz
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
2002-09-19  6:42       ` [PATCH] 2.4.20-pre sundance.c cleanups Richard Gooch
2002-09-19  7:11         ` Keith Owens
2002-09-19 12:58     ` Donald Becker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20020828231333.GA15183@reflexsecurity.com \
    --to=lunz@gtf.org \
    --cc=becker@scyld.com \
    --cc=jgarzik@mandrakesoft.com \
    --cc=mcmanus@ducksong.com \
    --cc=netdev@oss.sgi.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.