All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tim Hockin <thockin@sun.com>
To: Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	jgarzik@mandrakesoft.com, manfred@colorfullife.com
Subject: [PATCH] try #2 even bigger natsemi patch
Date: Fri, 19 Oct 2001 18:05:49 -0700	[thread overview]
Message-ID: <3BD0CDED.3850B31D@sun.com> (raw)

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

Jeff,

I took your feedback, and cleaned up both the patch and more.  Attached are
two seperate diffs - one for adding the natsemi to pci_ids.h, and one
against natsemi.c

I'll do my best to narrate the dif hunk by hunk.  I hope this is sufficient
for you to send it on, now :)

* comments

* update release info
* add strings for 83816
 
* formatting for 8-space tab disply

* increase RX Queue to eliminate some Something Wicked messages
* Define the timer frequency
* formatting

* formatting

* strings for 83816
* use new PCI ID (depends on pci_ids.h patch

* formatting
* add constants for magic register's values
* rename an enum for consistancy
* rename all 'bit' enums so they are named the same as their 
  register, and are found in the same order
* Add a comment about the default interrupt state
* Add some missing but needed bits definitions (TXConfig...)

* more bits
* constants for Silicon Revisions

* more bits for descriptor status

* store SRR in private struct
* formatting
* add mdio_write function protoype

* formatting

* magic number removal

* call eeprom reload during probe - BEFORE chip reset

* use MII defines for mdio
* magic number removal
* save SRR

* moved bits

* cleanup mdio_read to look more like mdio_write
* add mdio_write
* add defines for bits to save across a reset
* expand natsemi_reset() to save and restore state that would be 
  nuked by a chip reset

* add a natsemi_reload_eeprom() func

* use a constant for netdev timer
* formatting

* magic number removal

* use the stored SRR
* poll for AnegDone for a bit during open()

* use magic register constants
* comments
* cleanup for WoL
* magic numbers
* no need to blast mac-address, natsemi_reset should be doing it now

* magic numbers

* print if we got a WoL event

* add the phy reset checker to catch spurious PHY resets
* use mod_timer

* formatting

* magic numbers

* increase a debug level (didn't check into netif_msg yet - later

* magic numbers

* use mdio_read where applicable

* magic

* get rid of "Something Wicked" and print what it actually is

* fixup bit definitions

* only do SOPASS for rev D or up

* remove FIXME comment

* More SOPASS for rev D

* Magic numbers

* use MII defines where appropriate

* use mdio_read()/mdio_write()

* WoL cleanup


Detailed enough?  Please don't make me break it down further :)	
-- 
Tim Hockin
Systems Software Engineer
Sun Microsystems, Cobalt Server Appliances
thockin@sun.com

[-- Attachment #2: include_linux_pci_ids.h.diff --]
[-- Type: text/plain, Size: 507 bytes --]

diff -ruN dist-2.4.12+patches/include/linux/pci_ids.h cvs-2.4.12+patches/include/linux/pci_ids.h
--- dist-2.4.12+patches/include/linux/pci_ids.h	Mon Oct 15 10:23:43 2001
+++ cvs-2.4.12+patches/include/linux/pci_ids.h	Mon Oct 15 10:23:43 2001
@@ -285,6 +285,7 @@
 #define PCI_DEVICE_ID_NS_87415		0x0002
 #define PCI_DEVICE_ID_NS_87560_LIO	0x000e
 #define PCI_DEVICE_ID_NS_87560_USB	0x0012
+#define PCI_DEVICE_ID_NS_83815		0x0020
 #define PCI_DEVICE_ID_NS_87410		0xd001
 
 #define PCI_VENDOR_ID_TSENG		0x100c

[-- Attachment #3: drivers_net_natsemi.c.diff --]
[-- Type: text/plain, Size: 41060 bytes --]

diff -ruN dist-2.4.12+patches/drivers/net/natsemi.c cvs-2.4.12+patches/drivers/net/natsemi.c
--- dist-2.4.12+patches/drivers/net/natsemi.c	Mon Oct 15 10:22:07 2001
+++ cvs-2.4.12+patches/drivers/net/natsemi.c	Mon Oct 15 10:22:08 2001
@@ -85,6 +85,14 @@
 		* use long for ee_addr (various)
 		* print pointers properly (DaveM)
 		* include asm/irq.h (?)
+	
+	version 1.0.11:
+		* check and reset if PHY errors appear (Adrian Sun)
+		* WoL cleanup (Tim Hockin)
+		* Magic number cleanup (Tim Hockin)
+		* Don't reload EEPROM on every reset (Tim Hockin)
+		* Save and restore EEPROM state across reset (Tim Hockin)
+		* MDIO Cleanup (Tim Hockin)
 
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
@@ -93,9 +101,8 @@
 */
 
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.10"
-#define DRV_RELDATE	"Oct 09, 2001"
-
+#define DRV_VERSION	"1.07+LK1.0.11"
+#define DRV_RELDATE	"Oct 19, 2001"
 
 /* Updated to recommendations in pci-skeleton v2.03. */
 
@@ -106,7 +113,7 @@
 c-help-name: National Semiconductor DP8381x series PCI Ethernet support
 c-help-symbol: CONFIG_NATSEMI
 c-help: This driver is for the National Semiconductor DP8381x series,
-c-help: including the 83815 chip.
+c-help: including the 8381[56] chips.
 c-help: More specific information and updates are available from 
 c-help: http://www.scyld.com/network/natsemi.html
 */
@@ -114,10 +121,12 @@
 /* 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. */
+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 = 20;
 static int mtu;
+
 /* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
    This chip uses a 512 element hash table based on the Ethernet CRC.  */
 static int multicast_filter_limit = 100;
@@ -143,16 +152,17 @@
    bonding and packet priority.
    There are no ill effects from too-large receive rings. */
 #define TX_RING_SIZE	16
-#define TX_QUEUE_LEN	10		/* Limit ring entries actually used, min 4.  */
-#define RX_RING_SIZE	32
+#define TX_QUEUE_LEN	10 /* Limit ring entries actually used, min 4. */
+#define RX_RING_SIZE	64
 
 /* Operational parameters that usually are not changed. */
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (2*HZ)
 
 #define NATSEMI_HW_TIMEOUT	400
+#define NATSEMI_TIMER_FREQ	3*HZ
 
-#define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
+#define PKT_BUF_SZ		1536 /* Size of each temporary Rx buffer. */
 
 #if !defined(__OPTIMIZE__)
 #warning  You must compile this file with the correct options!
@@ -179,17 +189,18 @@
 #include <linux/delay.h>
 #include <linux/rtnetlink.h>
 #include <linux/mii.h>
-#include <asm/processor.h>		/* Processor type for cache alignment. */
+#include <asm/processor.h> /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 
 /* These identify the driver base version and may not be removed. */
-static char version[] __devinitdata =
-KERN_INFO DRV_NAME ".c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
-KERN_INFO "  http://www.scyld.com/network/natsemi.html\n"
-KERN_INFO "  (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "  Jeff Garzik, Tjeerd Mulder)\n";
+static char version[] __devinitdata = KERN_INFO 
+__FILE__ ":v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
+"  http://www.scyld.com/network/natsemi.html\n"
+"  unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "\n"
+"  Jeff Garzik, Tjeerd Mulder\n";
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
@@ -308,11 +319,11 @@
 	const char *name;
 	unsigned long flags;
 } natsemi_pci_info[] __devinitdata = {
-	{ "NatSemi DP83815", PCI_IOTYPE },
+	{ "NatSemi DP8381[56]", PCI_IOTYPE },
 };
 
 static struct pci_device_id natsemi_pci_tbl[] __devinitdata = {
-	{ 0x100B, 0x0020, PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0, },
 };
 MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
@@ -331,54 +342,94 @@
 	BootRomAddr=0x50, BootRomData=0x54, SiliconRev=0x58, StatsCtrl=0x5C,
 	StatsData=0x60, RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64,
 	BasicControl=0x80, BasicStatus=0x84,
-	AnegAdv=0x90, AnegPeer = 0x94, PhyStatus=0xC0, MIntrCtrl=0xC4, 
+	AnegAdv=0x90, AnegPeer=0x94, PhyStatus=0xC0, MIntrCtrl=0xC4, 
 	MIntrStatus=0xC8, PhyCtrl=0xE4,
 
 	/* These are from the spec, around page 78... on a separate table.
 	 * The meaning of these registers depend on the value of PGSEL. */
-	PGSEL=0xCC, PMDCSR=0xE4, TSTDAT=0xFC, DSPCFG=0xF4, SDCFG=0x8C
+	PGSEL=0xCC, PMDCSR=0xE4, TSTDAT=0xFC, DSPCFG=0xF4, SDCFG=0xF8
 };
+/* the values for the 'magic' registers above (PGSEL=1) */
+#define PMDCSR_VAL	0x189C
+#define TSTDAT_VAL	0x0
+#define DSPCFG_VAL	0x5040
+#define SDCFG_VAL	0x008c
 
 /* misc PCI space registers */
-enum PCISpaceRegs {
+enum pci_register_offsets {
 	PCIPM=0x44,
 };
 
-/* Bit in ChipCmd. */
-enum ChipCmdBits {
+enum ChipCmd_bits {
 	ChipReset=0x100, RxReset=0x20, TxReset=0x10, RxOff=0x08, RxOn=0x04,
 	TxOff=0x02, TxOn=0x01,
 };
 
-enum PCIBusCfgBits {
+enum ChipConfig_bits {
+	CfgPhyDis=0x200, CfgPhyRst=0x400, CfgAnegEnable=0x2000,
+	CfgAneg100=0x4000, CfgAnegFull=0x8000, CfgAnegDone=0x8000000,
+	CfgFullDuplex=0x20000000,
+	CfgSpeed100=0x40000000, CfgLink=0x80000000,
+};
+
+enum EECtrl_bits {
+	EE_ShiftClk=0x04, EE_DataIn=0x01, EE_ChipSelect=0x08, EE_DataOut=0x02,
+};
+
+enum PCIBusCfg_bits {
 	EepromReload=0x4,
 };
 
 /* Bits in the interrupt status/mask registers. */
-enum intr_status_bits {
-	IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008,
-	IntrRxIdle=0x0010, IntrRxOverrun=0x0020,
+enum IntrStatus_bits {
+	IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, 
+	IntrRxEarly=0x0008, IntrRxIdle=0x0010, IntrRxOverrun=0x0020,
 	IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100,
 	IntrTxIdle=0x0200, IntrTxUnderrun=0x0400,
-	StatsMax=0x0800, LinkChange=0x4000,
-	WOLPkt=0x2000,
+	StatsMax=0x0800, SWInt=0x1000, WOLPkt=0x2000, LinkChange=0x4000, 
+	IntrHighBits=0x8000,
+	RxStatusFIFOOver=0x10000,
+	IntrPCIErr=0xf00000,
 	RxResetDone=0x1000000, TxResetDone=0x2000000,
-	IntrPCIErr=0x00f00000,
-	IntrNormalSummary=0x025f, IntrAbnormalSummary=0xCD20,
+	IntrAbnormalSummary=0xCD20,
 };
 
+/*
+ * Default Interrupts:
+ * Rx OK, Rx Packet Error, Rx Overrun, 
+ * Tx OK, Tx Packet Error, Tx Underrun, 
+ * MIB Service, Phy Interrupt, High Bits,
+ * Rx Status FIFO overrun,
+ * Received Target Abort, Received Master Abort, 
+ * Signalled System Error, Received Parity Error
+ */
 #define DEFAULT_INTR 0x00f1cd65
 
-/* Bits in the RxMode register. */
-enum rx_mode_bits {
-	AcceptErr=0x20, AcceptRunt=0x10,
-	AcceptBroadcast=0xC0000000,
-	AcceptMulticast=0x00200000, AcceptAllMulticast=0x20000000,
-	AcceptAllPhys=0x10000000, AcceptMyPhys=0x08000000,
+enum TxConfig_bits {
+	TxDrthMask=0x3f, TxFlthMask=0x3f00, 
+	TxMxdmaMask=0x700000, TxMxdma_512=0x0, 
+	TxMxdma_4=0x100000, TxMxdma_8=0x200000, TxMxdma_16=0x300000,
+	TxMxdma_32=0x400000, TxMxdma_64=0x500000, TxMxdma_128=0x600000,
+	TxMxdma_256=0x700000, TxCollRetry=0x800000,
+	TxAutoPad=0x10000000, TxMacLoop=0x20000000,
+	TxHeartIgn=0x40000000, TxCarrierIgn=0x80000000
 };
 
-/* Bits in WOLCmd register. */
-enum wol_bits {
+enum RxConfig_bits {
+	RxDrthMask=0x3e,
+	RxMxdmaMask=0x700000, RxMxdma_512=0x0, 
+	RxMxdma_4=0x100000, RxMxdma_8=0x200000, RxMxdma_16=0x300000,
+	RxMxdma_32=0x400000, RxMxdma_64=0x500000, RxMxdma_128=0x600000,
+	RxMxdma_256=0x700000,
+       	RxAcceptLong=0x8000000, RxAcceptTx=0x10000000, 
+	RxAcceptRunt=0x40000000, RxAcceptErr=0x80000000
+};
+
+enum ClkRun_bits {
+	PMEEnable=0x100, PMEStatus=0x8000,
+};
+
+enum WolCmd_bits {
 	WakePhy=0x1, WakeUnicast=0x2, WakeMulticast=0x4, WakeBroadcast=0x8,
 	WakeArp=0x10, WakePMatch0=0x20, WakePMatch1=0x40, WakePMatch2=0x80,
 	WakePMatch3=0x100, WakeMagic=0x200, WakeMagicSecure=0x400, 
@@ -388,23 +439,28 @@
 	WokePMatch3=0x40000000, WokeMagic=0x80000000, WakeOptsSummary=0x7ff
 };
 
-enum aneg_bits {
-	Aneg10BaseT=0x20, Aneg10BaseTFull=0x40, 
-	Aneg100BaseT=0x80, Aneg100BaseTFull=0x100,
+enum RxFilterAddr_bits {
+	RFCRAddressMask=0x3ff, 
+	AcceptMulticast=0x00200000, AcceptMyPhys=0x08000000,
+	AcceptAllPhys=0x10000000, AcceptAllMulticast=0x20000000,
+	AcceptBroadcast=0x40000000, RxFilterEnable=0x80000000
 };
 
-enum config_bits {
-	CfgPhyDis=0x200, CfgPhyRst=0x400, CfgAnegEnable=0x2000,
-	CfgAneg100=0x4000, CfgAnegFull=0x8000, CfgAnegDone=0x8000000,
-	CfgFullDuplex=0x20000000,
-	CfgSpeed100=0x40000000, CfgLink=0x80000000,
+enum StatsCtrl_bits {
+	StatsWarn=0x1, StatsFreeze=0x2, StatsClear=0x4, StatsStrobe=0x8,
 };
 
-enum bmcr_bits {
-	BMCRDuplex=0x100, BMCRAnegRestart=0x200, BMCRAnegEnable=0x1000,
-	BMCRSpeed=0x2000, BMCRPhyReset=0x8000,
+enum MIntrCtrl_bits {
+	MICRIntEn=0x2,
 };
 
+enum PhyCtrl_bits {
+	PhyAddrMask = 0xf,
+};
+
+#define SRR_REV_C	0x0302
+#define SRR_REV_D	0x0403
+
 /* The Rx and Tx buffer descriptors. */
 /* Note that using only 32 bit fields simplifies conversion to big-endian
    architectures. */
@@ -418,8 +474,19 @@
 /* Bits in network_desc.status */
 enum desc_status_bits {
 	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
-	DescNoCRC=0x10000000,
-	DescPktOK=0x08000000, RxTooLong=0x00400000,
+	DescNoCRC=0x10000000, DescPktOK=0x08000000, 
+	DescSizeMask=0xfff,
+
+	DescTxAbort=0x04000000, DescTxFIFO=0x02000000, 
+	DescTxCarrier=0x01000000, DescTxDefer=0x00800000,
+	DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,
+	DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,
+	
+	DescRxAbort=0x04000000, DescRxOver=0x02000000,
+	DescRxDest=0x01800000, DescRxLong=0x00400000,
+	DescRxRunt=0x00200000, DescRxInvalid=0x00100000,
+	DescRxCRC=0x00080000, DescRxAlign=0x00040000,
+	DescRxLoop=0x00020000, DesRxColl=0x00010000,
 };
 
 struct netdev_private {
@@ -450,17 +517,21 @@
 	u32 tx_config, rx_config;
 	/* original contents of ClkRun register */
 	u32 SavedClkRun;
+	/* silicon revision */
+	u32 srr;
 	/* MII transceiver section. */
-	u16 advertising;			/* NWay media advertisement */
+	u16 advertising; /* NWay media advertisement */
 	unsigned int iosize;
 	spinlock_t lock;
 };
 
-static int  eeprom_read(long ioaddr, int location);
-static int  mdio_read(struct net_device *dev, int phy_id, int location);
+static int eeprom_read(long ioaddr, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int reg);
+static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data);
 static void natsemi_reset(struct net_device *dev);
+static void natsemi_reload_eeprom(struct net_device *dev);
 static void natsemi_stop_rxtx(struct net_device *dev);
-static int  netdev_open(struct net_device *dev);
+static int netdev_open(struct net_device *dev);
 static void check_link(struct net_device *dev);
 static void netdev_timer(unsigned long data);
 static void tx_timeout(struct net_device *dev);
@@ -469,7 +540,7 @@
 static void drain_ring(struct net_device *dev);
 static void free_ring(struct net_device *dev);
 static void init_registers(struct net_device *dev);
-static int  start_tx(struct sk_buff *skb, struct net_device *dev);
+static int start_tx(struct sk_buff *skb, struct net_device *dev);
 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 void netdev_rx(struct net_device *dev);
@@ -486,7 +557,7 @@
 static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);
 static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd);
 static void enable_wol_mode(struct net_device *dev, int enable_intr);
-static int  netdev_close(struct net_device *dev);
+static int netdev_close(struct net_device *dev);
 
 \f
 static int __devinit natsemi_probe1 (struct pci_dev *pdev,
@@ -516,9 +587,9 @@
 	 * to be brought to D0 in this manner.
 	 */
 	pci_read_config_dword(pdev, PCIPM, &tmp);
-	if (tmp & (0x03|0x100)) {
+	if (tmp & PCI_PM_CTRL_STATE_MASK) {
 		/* D0 state, disable PME assertion */
-		u32 newtmp = tmp & ~(0x03|0x100);
+		u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK;
 		pci_write_config_dword(pdev, PCIPM, newtmp);
 	}
 
@@ -571,7 +642,9 @@
 	spin_lock_init(&np->lock);
 
 	/* Reset the chip to erase previous misconfiguration. */
+	natsemi_reload_eeprom(dev);
 	natsemi_reset(dev);
+
 	option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -616,20 +689,23 @@
 			printk("%2.2x:", dev->dev_addr[i]);
 	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
 
-	np->advertising = mdio_read(dev, 1, 4);
+	np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
 	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) {
 		u32 chip_config = readl(ioaddr + ChipConfig);
 		printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
 			   "10%s %s duplex.\n",
 			   dev->name,
-			   chip_config & 0x2000 ? "enabled, advertise" : "disabled, force",
-			   chip_config & 0x4000 ? "0" : "",
-			   chip_config & 0x8000 ? "full" : "half");
+			   chip_config & CfgAnegEnable ? "enabled, advertise" : "disabled, force",
+			   chip_config & CfgAneg100 ? "0" : "",
+			   chip_config & CfgAnegFull ? "full" : "half");
 	}
 	printk(KERN_INFO "%s: Transceiver status 0x%4.4x advertising %4.4x.\n",
-		   dev->name, (int)readl(ioaddr + BasicStatus), 
+		   dev->name, (int)mdio_read(dev, 1, MII_BMSR), 
 		   np->advertising);
 
+	/* save the silicon revision for later querying */
+	np->srr = readl(ioaddr + SiliconRev);
+
 	return 0;
 }
 
@@ -646,9 +722,6 @@
 */
 #define eeprom_delay(ee_addr)	readl(ee_addr)
 
-enum EEPROM_Ctrl_Bits {
-	EE_ShiftClk=0x04, EE_DataIn=0x01, EE_ChipSelect=0x08, EE_DataOut=0x02,
-};
 #define EE_Write0 (EE_ChipSelect)
 #define EE_Write1 (EE_ChipSelect | EE_DataIn)
 
@@ -694,18 +767,67 @@
 	The 83815 series has an internal transceiver, and we present the
 	management registers as if they were MII connected. */
 
-static int mdio_read(struct net_device *dev, int phy_id, int location)
+static int mdio_read(struct net_device *dev, int phy_id, int reg)
 {
-	if (phy_id == 1 && location < 32)
-		return readl(dev->base_addr+BasicControl+(location<<2))&0xffff;
+	if (phy_id == 1 && reg < 32)
+		return readl(dev->base_addr+BasicControl+(reg<<2))&0xffff;
 	else
 		return 0xffff;
 }
 
+static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 data)
+{
+	struct netdev_private *np = dev->priv;
+	if (phy_id == 1 && reg < 32) {
+		writew(data, dev->base_addr+BasicControl+(reg<<2));
+		switch (reg) {
+			case MII_ADVERTISE: np->advertising = data; break;
+		}
+	}
+}
+
+/* CFG bits [13:16] [18:23] */
+#define CFG_RESET_SAVE 0xfde000
+/* WCSR bits [0:4] [9:10] */
+#define WCSR_RESET_SAVE 0x61f
+/* RFCR bits [20] [22] [27:31] */
+#define RFCR_RESET_SAVE 0xf8500000;
+
 static void natsemi_reset(struct net_device *dev)
 {
 	int i;
+	u32 cfg;
+	u32 wcsr;
+	u32 rfcr;
+	u16 pmatch[3];
+	u16 sopass[3];
+
+	/* 
+	 * Resetting the chip causes some registers to be lost.
+	 * Natsemi suggests NOT reloading the EEPROM while live, so instead
+	 * we save the state that would have been loaded from EEPROM
+	 * on a normal power-up (see the spec EEPROM map).  This assumes 
+	 * whoever calls this will follow up with init_registers() eventually.
+	 */
+
+	/* CFG */
+	cfg = readl(dev->base_addr + ChipConfig) & CFG_RESET_SAVE;
+	/* WCSR */
+	wcsr = readl(dev->base_addr + WOLCmd) & WCSR_RESET_SAVE;
+	/* RFCR */
+	rfcr = readl(dev->base_addr + RxFilterAddr) & RFCR_RESET_SAVE;
+	/* PMATCH */
+	for (i = 0; i < 3; i++) {
+		writel(i*2, dev->base_addr + RxFilterAddr);
+		pmatch[i] = readw(dev->base_addr + RxFilterData);
+	}
+	/* SOPAS */
+	for (i = 0; i < 3; i++) {
+		writel(0xa+(i*2), dev->base_addr + RxFilterAddr);
+		sopass[i] = readw(dev->base_addr + RxFilterData);
+	}
 
+	/* now whack the chip */
 	writel(ChipReset, dev->base_addr + ChipCmd);
 	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
 		if (!(readl(dev->base_addr + ChipCmd) & ChipReset))
@@ -720,6 +842,32 @@
 		   dev->name, i*5);
 	}
 
+	/* restore CFG */
+	cfg |= readl(dev->base_addr + ChipConfig) & ~CFG_RESET_SAVE;
+	writel(cfg, dev->base_addr + ChipConfig);
+	/* restore WCSR */
+	wcsr |= readl(dev->base_addr + WOLCmd) & ~WCSR_RESET_SAVE;
+	writel(wcsr, dev->base_addr + WOLCmd);
+	/* read RFCR */
+	rfcr |= readl(dev->base_addr + RxFilterAddr) & ~RFCR_RESET_SAVE;
+	/* restore PMATCH */ 
+	for (i = 0; i < 3; i++) {
+		writel(i*2, dev->base_addr + RxFilterAddr);
+		writew(pmatch[i], dev->base_addr + RxFilterData);
+	}
+	for (i = 0; i < 3; i++) {
+		writel(0xa+(i*2), dev->base_addr + RxFilterAddr);
+		writew(sopass[i], dev->base_addr + RxFilterData);
+	}
+	/* restore RFCR */
+	writel(rfcr, dev->base_addr + RxFilterAddr);
+	
+}
+
+static void natsemi_reload_eeprom(struct net_device *dev)
+{
+	int i;
+
 	writel(EepromReload, dev->base_addr + PCIBusCfg);
 	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
 		if (!(readl(dev->base_addr + PCIBusCfg) & EepromReload))
@@ -788,9 +936,9 @@
 
 	/* Set the timer to check for link beat. */
 	init_timer(&np->timer);
-	np->timer.expires = jiffies + 3*HZ;
+	np->timer.expires = jiffies + NATSEMI_TIMER_FREQ;
 	np->timer.data = (unsigned long)dev;
-	np->timer.function = &netdev_timer;				/* timer handler */
+	np->timer.function = &netdev_timer; /* timer handler */
 	add_timer(&np->timer);
 
 	return 0;
@@ -803,7 +951,7 @@
 	int duplex;
 	int chipcfg = readl(ioaddr + ChipConfig);
 
-	if(!(chipcfg & 0x80000000)) {
+	if(!(chipcfg & CfgLink)) {
 		if (netif_carrier_ok(dev)) {
 			if (debug)
 				printk(KERN_INFO "%s: no link. Disabling watchdog.\n",
@@ -819,20 +967,20 @@
 		netif_carrier_on(dev);
 	}
 
-	duplex = np->full_duplex || (chipcfg & 0x20000000 ? 1 : 0);
+	duplex = np->full_duplex || (chipcfg & CfgFullDuplex ? 1 : 0);
 
 	/* if duplex is set then bit 28 must be set, too */
-	if (duplex ^ !!(np->rx_config & 0x10000000)) {
+	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
 		if (debug)
 			printk(KERN_INFO "%s: Setting %s-duplex based on negotiated link"
 				   " capability.\n", dev->name,
 				   duplex ? "full" : "half");
 		if (duplex) {
-			np->rx_config |= 0x10000000;
-			np->tx_config |= 0xC0000000;
+			np->rx_config |= RxAcceptTx;
+			np->tx_config |= TxCarrierIgn | TxHeartIgn;
 		} else {
-			np->rx_config &= ~0x10000000;
-			np->tx_config &= ~0xC0000000;
+			np->rx_config &= ~RxAcceptTx;
+			np->tx_config &= ~(TxCarrierIgn | TxHeartIgn);
 		}
 		writel(np->tx_config, ioaddr + TxConfig);
 		writel(np->rx_config, ioaddr + RxConfig);
@@ -845,9 +993,21 @@
 	long ioaddr = dev->base_addr;
 	int i;
 
+	/* save the silicon revision for later */
 	if (debug > 4)
 		printk(KERN_DEBUG "%s: found silicon revision %xh.\n",
-				dev->name, readl(ioaddr + SiliconRev));
+				dev->name, np->srr);
+
+	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+		if (readl(dev->base_addr + ChipConfig) & CfgAnegDone)
+			break;
+		udelay(10);
+	}
+	if (i==NATSEMI_HW_TIMEOUT && debug) {
+		printk(KERN_INFO 
+			"%s: autonegotiation did not complete in %d usec.\n",
+			dev->name, i*10);
+	}
 
 	/* On page 78 of the spec, they recommend some settings for "optimum
 	   performance" to be done in sequence.  These settings optimize some
@@ -856,26 +1016,26 @@
 	   Kennedy) recommends always setting them.  If you don't, you get 
 	   errors on some autonegotiations that make the device unusable.
 	*/
-	writew(0x0001, ioaddr + PGSEL);
-	writew(0x189C, ioaddr + PMDCSR);
-	writew(0x0000, ioaddr + TSTDAT);
-	writew(0x5040, ioaddr + DSPCFG);
-	writew(0x008C, ioaddr + SDCFG);
-	writew(0x0000, ioaddr + PGSEL);
+	writew(1, ioaddr + PGSEL);
+	writew(PMDCSR_VAL, ioaddr + PMDCSR);
+	writew(TSTDAT_VAL, ioaddr + TSTDAT);
+	writew(DSPCFG_VAL, ioaddr + DSPCFG);
+	writew(SDCFG_VAL, ioaddr + SDCFG);
+	writew(0, ioaddr + PGSEL);
 
 	/* Enable PHY Specific event based interrupts.  Link state change
 	   and Auto-Negotiation Completion are among the affected.
+	   Read the intr status to clear it (needed for wake events).
 	*/
-	writew(0x0002, ioaddr + MIntrCtrl);
+	readw(ioaddr + MIntrStatus);
+	writew(MICRIntEn, ioaddr + MIntrCtrl);
 
-	writel(np->ring_dma, ioaddr + RxRingPtr);
-	writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc), ioaddr + TxRingPtr);
+	/* clear any interrupts that are pending, such as wake events */
+	readl(ioaddr + IntrStatus);
 
-	for (i = 0; i < ETH_ALEN; i += 2) {
-		writel(i, ioaddr + RxFilterAddr);
-		writew(dev->dev_addr[i] + (dev->dev_addr[i+1] << 8),
-			   ioaddr + RxFilterData);
-	}
+	writel(np->ring_dma, ioaddr + RxRingPtr);
+	writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc), 
+		ioaddr + TxRingPtr);
 
 	/* Initialize other registers.
 	 * Configure the PCI bus bursts and FIFO thresholds.
@@ -891,12 +1051,13 @@
 	 * ECRETRY=1
 	 * ATP=1
 	 */
-	np->tx_config = 0x10f01002;
+	np->tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 | (0x1002);
+	writel(np->tx_config, ioaddr + TxConfig);
+
 	/* DRTH 0x10: start copying to memory if 128 bytes are in the fifo
 	 * MXDMA 0: up to 256 byte bursts
 	 */
-	np->rx_config = 0x700020;
-	writel(np->tx_config, ioaddr + TxConfig);
+	np->rx_config = RxMxdma_256 | 0x20;
 	writel(np->rx_config, ioaddr + RxConfig);
 
 	/* Disable PME:
@@ -906,24 +1067,37 @@
 	 * With PME set the chip will scan incoming packets but
 	 * nothing will be written to memory. */
 	np->SavedClkRun = readl(ioaddr + ClkRun);
-	writel(np->SavedClkRun & ~0x100, ioaddr + ClkRun);
+	writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
+	if (np->SavedClkRun & PMEStatus) {
+		printk(KERN_NOTICE "%s: Wake-up event %8.8x\n", 
+			dev->name, readl(ioaddr + WOLCmd));
+	}
 
 	check_link(dev);
 	__set_rx_mode(dev);
 
 	/* Enable interrupts by setting the interrupt mask. */
- 	writel(DEFAULT_INTR, ioaddr + IntrMask);
+	writel(DEFAULT_INTR, ioaddr + IntrMask);
 	writel(1, ioaddr + IntrEnable);
 
 	writel(RxOn | TxOn, ioaddr + ChipCmd);
-	writel(4, ioaddr + StatsCtrl); /* Clear Stats */
+	writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */
 }
 
