Skip to contentSkip to conversation

[PATCH} NatSemi ethernet - largish

DISCUSSION Tim Hockin 1 participant 1 message Aug 14, 2001 1 message not in the archive

Subsystems (3)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

Tim Hockin

author
#1 lore raw

Hey gang, not be a pest but...

Another release kernel has come and gone with a severely broken NatSemi driver. Attached here is the same patch I sent you guys before (change listing below). The first two listings combined are this patch, the third is the ethtool patch (also attached).

It would be super if we could push these changes into the mainline kernel ASAP - I know the magic registers thing is causing problems...

Quoted 24 lines

1) As discussed with Andrew and Jeff: a generic wake-on-lan API for ethtool. A simple struct, 2 ETHTOOL sub-ioctls, and some bitmasks.

2) A largish patch against natsemi.c implementing several things.
* increase the HW timeout to be long enough for an EEPROM reload
* add several register names
* add several bit-names for registers
* remove the wake-on-lan interrupt from the default "on" list
* call netif_carrier_off if we are not currently opened
* force an EEPROM reload on a chip reset - some systems have trouble loading the EEPROM initially due to the dp83815 starting at voltage < 3.0v
* ALWAYS set the magic registers
* decrease maxdma to 256 - makes "Something Wicked" stop happening on ServerWorks.
* Add support for ETHTOOL_GSET and ETHTOOL_SSET
* Add support for new wake-on-lan ethtools (ETHTOOL_GWOL, ETHTOOL_SWOL)
* only halt the chip if wake-on-lan is not enabled

3) A patch against ethtool-1.2 implementing wake-on-lan support
* bump version to 1.3
* compile with -Wall
* updated man page
* added cmdline opts for wol and sopass
* minor restructuring to make it easier

Signature · 4 lines
Tim Hockin
Systems Software Engineer
Sun Microsystems, Cobalt Server Appliances
[email protected]
drivers/net/natsemi.c +373 -44
diff -ruN dist+patches-2.4.8/drivers/net/natsemi.c cobalt-2.4.8/drivers/net/natsemi.c
@@ -60,16 +60,20 @@
 			 from Myrio Corporation, Greg Smith]
 		* suspend/resume
 
+	version 1.0.8 (Tim Hockin <[email protected]>)
+		* ETHTOOL_* support
+		* Wake on lan support (Erik Gilling)
+		* MXDMA fixes for serverworks
+		* EEPROM reload
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
 	* support for an external PHY
 	* flow control
-	* Wake-On-LAN
 */
 
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.7"
-#define DRV_RELDATE	"May 18, 2001"
+#define DRV_VERSION	"1.07+LK1.0.8"
+#define DRV_RELDATE	"Aug 07, 2001"
 
 
 /* Updated to recommendations in pci-skeleton v2.03. */
@@ -125,7 +129,7 @@
 /* Time in jiffies before concluding the transmitter is hung. */
 #define TX_TIMEOUT  (2*HZ)
 
-#define NATSEMI_HW_TIMEOUT	200
+#define NATSEMI_HW_TIMEOUT	400
 
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
@@ -302,18 +306,30 @@
 	WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C,
 	BootRomAddr=0x50, BootRomData=0x54, SiliconRev=0x58, StatsCtrl=0x5C,
 	StatsData=0x60, RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64,
-	PCIPM=0x44, PhyStatus=0xC0, MIntrCtrl=0xC4, MIntrStatus=0xC8,
+	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. */
+	/* 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
 };
 
+/* misc PCI space registers */
+enum PCISpaceRegs {
+	PCIPM=0x44,
+};
+
 /* Bit in ChipCmd. */
 enum ChipCmdBits {
 	ChipReset=0x100, RxReset=0x20, TxReset=0x10, RxOff=0x08, RxOn=0x04,
 	TxOff=0x02, TxOn=0x01,
 };
 
+enum PCIBusCfgBits {
+	EepromReload=0x4,
+};
+
 /* Bits in the interrupt status/mask registers. */
 enum intr_status_bits {
 	IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008,
@@ -324,7 +340,7 @@
 	WOLPkt=0x2000,
 	RxResetDone=0x1000000, TxResetDone=0x2000000,
 	IntrPCIErr=0x00f00000,
-	IntrNormalSummary=0x0251, IntrAbnormalSummary=0xED20,
+	IntrNormalSummary=0x025f, IntrAbnormalSummary=0xCD20,
 };
 
 /* Bits in the RxMode register. */
