patch-2.4.14 linux/drivers/net/natsemi.c

Next file: linux/drivers/net/pcmcia/xircom_tulip_cb.c
Previous file: linux/drivers/net/irda/sa1100_ir.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.13/linux/drivers/net/natsemi.c linux/drivers/net/natsemi.c
@@ -85,6 +85,15 @@
 		* 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)
+		* Reformat register offsets/bits (jgarzik)
 
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
@@ -93,9 +102,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 +114,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 +122,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 +153,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,7 +190,7 @@
 #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>
@@ -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);
@@ -323,88 +334,222 @@
    device.
 */
 enum register_offsets {
-	ChipCmd=0x00, ChipConfig=0x04, EECtrl=0x08, PCIBusCfg=0x0C,
-	IntrStatus=0x10, IntrMask=0x14, IntrEnable=0x18,
-	TxRingPtr=0x20, TxConfig=0x24,
-	RxRingPtr=0x30, RxConfig=0x34, ClkRun=0x3C,
-	WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C,
-	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, 
-	MIntrStatus=0xC8, PhyCtrl=0xE4,
+	ChipCmd			= 0x00,
+	ChipConfig		= 0x04,
+	EECtrl			= 0x08,
+	PCIBusCfg		= 0x0C,
+	IntrStatus		= 0x10,
+	IntrMask		= 0x14,
+	IntrEnable		= 0x18,
+	TxRingPtr		= 0x20,
+	TxConfig		= 0x24,
+	RxRingPtr		= 0x30,
+	RxConfig		= 0x34,
+	ClkRun			= 0x3C,
+	WOLCmd			= 0x40,
+	PauseCmd		= 0x44,
+	RxFilterAddr		= 0x48,
+	RxFilterData		= 0x4C,
+	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,
+	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 {
-	PCIPM=0x44,
+enum pci_register_offsets {
+	PCIPM			= 0x44,
+};
+
+enum ChipCmd_bits {
+	ChipReset		= 0x100,
+	RxReset			= 0x20,
+	TxReset			= 0x10,
+	RxOff			= 0x08,
+	RxOn			= 0x04,
+	TxOff			= 0x02,
+	TxOn			= 0x01,
+};
+
+enum ChipConfig_bits {
+	CfgPhyDis		= 0x200,
+	CfgPhyRst		= 0x400,
+	CfgAnegEnable		= 0x2000,
+	CfgAneg100		= 0x4000,
+	CfgAnegFull		= 0x8000,
+	CfgAnegDone		= 0x8000000,
+	CfgFullDuplex		= 0x20000000,
+	CfgSpeed100		= 0x40000000,
+	CfgLink			= 0x80000000,
 };
 
-/* Bit in ChipCmd. */
-enum ChipCmdBits {
-	ChipReset=0x100, RxReset=0x20, TxReset=0x10, RxOff=0x08, RxOn=0x04,
-	TxOff=0x02, TxOn=0x01,
+enum EECtrl_bits {
+	EE_ShiftClk		= 0x04,
+	EE_DataIn		= 0x01,
+	EE_ChipSelect		= 0x08,
+	EE_DataOut		= 0x02,
 };
 
-enum PCIBusCfgBits {
-	EepromReload=0x4,
+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,
-	IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100,
-	IntrTxIdle=0x0200, IntrTxUnderrun=0x0400,
-	StatsMax=0x0800, LinkChange=0x4000,
-	WOLPkt=0x2000,
-	RxResetDone=0x1000000, TxResetDone=0x2000000,
-	IntrPCIErr=0x00f00000,
-	IntrNormalSummary=0x025f, IntrAbnormalSummary=0xCD20,
+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,
+	SWInt			= 0x1000,
+	WOLPkt			= 0x2000,
+	LinkChange		= 0x4000,
+	IntrHighBits		= 0x8000,
+	RxStatusFIFOOver	= 0x10000,
+	IntrPCIErr		= 0xf00000,
+	RxResetDone		= 0x1000000,
+	TxResetDone		= 0x2000000,
+	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,
-};
-
-/* Bits in WOLCmd register. */
-enum wol_bits {
-	WakePhy=0x1, WakeUnicast=0x2, WakeMulticast=0x4, WakeBroadcast=0x8,
-	WakeArp=0x10, WakePMatch0=0x20, WakePMatch1=0x40, WakePMatch2=0x80,
-	WakePMatch3=0x100, WakeMagic=0x200, WakeMagicSecure=0x400, 
-	SecureHack=0x100000, WokePhy=0x400000, WokeUnicast=0x800000, 
-	WokeMulticast=0x1000000, WokeBroadcast=0x2000000, WokeArp=0x4000000,
-	WokePMatch0=0x8000000, WokePMatch1=0x10000000, WokePMatch2=0x20000000,
-	WokePMatch3=0x40000000, WokeMagic=0x80000000, WakeOptsSummary=0x7ff
-};
-
-enum aneg_bits {
-	Aneg10BaseT=0x20, Aneg10BaseTFull=0x40, 
-	Aneg100BaseT=0x80, Aneg100BaseTFull=0x100,
-};
-
-enum config_bits {
-	CfgPhyDis=0x200, CfgPhyRst=0x400, CfgAnegEnable=0x2000,
-	CfgAneg100=0x4000, CfgAnegFull=0x8000, CfgAnegDone=0x8000000,
-	CfgFullDuplex=0x20000000,
-	CfgSpeed100=0x40000000, CfgLink=0x80000000,
-};
-
-enum bmcr_bits {
-	BMCRDuplex=0x100, BMCRAnegRestart=0x200, BMCRAnegEnable=0x1000,
-	BMCRSpeed=0x2000, BMCRPhyReset=0x8000,
+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
+};
+
+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,
+	SecureHack		= 0x100000,
+	WokePhy			= 0x400000,
+	WokeUnicast		= 0x800000,
+	WokeMulticast		= 0x1000000,
+	WokeBroadcast		= 0x2000000,
+	WokeArp			= 0x4000000,
+	WokePMatch0		= 0x8000000,
+	WokePMatch1		= 0x10000000,
+	WokePMatch2		= 0x20000000,
+	WokePMatch3		= 0x40000000,
+	WokeMagic		= 0x80000000,
+	WakeOptsSummary		= 0x7ff
 };
 
+enum RxFilterAddr_bits {
+	RFCRAddressMask		= 0x3ff,
+	AcceptMulticast		= 0x00200000,
+	AcceptMyPhys		= 0x08000000,
+	AcceptAllPhys		= 0x10000000,
+	AcceptAllMulticast	= 0x20000000,
+	AcceptBroadcast		= 0x40000000,
+	RxFilterEnable		= 0x80000000
+};
+
+enum StatsCtrl_bits {
+	StatsWarn		= 0x1,
+	StatsFreeze		= 0x2,
+	StatsClear		= 0x4,
+	StatsStrobe		= 0x8,
+};
+
+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 +563,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 +606,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 +629,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 +646,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);
 
 
 static int __devinit natsemi_probe1 (struct pci_dev *pdev,
@@ -516,9 +676,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 +731,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 +778,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 +811,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 +856,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 +931,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 +1025,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 +1040,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 +1056,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 +1082,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 +1105,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 +1140,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 +1156,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, 
@@ -932,11 +1195,32 @@
 		printk(KERN_DEBUG "%s: Media selection timer tick.\n",
 			   dev->name);
 	}
-	spin_lock_irq(&np->lock);
-	check_link(dev);
-	spin_unlock_irq(&np->lock);
-	np->timer.expires = jiffies + next_tick;
-	add_timer(&np->timer);
+
+	/* 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);
+			disable_irq(dev->irq);
+			spin_lock_irq(&np->lock);
+			init_registers(dev);
+			spin_unlock_irq(&np->lock);
+			enable_irq(dev->irq);
+		} else {
+			/* hurry back */
+			next_tick = HZ;
+		}
+	} else {
+		/* init_registers() calls check_link() for the above case */
+		spin_lock_irq(&np->lock);
+		check_link(dev);
+		spin_unlock_irq(&np->lock);
+	}
+	mod_timer(&np->timer, jiffies + next_tick);
 }
 
 static void dump_ring(struct net_device *dev)
@@ -946,15 +1230,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 +1251,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 +1264,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 +1307,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 +1395,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 +1437,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 +1512,7 @@
 		}
 	} while (1);
 
-	if (debug > 3)
+	if (debug > 4)
 		printk(KERN_DEBUG "%s: exiting interrupt.\n",
 			   dev->name);
 }
@@ -1240,7 +1533,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 +1545,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 +1622,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 +1635,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 +1647,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 +1755,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 +1770,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 +1855,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 +1871,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 +1943,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 +1971,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 +1992,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 +2064,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 +2099,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 +2118,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 +2153,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 +2167,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 +2201,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 +2244,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 +2276,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 +2283,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 +2315,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);
 	}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)