+/* 
+ * The frequency on this has been increased because of a nasty little problem.
+ * It seems that a reference set for this chip went out with incorrect info,
+ * and there exist boards that aren't quite right.  An unexpected voltage drop
+ * can cause the PHY to get itself in a weird state (basically reset..).
+ * NOTE: this only seems to affect revC chips.
+ */
 static void netdev_timer(unsigned long data)
 {
 	struct net_device *dev = (struct net_device *)data;
 	struct netdev_private *np = dev->priv;
-	int next_tick = 60*HZ;
+	int next_tick = 5*HZ;
+	long ioaddr = dev->base_addr;
+	u16 dspcfg;
 
 	if (debug > 3) {
 		/* DO NOT read the IntrStatus register, 
@@ -933,10 +1107,27 @@
 			   dev->name);
 	}
 	spin_lock_irq(&np->lock);
-	check_link(dev);
+
+	/* check for a nasty random phy-reset - use dspcfg as a flag */
+	writew(1, ioaddr+PGSEL);
+	dspcfg = readw(ioaddr+DSPCFG);
+	writew(0, ioaddr+PGSEL);
+	if (dspcfg != DSPCFG_VAL) {
+		if (!netif_queue_stopped(dev)) {
+			printk(KERN_INFO 
+				"%s: possible phy reset: re-initializing\n",
+				dev->name);
+			init_registers(dev);
+		} else {
+			/* hurry back */
+			next_tick = HZ;
+		}
+	} else {
+		/* init_registers() calls check_link() for the above case */
+		check_link(dev);
+	}
 	spin_unlock_irq(&np->lock);
-	np->timer.expires = jiffies + next_tick;
-	add_timer(&np->timer);
+	mod_timer(&np->timer, jiffies + next_tick);
 }
 
 static void dump_ring(struct net_device *dev)
@@ -946,15 +1137,18 @@
 	if (debug > 2) {
 		int i;
 		printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
-		for (i = 0; i < TX_RING_SIZE; i++)
+		for (i = 0; i < TX_RING_SIZE; i++) {
 			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
 				   i, np->tx_ring[i].next_desc,
-				   np->tx_ring[i].cmd_status, np->tx_ring[i].addr);
+				   np->tx_ring[i].cmd_status, 
+				   np->tx_ring[i].addr);
+		}
 		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++) {
 			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
 				   i, np->rx_ring[i].next_desc,
-				   np->rx_ring[i].cmd_status, np->rx_ring[i].addr);
+				   np->rx_ring[i].cmd_status, 
+				   np->rx_ring[i].addr);
 		}
 	}
 }