@@ -335,6 +351,34 @@
 	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,
+};
+
 /* The Rx and Tx buffer descriptors. */
 /* Note that using only 32 bit fields simplifies conversion to big-endian
    architectures. */
@@ -408,6 +452,12 @@
 static void __get_stats(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_set_wol(struct net_device *dev, u32 newval);
+static int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);
+static int netdev_set_sopass(struct net_device *dev, u8 *newval);
+static int netdev_get_sopass(struct net_device *dev, u8 *data);
+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 int  netdev_close(struct net_device *dev);
 
 
@@ -530,6 +580,7 @@
 		pci_set_drvdata(pdev, NULL);
 		return i;
 	}
+	netif_carrier_off(dev);
 
 	printk(KERN_INFO "%s: %s at 0x%lx, ",
 		   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
@@ -548,7 +599,8 @@
 			   chip_config & 0x8000 ? "full" : "half");
 	}
 	printk(KERN_INFO "%s: Transceiver status 0x%4.4x advertising %4.4x.\n",
-		   dev->name, (int)readl(ioaddr + 0x84), np->advertising);
+		   dev->name, (int)readl(ioaddr + BasicStatus), 
+		   np->advertising);
 
 	return 0;
 }
@@ -617,7 +669,7 @@
 static int mdio_read(struct net_device *dev, int phy_id, int location)
 {
 	if (phy_id == 1 && location < 32)
-		return readl(dev->base_addr + 0x80 + (location<<2)) & 0xffff;
+		return readl(dev->base_addr+BasicControl+(location<<2))&0xffff;
 	else
 		return 0xffff;
 }
@@ -639,6 +691,20 @@
 		printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
 		   dev->name, i*5);
 	}
+
+	writel(EepromReload, dev->base_addr + PCIBusCfg);
+	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+		if (!(readl(dev->base_addr + PCIBusCfg) & EepromReload))
+			break;
+		udelay(5);
+	}
+	if (i==NATSEMI_HW_TIMEOUT && debug) {
+		printk(KERN_INFO "%s: EEPROM did not reload in %d usec.\n",
+		   dev->name, i*5);
+	} else if (debug > 2) {
+		printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n",
+		   dev->name, i*5);
+	}
 }
 
 
@@ -733,23 +799,20 @@
 	if (debug > 4)
 		printk(KERN_DEBUG "%s: found silicon revision %xh.\n",
 				dev->name, readl(ioaddr + SiliconRev));
+
 	/* On page 78 of the spec, they recommend some settings for "optimum
 	   performance" to be done in sequence.  These settings optimize some
-	   of the 100Mbit autodetection circuitry.  Also, we only want to do
-	   this for rev C of the chip.
-
-	   There seems to be a typo on page 78, but there isn't.  The fixup 
-	   should be performed for "DP83815CVNG (SRR = 203h)", which is a 
-	   pretty old rev.  This is not to be confused with 302h, which is 
-	   current.  Confirmed with engineers at NSC.
+	   of the 100Mbit autodetection circuitry.  They say we only want to 
+	   do this for rev C of the chip, but engineers at NSC (Bradley 
+	   Kennedy) recommends always setting them.  If you don't, you get 
+	   errors on some autonegotiations that make the device unusable.
 	*/
-	if (readl(ioaddr + SiliconRev) == 0x203) {
-		writew(0x0001, ioaddr + PGSEL);
-		writew(0x189C, ioaddr + PMDCSR);
-		writew(0x0000, ioaddr + TSTDAT);
-		writew(0x5040, ioaddr + DSPCFG);
-		writew(0x008C, ioaddr + SDCFG);
-	}
+	writew(0x0001, ioaddr + PGSEL);
+	writew(0x189C, ioaddr + PMDCSR);
+	writew(0x0000, ioaddr + TSTDAT);
+	writew(0x5040, ioaddr + DSPCFG);
+	writew(0x008C, ioaddr + SDCFG);
+	writew(0x0000, ioaddr + PGSEL);
 
 	/* Enable PHY Specific event based interrupts.  Link state change
 	   and Auto-Negotiation Completion are among the affected.
@@ -774,16 +837,16 @@
 
 	/* DRTH: 2: start tx if 64 bytes are in the fifo
 	 * FLTH: 0x10: refill with next packet if 512 bytes are free
-	 * MXDMA: 0: up to 512 byte bursts.
+	 * MXDMA: 0: up to 256 byte bursts.
 	 * 	MXDMA must be <= FLTH
 	 * ECRETRY=1
 	 * ATP=1
 	 */