@@ -964,12 +1158,12 @@
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 
-
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
 	if (netif_device_present(dev)) {
 		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
-			   " resetting...\n", dev->name, readl(ioaddr + IntrStatus));
+			" resetting...\n", 
+			dev->name, readl(ioaddr + IntrStatus));
 		dump_ring(dev);
 
 		natsemi_reset(dev);
@@ -977,8 +1171,9 @@
 		init_ring(dev);
 		init_registers(dev);
 	} else {
-		printk(KERN_WARNING "%s: tx_timeout while in suspended state?\n",
-		   		dev->name);
+		printk(KERN_WARNING 
+			"%s: tx_timeout while in suspended state?\n",
+		   	dev->name);
 	}
 	spin_unlock_irq(&np->lock);
 	enable_irq(dev->irq);
@@ -1019,7 +1214,7 @@
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		np->rx_ring[i].next_desc = cpu_to_le32(np->ring_dma
 				+sizeof(struct netdev_desc)
-				 *((i+1)%RX_RING_SIZE));
+				*((i+1)%RX_RING_SIZE));
 		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
 		np->rx_skbuff[i] = NULL;
 	}
@@ -1107,7 +1302,8 @@
 	
 	if (netif_device_present(dev)) {
 		np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
-		/* StrongARM: Explicitly cache flush np->tx_ring and skb->data,skb->len. */
+		/* StrongARM: Explicitly cache flush np->tx_ring and 
+		 * skb->data,skb->len. */
 		wmb();
 		np->cur_tx++;
 		if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) {
@@ -1148,15 +1344,19 @@
 			printk(KERN_DEBUG "%s: tx frame #%d finished with status %8.8xh.\n",
 					dev->name, np->dirty_tx,
 					le32_to_cpu(np->tx_ring[entry].cmd_status));
-		if (np->tx_ring[entry].cmd_status & cpu_to_le32(0x08000000)) {
+		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
 			np->stats.tx_packets++;
 			np->stats.tx_bytes += np->tx_skbuff[entry]->len;
-		} else {			/* Various Tx errors */
+		} else { /* Various Tx errors */
 			int tx_status = le32_to_cpu(np->tx_ring[entry].cmd_status);
-			if (tx_status & 0x04010000) np->stats.tx_aborted_errors++;
-			if (tx_status & 0x02000000) np->stats.tx_fifo_errors++;
-			if (tx_status & 0x01000000) np->stats.tx_carrier_errors++;
-			if (tx_status & 0x00200000) np->stats.tx_window_errors++;
+			if (tx_status & (DescTxAbort|DescTxExcColl)) 
+				np->stats.tx_aborted_errors++;
+			if (tx_status & DescTxFIFO) 
+				np->stats.tx_fifo_errors++;
+			if (tx_status & DescTxCarrier) 
+				np->stats.tx_carrier_errors++;
+			if (tx_status & DescTxOOWCol) 
+				np->stats.tx_window_errors++;
 			np->stats.tx_errors++;
 		}
 		pci_unmap_single(np->pci_dev,np->tx_dma[entry],
@@ -1219,7 +1419,7 @@
 		}
 	} while (1);
 
-	if (debug > 3)
+	if (debug > 4)
 		printk(KERN_DEBUG "%s: exiting interrupt.\n",
 			   dev->name);
 }
@@ -1240,7 +1440,7 @@
 				   entry, desc_status);
 		if (--boguscnt < 0)
 			break;
-		if ((desc_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) {
+		if ((desc_status & (DescMore|DescPktOK|DescRxLong)) != DescPktOK) {
 			if (desc_status & DescMore) {
 				printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned "
 					   "multiple buffers, entry %#x status %x.\n",
@@ -1252,14 +1452,19 @@
 					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
 						   desc_status);
 				np->stats.rx_errors++;
-				if (desc_status & 0x06000000) np->stats.rx_over_errors++;
-				if (desc_status & 0x00600000) np->stats.rx_length_errors++;
-				if (desc_status & 0x00140000) np->stats.rx_frame_errors++;
-				if (desc_status & 0x00080000) np->stats.rx_crc_errors++;
+				if (desc_status & (DescRxAbort|DescRxOver)) 
+					np->stats.rx_over_errors++;
+				if (desc_status & (DescRxLong|DescRxRunt)) 
+					np->stats.rx_length_errors++;
+				if (desc_status & (DescRxInvalid|DescRxAlign)) 
+					np->stats.rx_frame_errors++;
+				if (desc_status & DescRxCRC) 
+					np->stats.rx_crc_errors++;
 			}
 		} else {
 			struct sk_buff *skb;
-			int pkt_len = (desc_status & 0x0fff) - 4; 	/* Omit CRC size. */
+			/* Omit CRC size. */
+			int pkt_len = (desc_status & DescSizeMask) - 4;
 			/* Check if the packet is long enough to accept without copying
 			   to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
@@ -1324,10 +1529,11 @@
 
 	spin_lock(&np->lock);
 	if (intr_status & LinkChange) {
-		printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising"
-			   " %4.4x  partner %4.4x.\n", dev->name,
-			   (int)readl(ioaddr + AnegAdv), 
-			   (int)readl(ioaddr + AnegPeer));
+		printk(KERN_NOTICE 
+			"%s: Link changed: Autonegotiation advertising"
+			" %4.4x  partner %4.4x.\n", dev->name,
+			(int)mdio_read(dev, 1, MII_ADVERTISE), 
+			(int)mdio_read(dev, 1, MII_LPA));
 		/* read MII int status to clear the flag */
 		readw(ioaddr + MIntrStatus);
 		check_link(dev);
@@ -1336,7 +1542,7 @@
 		__get_stats(dev);
 	}
 	if (intr_status & IntrTxUnderrun) {
-		if ((np->tx_config & 0x3f) < 62)
+		if ((np->tx_config & TxDrthMask) < 62)
 			np->tx_config += 2;
 		if (debug > 2)
 			printk(KERN_NOTICE "%s: increasing Tx theshold, new tx cfg %8.8xh.\n",
@@ -1348,12 +1554,15 @@
 		printk(KERN_NOTICE "%s: Link wake-up event %8.8x\n",
 			   dev->name, wol_status);
 	}
-	if ((intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone|0xA7ff))
-		&& debug)
-		printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n",
-			   dev->name, intr_status);
+	if (intr_status & RxStatusFIFOOver && debug) {
+		printk(KERN_NOTICE "%s: Rx status FIFO overrun\n", dev->name);
+	}
 	/* Hmmmmm, it's not clear how to recover from PCI faults. */
 	if (intr_status & IntrPCIErr) {
+		if (debug) {
+			printk(KERN_NOTICE "%s: PCI error %08x\n", dev->name,
+				intr_status & IntrPCIErr);
+		}
 		np->stats.tx_fifo_errors++;
 		np->stats.rx_fifo_errors++;
 	}