-	np->tx_config = 0x10801002;
+	np->tx_config = 0x10f01002;
 	/* DRTH 0x10: start copying to memory if 128 bytes are in the fifo
-	 * MXDMA 0: up to 512 byte bursts
+	 * MXDMA 0: up to 256 byte bursts
 	 */
-	np->rx_config = 0x0020;
+	np->rx_config = 0x700020;
 	writel(np->tx_config, ioaddr + TxConfig);
 	writel(np->rx_config, ioaddr + RxConfig);
 
@@ -800,11 +863,11 @@
 	__set_rx_mode(dev);
 
 	/* Enable interrupts by setting the interrupt mask. */
-	writel(IntrNormalSummary | IntrAbnormalSummary | 0x1f, ioaddr + IntrMask);
+	writel(IntrNormalSummary | IntrAbnormalSummary, ioaddr + IntrMask);
 	writel(1, ioaddr + IntrEnable);
 
 	writel(RxOn | TxOn, ioaddr + ChipCmd);
-	writel(4, ioaddr + StatsCtrl); 					/* Clear Stats */
+	writel(4, ioaddr + StatsCtrl); /* Clear Stats */
 }
 
 static void netdev_timer(unsigned long data)
@@ -1190,7 +1253,8 @@
 	if (intr_status & LinkChange) {
 		printk(KERN_NOTICE "%s: Link changed: Autonegotiation advertising"
 			   " %4.4x  partner %4.4x.\n", dev->name,
-			   (int)readl(ioaddr + 0x90), (int)readl(ioaddr + 0x94));
+			   (int)readl(ioaddr + AnegAdv), 
+			   (int)readl(ioaddr + AnegPeer));
 		/* read MII int status to clear the flag */
 		readw(ioaddr + MIntrStatus);
 		check_link(dev);
@@ -1208,7 +1272,7 @@
 	}
 	if (intr_status & WOLPkt) {
 		int wol_status = readl(ioaddr + WOLCmd);
-		printk(KERN_NOTICE "%s: Link wake-up event %8.8x",
+		printk(KERN_NOTICE "%s: Link wake-up event %8.8x\n",
 			   dev->name, wol_status);
 	}
 	if ((intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone|0xA7ff))
@@ -1230,7 +1294,7 @@
 
 	/* The chip only need report frame silently dropped. */
 	np->stats.rx_crc_errors	+= readl(ioaddr + RxCRCErrs);
-	np->stats.rx_missed_errors	+= readl(ioaddr + RxMissed);
+	np->stats.rx_missed_errors += readl(ioaddr + RxMissed);
 }
 
 static struct net_device_stats *get_stats(struct net_device *dev)