@@ -1453,11 +1662,12 @@
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
 		/* Unconditionally log net taps. */
 		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
-		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptAllPhys
-			| AcceptMyPhys;
+		rx_mode = RxFilterEnable | AcceptBroadcast 
+			| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
 	} else if ((dev->mc_count > multicast_filter_limit)
 			   ||  (dev->flags & IFF_ALLMULTI)) {
-		rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys;
+		rx_mode = RxFilterEnable | AcceptBroadcast 
+			| AcceptAllMulticast | AcceptMyPhys;
 	} else {
 		struct dev_mc_list *mclist;
 		int i;
@@ -1467,10 +1677,12 @@
 			set_bit_le(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
 					mc_filter);
 		}
-		rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys;
+		rx_mode = RxFilterEnable | AcceptBroadcast 
+			| AcceptMulticast | AcceptMyPhys;
 		for (i = 0; i < 64; i += 2) {
 			writew(HASH_TABLE + i, ioaddr + RxFilterAddr);
-			writew((mc_filter[i+1]<<8) + mc_filter[i], ioaddr + RxFilterData);
+			writew((mc_filter[i+1]<<8) + mc_filter[i], 
+				ioaddr + RxFilterData);
 		}
 	}
 	writel(rx_mode, ioaddr + RxFilterAddr);
@@ -1550,6 +1762,7 @@
 
 static int netdev_set_wol(struct net_device *dev, u32 newval)
 {
+	struct netdev_private *np = dev->priv;
 	u32 data = readl(dev->base_addr + WOLCmd) & ~WakeOptsSummary;
 
 	/* translate to bitmasks this chip understands */
@@ -1565,49 +1778,65 @@
 		data |= WakeArp;
 	if (newval & WAKE_MAGIC)
 		data |= WakeMagic;
-	if (newval & WAKE_MAGICSECURE)
-		data |= WakeMagicSecure;
+	if (np->srr >= SRR_REV_D) {
+		if (newval & WAKE_MAGICSECURE) {
+			data |= WakeMagicSecure;
+		}
+	}
 
 	writel(data, dev->base_addr + WOLCmd);
 
-	/* should we burn these into the EEPROM? */
-	
 	return 0;
 }
 
 static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
 {
+	struct netdev_private *np = dev->priv;
 	u32 regval = readl(dev->base_addr + WOLCmd);
 
 	*supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST 
-			| WAKE_ARP | WAKE_MAGIC | WAKE_MAGICSECURE);
+			| WAKE_ARP | WAKE_MAGIC);
+	
+	if (np->srr >= SRR_REV_D) {
+		/* SOPASS works on revD and higher */
+		*supported |= WAKE_MAGICSECURE;
+	}
 	*cur = 0;