@@ -1349,12 +1413,12 @@
 static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
 {
 	struct netdev_private *np = dev->priv;
-	u32 ethcmd;
+	struct ethtool_cmd ecmd;
 		
-	if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+	if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
 		return -EFAULT;
 
-        switch (ethcmd) {
+        switch (ecmd.cmd) {
         case ETHTOOL_GDRVINFO: {
 		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
 		strcpy(info.driver, DRV_NAME);
@@ -1364,12 +1428,257 @@
 			return -EFAULT;
 		return 0;
 	}
+	case ETHTOOL_GSET: {
+		spin_lock_irq(&np->lock);
+		netdev_get_ecmd(dev, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SSET: {
+		int r;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = netdev_set_ecmd(dev, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	case ETHTOOL_GWOL: {
+		struct ethtool_wolinfo wol = {ETHTOOL_GWOL};
+		spin_lock_irq(&np->lock);
+		netdev_get_wol(dev, &wol.supported, &wol.wolopts);
+		netdev_get_sopass(dev, wol.sopass);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &wol, sizeof(wol)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SWOL: {
+		struct ethtool_wolinfo wol;
+		int r;
+		if (copy_from_user(&wol, useraddr, sizeof(wol)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		netdev_set_wol(dev, wol.wolopts);
+		r = netdev_set_sopass(dev, wol.sopass);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
 
         }
 	
 	return -EOPNOTSUPP;
 }
 
+static int netdev_set_wol(struct net_device *dev, u32 newval)
+{
+	u32 data = readl(dev->base_addr + WOLCmd) & ~WakeOptsSummary;
+
+	/* translate to bitmasks this chip understands */
+	if (newval & WAKE_PHY)
+		data |= WakePhy;
+	if (newval & WAKE_UCAST)
+		data |= WakeUnicast;
+	if (newval & WAKE_MCAST)
+		data |= WakeMulticast;
+	if (newval & WAKE_BCAST)
+		data |= WakeBroadcast;
+	if (newval & WAKE_ARP)
+		data |= WakeArp;
+	if (newval & WAKE_MAGIC)
+		data |= WakeMagic;
+	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)
+{
+	u32 regval = readl(dev->base_addr + WOLCmd);
+
+	*supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST 
+			| WAKE_ARP | WAKE_MAGIC | WAKE_MAGICSECURE);
+	*cur = 0;
+	/* translate from chip bitmasks */
+	if (regval & 0x1)
+		*cur |= WAKE_PHY;
+	if (regval & 0x2)
+		*cur |= WAKE_UCAST;
+	if (regval & 0x4)
+		*cur |= WAKE_MCAST;
+	if (regval & 0x8)
+		*cur |= WAKE_BCAST;
+	if (regval & 0x10)
+		*cur |= WAKE_ARP;
+	if (regval & 0x200)
+		*cur |= WAKE_MAGIC;
+	if (regval & 0x400)
+		*cur |= WAKE_MAGICSECURE;
+
+	return 0;
+}
+
+static int netdev_set_sopass(struct net_device *dev, u8 *newval)
+{
+	u16 *sval = (u16 *)newval;
+	u32 addr = readl(dev->base_addr + RxFilterAddr) & ~0x3ff;
+
+	/* enable writing to these registers by disabling the RX filter */
+	addr &= ~0x80000000;
+	writel(addr, dev->base_addr + RxFilterAddr);
+
+	/* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
+	writel(addr | 0xa, dev->base_addr + RxFilterAddr);
+	writew(sval[0], dev->base_addr + RxFilterData);
+
+	writel(addr | 0xc, dev->base_addr + RxFilterAddr);
+	writew(sval[1], dev->base_addr + RxFilterData);
+	
+	writel(addr | 0xe, dev->base_addr + RxFilterAddr);
+	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? */
+
+	return 0;
+}
+
+static int netdev_get_sopass(struct net_device *dev, u8 *data)
+{
+	u16 *sval = (u16 *)data;
+	u32 addr = readl(dev->base_addr + RxFilterAddr) & ~0x3ff;
+
+	/* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
+	writel(addr | 0xa, dev->base_addr + RxFilterAddr);
+	sval[0] = readw(dev->base_addr + RxFilterData);
+
+	writel(addr | 0xc, dev->base_addr + RxFilterAddr);
+	sval[1] = readw(dev->base_addr + RxFilterData);
+	
+	writel(addr | 0xe, dev->base_addr + RxFilterAddr);
+	sval[2] = readw(dev->base_addr + RxFilterData);
+	
+	return 0;
+}
+
+static int netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	u32 tmp;
+
+	ecmd->supported = 
+		(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+		SUPPORTED_Autoneg | SUPPORTED_TP);
+	
+	/* only supports twisted-pair */
+	ecmd->port = PORT_TP;
+
+	/* only supports internal transceiver */
+	ecmd->transceiver = XCVR_INTERNAL;
+
+	/* this isn't fully supported at higher layers */
+	ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & 0xf;
+
+	tmp = readl(dev->base_addr + AnegAdv);
+	ecmd->advertising = ADVERTISED_TP;
+	if (tmp & Aneg10BaseT)
+		ecmd->advertising |= ADVERTISED_10baseT_Half;
+	if (tmp & Aneg10BaseTFull)
+		ecmd->advertising |= ADVERTISED_10baseT_Full;
+	if (tmp & Aneg100BaseT)
+		ecmd->advertising |= ADVERTISED_100baseT_Half;
+	if (tmp & Aneg100BaseTFull)
+		ecmd->advertising |= ADVERTISED_100baseT_Full;
+
+	tmp = readl(dev->base_addr + ChipConfig);
+	if (tmp & CfgAnegEnable) {
+		ecmd->advertising |= ADVERTISED_Autoneg;
+		ecmd->autoneg = AUTONEG_ENABLE;
+	} else {
+		ecmd->autoneg = AUTONEG_DISABLE;
+	}
+
+	if (tmp & CfgSpeed100) {
+		ecmd->speed = SPEED_100;
+	} else {
+		ecmd->speed = SPEED_10;
+	}
+
+	if (tmp & CfgFullDuplex) {
+		ecmd->duplex = DUPLEX_FULL;
+	} else {
+		ecmd->duplex = DUPLEX_HALF;
+	}
+
+	/* ignore maxtxpkt, maxrxpkt for now */
+
+	return 0;
+}
+
+static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct netdev_private *np = dev->priv;
+	u32 tmp;
+
+	if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+		return -EINVAL;
+	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+		return -EINVAL;
+	if (ecmd->port != PORT_TP)
+		return -EINVAL;
+	if (ecmd->transceiver != XCVR_INTERNAL)
+		return -EINVAL;
+	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
+		return -EINVAL;
+	/* ignore phy_address, maxtxpkt, maxrxpkt for now */
+	
+	/* WHEW! now lets bang some bits */
+	
+	if (ecmd->autoneg == AUTONEG_ENABLE) {
+		/* advertise only what has been requested */
+		tmp = readl(dev->base_addr + ChipConfig);
+		tmp &= ~(CfgAneg100 | CfgAnegFull);
+		tmp |= CfgAnegEnable;
+		if (ecmd->advertising & ADVERTISED_100baseT_Half 
+		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
+			tmp |= CfgAneg100;
+		}
+		if (ecmd->advertising & ADVERTISED_10baseT_Full 
+		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
+			tmp |= CfgAnegFull;
+		}
+		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);
+	} else {
+		/* turn off auto negotiation, set speed and duplexity */
+		tmp = readl(dev->base_addr + BasicControl);
+		tmp &= ~(BMCRAnegEnable | BMCRSpeed | BMCRDuplex);
+		if (ecmd->speed == SPEED_100) {
+			tmp |= BMCRSpeed;
+		}
+		if (ecmd->duplex == DUPLEX_FULL) {
+			tmp |= BMCRDuplex;
+		} else {
+			np->full_duplex = 0;
+		}
+		writel(tmp, dev->base_addr + BasicControl);
+	}
+	return 0;
+}
+
 static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct netdev_private *np = dev->priv;
@@ -1395,7 +1704,8 @@
 		if (data->phy_id == 1) {
 			u16 miireg = data->reg_num & 0x1f;
 			u16 value = data->val_in;
-			writew(value, dev->base_addr + 0x80 + (miireg << 2));
+			writew(value, dev->base_addr + BasicControl 
+					+ (miireg << 2));
 			switch (miireg) {
 			case 4: np->advertising = value; break;
 			}
@@ -1410,8 +1720,11 @@
 {
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
+	u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
+	u32 clkrun;
 
 	netif_stop_queue(dev);
+	netif_carrier_off(dev);
 
 	if (debug > 1) {
 		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.",
@@ -1420,14 +1733,23 @@
 			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
 	}
 
-	/* Disable interrupts using the mask. */
-	writel(0, ioaddr + IntrMask);
-	writel(0, ioaddr + IntrEnable);
-	writel(2, ioaddr + StatsCtrl); 					/* Freeze Stats */
-
-	/* Stop the chip's Tx and Rx processes. */
-	writel(RxOff | TxOff, ioaddr + ChipCmd);
-
+	/* Only shut down chip if wake on lan is not set */
+	if (!wol) {
+		/* Disable interrupts using the mask. */
+		writel(0, ioaddr + IntrMask);
+		writel(0, ioaddr + IntrEnable);
+		writel(2, ioaddr + StatsCtrl); 	/* Freeze Stats */
+	    
+		/* Stop the chip's Tx and Rx processes. */
+		writel(RxOff | TxOff, ioaddr + ChipCmd);
+	} else if (debug > 1) {
+		printk(KERN_INFO "%s: remaining active for wake-on-lan\n", 
+			dev->name);
+		/* spec says write 0 here */
+		writel(0, ioaddr + RxRingPtr);
+		/* allow wake-event interrupts now */
+		writel(readl(ioaddr + IntrMask) | WOLPkt, ioaddr + IntrMask);
+	}
 	del_timer_sync(&np->timer);
 
 #ifdef __i386__
@@ -1451,8 +1773,15 @@
 	drain_ring(dev);
 	free_ring(dev);
 
+	clkrun = np->SavedClkRun;
+	if (wol) {
+		/* make sure to enable PME */
+		clkrun |= 0x100;
+	}
+
 	/* Restore PME enable bit */
 	writel(np->SavedClkRun, ioaddr + ClkRun);
+	
 #if 0
 	writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */
 #endif
include/linux/ethtool.h +20 -0
diff -ruN dist+patches-2.4.8/include/linux/ethtool.h cobalt-2.4.8/include/linux/ethtool.h
@@ -38,11 +38,22 @@
 	u32	regdump_len;	/* Amount of data from ETHTOOL_GREGS */
 };
 
+#define SOPASS_MAX	6
+/* wake-on-lan settings */
+struct ethtool_wolinfo {
+	u32	cmd;
+	u32	supported;
+	u32	wolopts;
+	u8	sopass[SOPASS_MAX]; /* SecureOn(tm) password */
+};
+
 /* CMDs currently supported */
 #define ETHTOOL_GSET		0x00000001 /* Get settings. */
 #define ETHTOOL_SSET		0x00000002 /* Set settings, privileged. */
 #define ETHTOOL_GDRVINFO	0x00000003 /* Get driver info. */
 #define ETHTOOL_GREGS		0x00000004 /* Get NIC registers, privileged. */
+#define ETHTOOL_GWOL		0x00000005 /* Get wake-on-lan options. */
+#define ETHTOOL_SWOL		0x00000006 /* Set wake-on-lan options. */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
@@ -108,5 +119,14 @@
  */
 #define AUTONEG_DISABLE		0x00
 #define AUTONEG_ENABLE		0x01
+
+/* Wake-On-Lan options. */
+#define WAKE_PHY		(1 << 0)
+#define WAKE_UCAST		(1 << 1)
+#define WAKE_MCAST		(1 << 2)
+#define WAKE_BCAST		(1 << 3)
+#define WAKE_ARP		(1 << 4)
+#define WAKE_MAGIC		(1 << 5)
+#define WAKE_MAGICSECURE	(1 << 6) /* only meaningful if WAKE_MAGIC */
 
 #endif /* _LINUX_ETHTOOL_H */
AUTHORS +1 -0
diff -ruN ethtool-1.2/AUTHORS ethtool-1.3/AUTHORS
@@ -1,3 +1,4 @@
 David Miller <[email protected]>
 Jakub Jelinek <[email protected]>
 Jeff Garzik <[email protected]>
+Tim Hockin <[email protected]>
NEWS +3 -0
diff -ruN ethtool-1.2/NEWS ethtool-1.3/NEWS
@@ -4,3 +4,6 @@
 	  information from the ethernet driver associated
 	  with the specified interface.
 
+Version 1.3 - Aug 02, 2001
+
+	* Support Wake-on-LAN (ETHTOOL GWOL and ETHTOOL SWOL ioctl).
configure +2 -2
diff -ruN ethtool-1.2/configure ethtool-1.3/configure
@@ -691,7 +691,7 @@
 
 PACKAGE=ethtool
 
-VERSION=1.2
+VERSION=1.3
 
 if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then
   { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; }
@@ -987,7 +987,7 @@
   CFLAGS="$ac_save_CFLAGS"
 elif test $ac_cv_prog_cc_g = yes; then
   if test "$GCC" = yes; then
-    CFLAGS="-g -O2"
+    CFLAGS="-g -O2 -Wall"
   else
     CFLAGS="-g"
   fi
ethtool.8 +53 -5

82 lines not on this page · raw message

ethtool.c +283 -65

513 lines not on this page · raw message