+
 	/* translate from chip bitmasks */
-	if (regval & 0x1)
+	if (regval & WakePhy)
 		*cur |= WAKE_PHY;
-	if (regval & 0x2)
+	if (regval & WakeUnicast)
 		*cur |= WAKE_UCAST;
-	if (regval & 0x4)
+	if (regval & WakeMulticast)
 		*cur |= WAKE_MCAST;
-	if (regval & 0x8)
+	if (regval & WakeBroadcast)
 		*cur |= WAKE_BCAST;
-	if (regval & 0x10)
+	if (regval & WakeArp)
 		*cur |= WAKE_ARP;
-	if (regval & 0x200)
+	if (regval & WakeMagic)
 		*cur |= WAKE_MAGIC;
-	if (regval & 0x400)
+	if (regval & WakeMagicSecure) {
+		/* this can be on in revC, but it's broken */
 		*cur |= WAKE_MAGICSECURE;
+	}
 
 	return 0;
 }
 
 static int netdev_set_sopass(struct net_device *dev, u8 *newval)
 {
+	struct netdev_private *np = dev->priv;
 	u16 *sval = (u16 *)newval;
-	u32 addr = readl(dev->base_addr + RxFilterAddr) & ~0x3ff;
+	u32 addr;
+	
+	if (np->srr < SRR_REV_D) {
+		return 0;
+	}
 
 	/* enable writing to these registers by disabling the RX filter */
-	addr &= ~0x80000000;
+	addr = readl(dev->base_addr + RxFilterAddr) & ~RFCRAddressMask;
+	addr &= ~RxFilterEnable;
 	writel(addr, dev->base_addr + RxFilterAddr);
 
 	/* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
@@ -1621,19 +1850,25 @@
 	writew(sval[2], dev->base_addr + RxFilterData);
 	
 	/* re-enable the RX filter */
-	writel(addr | 0x80000000, dev->base_addr + RxFilterAddr);
-
-	/* should we burn this into the EEPROM? */
+	writel(addr | RxFilterEnable, dev->base_addr + RxFilterAddr);
 
 	return 0;
 }
 
 static int netdev_get_sopass(struct net_device *dev, u8 *data)
 {
+	struct netdev_private *np = dev->priv;
 	u16 *sval = (u16 *)data;
-	u32 addr = readl(dev->base_addr + RxFilterAddr) & ~0x3ff;
+	u32 addr;
+
+	if (np->srr < SRR_REV_D) {
+		sval[0] = sval[1] = sval[2] = 0;
+		return 0;
+	}
 
 	/* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
+	addr = readl(dev->base_addr + RxFilterAddr) & ~RFCRAddressMask;
+
 	writel(addr | 0xa, dev->base_addr + RxFilterAddr);
 	sval[0] = readw(dev->base_addr + RxFilterData);
 
@@ -1643,6 +1878,8 @@
 	writel(addr | 0xe, dev->base_addr + RxFilterAddr);
 	sval[2] = readw(dev->base_addr + RxFilterData);
 	
+	writel(addr, dev->base_addr + RxFilterAddr);
+
 	return 0;
 }
 
@@ -1662,17 +1899,17 @@
 	ecmd->transceiver = XCVR_INTERNAL;
 
 	/* this isn't fully supported at higher layers */
-	ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & 0xf;
+	ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & PhyAddrMask;
 
-	tmp = readl(dev->base_addr + AnegAdv);
 	ecmd->advertising = ADVERTISED_TP;
-	if (tmp & Aneg10BaseT)
+	tmp = mdio_read(dev, 1, MII_ADVERTISE);
+	if (tmp & ADVERTISE_10HALF)
 		ecmd->advertising |= ADVERTISED_10baseT_Half;
-	if (tmp & Aneg10BaseTFull)
+	if (tmp & ADVERTISE_10FULL)
 		ecmd->advertising |= ADVERTISED_10baseT_Full;
-	if (tmp & Aneg100BaseT)
+	if (tmp & ADVERTISE_100HALF)
 		ecmd->advertising |= ADVERTISED_100baseT_Half;
-	if (tmp & Aneg100BaseTFull)
+	if (tmp & ADVERTISE_100FULL)
 		ecmd->advertising |= ADVERTISED_100baseT_Full;
 
 	tmp = readl(dev->base_addr + ChipConfig);
@@ -1734,30 +1971,29 @@
 		}
 		writel(tmp, dev->base_addr + ChipConfig);
 		/* turn on autonegotiation, and force a renegotiate */
-		tmp = readl(dev->base_addr + BasicControl);
-		tmp |= BMCRAnegEnable | BMCRAnegRestart;
-		writel(tmp, dev->base_addr + BasicControl);
-		np->advertising = mdio_read(dev, 1, 4);
+		tmp = mdio_read(dev, 1, MII_BMCR);
+		tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
+		mdio_write(dev, 1, MII_BMCR, tmp);
+		np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
 	} else {
 		/* turn off auto negotiation, set speed and duplexity */
-		tmp = readl(dev->base_addr + BasicControl);
-		tmp &= ~(BMCRAnegEnable | BMCRSpeed | BMCRDuplex);
+		tmp = mdio_read(dev, 1, MII_BMCR);
+		tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
 		if (ecmd->speed == SPEED_100) {
-			tmp |= BMCRSpeed;
+			tmp |= BMCR_SPEED100;
 		}
 		if (ecmd->duplex == DUPLEX_FULL) {
-			tmp |= BMCRDuplex;
+			tmp |= BMCR_FULLDPLX;
 		} else {
 			np->full_duplex = 0;
 		}
-		writel(tmp, dev->base_addr + BasicControl);
+		mdio_write(dev, 1, MII_BMCR, tmp);
 	}
 	return 0;
 }
 
 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) {
@@ -1770,22 +2006,16 @@
 
 	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);
+		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;
-		if (data->phy_id == 1) {
-			u16 miireg = data->reg_num & 0x1f;
-			u16 value = data->val_in;
-			writew(value, dev->base_addr + BasicControl 
-					+ (miireg << 2));
-			switch (miireg) {
-			case 4: np->advertising = value; break;
-			}
-		}
+		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, 
+			data->val_in);
 		return 0;
 	default:
 		return -EOPNOTSUPP;
@@ -1795,16 +2025,24 @@
 static void enable_wol_mode(struct net_device *dev, int enable_intr)
 {
 	long ioaddr = dev->base_addr;
+	struct netdev_private *np = dev->priv;
 
 	if (debug > 1)
 		printk(KERN_INFO "%s: remaining active for wake-on-lan\n", 
 			dev->name);
+
 	/* For WOL we must restart the rx process in silent mode.
 	 * Write NULL to the RxRingPtr. Only possible if
 	 * rx process is stopped
 	 */
 	writel(0, ioaddr + RxRingPtr);
 
+	/* read WoL status to clear */
+	readl(ioaddr + WOLCmd);
+
+	/* PME on, clear status */
+	writel(np->SavedClkRun | PMEEnable | PMEStatus, ioaddr + ClkRun);
+
 	/* and restart the rx process */
 	writel(RxOn, ioaddr + ChipCmd);
 
@@ -1822,9 +2060,10 @@
 	struct netdev_private *np = dev->priv;
 
 	netif_stop_queue(dev);
+	netif_carrier_off(dev);
 
 	if (debug > 1) {
- 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
+		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
 			   dev->name, (int)readl(ioaddr + ChipCmd));
 		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
 			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
@@ -1835,9 +2074,13 @@
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
 
+	/* Disable and clear interrupts */
 	writel(0, ioaddr + IntrEnable);
-	writel(0, ioaddr + IntrMask);
-	writel(2, ioaddr + StatsCtrl); 	/* Freeze Stats */
+	readl(ioaddr + IntrMask);
+	readw(ioaddr + MIntrStatus);
+
+ 	/* Freeze Stats */
+	writel(StatsFreeze, ioaddr + StatsCtrl);
 	    
 	/* Stop the chip's Tx and Rx processes. */
 	natsemi_stop_rxtx(dev);
@@ -1865,20 +2108,15 @@
 
 	 {
 		u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
-		u32 clkrun = np->SavedClkRun;
-		/* Restore PME enable bit */
 		if (wol) {
 			/* restart the NIC in WOL mode.
 			 * The nic must be stopped for this.
 			 */
 			enable_wol_mode(dev, 0);
-			/* make sure to enable PME */
-			clkrun |= 0x100;
+		} else {
+			/* Restore PME enable bit unmolested */
+			writel(np->SavedClkRun, ioaddr + ClkRun);
 		}
-		writel(clkrun, ioaddr + ClkRun);
-#if 0
-		writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */
-#endif
 	}
 	return 0;
 }
@@ -1913,8 +2151,8 @@
  *	* intr_handler: doesn't acquire the spinlock. suspend calls
  *		disable_irq() to enforce synchronization.
  *
- * netif_device_detach must occur under spin_unlock_irq(), interrupts from a detached
- * device would cause an irq storm.
+ * netif_device_detach must occur under spin_unlock_irq(), interrupts from a
+ * detached device would cause an irq storm.
  */
 
 static int natsemi_suspend (struct pci_dev *pdev, u32 state)
@@ -1945,7 +2183,6 @@
 		drain_ring(dev);
 		{
 			u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
-			u32 clkrun = np->SavedClkRun;
 			/* Restore PME enable bit */
 			if (wol) {
 				/* restart the NIC in WOL mode.
@@ -1953,10 +2190,10 @@
 				 * FIXME: use the WOL interupt 
 				 */
 				enable_wol_mode(dev, 0);
-				/* make sure to enable PME */
-				clkrun |= 0x100;
+			} else {
+				/* Restore PME enable bit unmolested */
+				writel(np->SavedClkRun, ioaddr + ClkRun);
 			}
-			writel(clkrun, ioaddr + ClkRun);
 		}
 	} else {
 		netif_device_detach(dev);
@@ -1985,8 +2222,7 @@
 		netif_device_attach(dev);
 		spin_unlock_irq(&np->lock);
 
-		np->timer.expires = jiffies + 1*HZ;
-		add_timer(&np->timer);
+		mod_timer(&np->timer, jiffies + 1*HZ);
 	} else {
 		netif_device_attach(dev);
 	}

             reply	other threads:[~2001-10-20  1:09 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-10-20  1:05 Tim Hockin [this message]
2001-10-20  1:12 ` [PATCH] try #2 even bigger natsemi patch Tim Hockin
2001-10-20 13:17 ` Manfred Spraul

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=3BD0CDED.3850B31D@sun.com \
    --to=thockin@sun.com \
    --cc=jgarzik@mandrakesoft.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=manfred@colorfullife.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.