Commit 6aaa2e5c authored by Roger Luethi's avatar Roger Luethi Committed by Jeff Garzik

[PATCH] Add WOL support

Add rhine_shutdown callback to prepare Rhine power registers for
shutdown.

Add rhine_get_wol/rhine_set_wol for ethtool ioctl.
Signed-off-by: default avatarRoger Luethi <rl@hellgate.ch>
parent 42094402
......@@ -394,8 +394,8 @@ enum register_offsets {
ConfigA=0x78, ConfigB=0x79, ConfigC=0x7A, ConfigD=0x7B,
RxMissed=0x7C, RxCRCErrs=0x7E, MiscCmd=0x81,
StickyHW=0x83, IntrStatus2=0x84,
WOLcrSet=0xA0, WOLcrClr=0xA4, WOLcrClr1=0xA6,
WOLcgClr=0xA7,
WOLcrSet=0xA0, PwcfgSet=0xA1, WOLcgSet=0xA3, WOLcrClr=0xA4,
WOLcrClr1=0xA6, WOLcgClr=0xA7,
PwrcsrSet=0xA8, PwrcsrSet1=0xA9, PwrcsrClr=0xAC, PwrcsrClr1=0xAD,
};
......@@ -427,6 +427,15 @@ enum intr_status_bits {
IntrTxErrSummary=0x082218,
};
/* Bits in WOLcrSet/WOLcrClr and PwrcsrSet/PwrcsrClr */
enum wol_bits {
WOLucast = 0x10,
WOLmagic = 0x20,
WOLbmcast = 0x30,
WOLlnkon = 0x40,
WOLlnkoff = 0x80,
};
/* The Rx and Tx buffer descriptors. */
struct rx_desc {
s32 rx_status;
......@@ -491,8 +500,8 @@ struct rhine_private {
unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */
unsigned int cur_tx, dirty_tx;
unsigned int rx_buf_sz; /* Based on MTU+slack. */
u8 wolopts;
/* These values are keep track of the transceiver/media in use. */
u8 tx_thresh, rx_thresh;
struct mii_if_info mii_if;
......@@ -512,6 +521,7 @@ static struct net_device_stats *rhine_get_stats(struct net_device *dev);
static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static struct ethtool_ops netdev_ethtool_ops;
static int rhine_close(struct net_device *dev);
static void rhine_shutdown (struct device *gdev);
#define RHINE_WAIT_FOR(condition) do { \
int i=1024; \
......@@ -537,12 +547,13 @@ static inline u32 get_intr_status(struct net_device *dev)
/*
* Get power related registers into sane state.
* Returns content of power-event (WOL) registers.
* Notify user about past WOL event.
*/
static void rhine_power_init(struct net_device *dev)
{
long ioaddr = dev->base_addr;
struct rhine_private *rp = netdev_priv(dev);
u16 wolstat;
if (rp->quirks & rqWOL) {
/* Make sure chip is in power state D0 */
......@@ -557,10 +568,40 @@ static void rhine_power_init(struct net_device *dev)
if (rp->quirks & rq6patterns)
writeb(0x03, ioaddr + WOLcrClr1);
/* Save power-event status bits */
wolstat = readb(ioaddr + PwrcsrSet);
if (rp->quirks & rq6patterns)
wolstat |= (readb(ioaddr + PwrcsrSet1) & 0x03) << 8;
/* Clear power-event status bits */
writeb(0xFF, ioaddr + PwrcsrClr);
if (rp->quirks & rq6patterns)
writeb(0x03, ioaddr + PwrcsrClr1);
if (wolstat) {
char *reason;
switch (wolstat) {
case WOLmagic:
reason = "Magic packet";
break;
case WOLlnkon:
reason = "Link went up";
break;
case WOLlnkoff:
reason = "Link went down";
break;
case WOLucast:
reason = "Unicast packet";
break;
case WOLbmcast:
reason = "Multicast/broadcast packet";
break;
default:
reason = "Unknown";
}
printk("%s: Woke system up. Reason: %s.\n",
DRV_NAME, reason);
}
}
}
......@@ -1766,6 +1807,39 @@ static void netdev_set_msglevel(struct net_device *dev, u32 value)
debug = value;
}
static void rhine_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rhine_private *rp = netdev_priv(dev);
if (!(rp->quirks & rqWOL))
return;
spin_lock_irq(&rp->lock);
wol->supported = WAKE_PHY | WAKE_MAGIC |
WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; /* Untested */
wol->wolopts = rp->wolopts;
spin_unlock_irq(&rp->lock);
}
static int rhine_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rhine_private *rp = netdev_priv(dev);
u32 support = WAKE_PHY | WAKE_MAGIC |
WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; /* Untested */
if (!(rp->quirks & rqWOL))
return -EINVAL;
if (wol->wolopts & ~support)
return -EINVAL;
spin_lock_irq(&rp->lock);
rp->wolopts = wol->wolopts;
spin_unlock_irq(&rp->lock);
return 0;
}
static struct ethtool_ops netdev_ethtool_ops = {
.get_drvinfo = netdev_get_drvinfo,
.get_settings = netdev_get_settings,
......@@ -1774,6 +1848,8 @@ static struct ethtool_ops netdev_ethtool_ops = {
.get_link = netdev_get_link,
.get_msglevel = netdev_get_msglevel,
.set_msglevel = netdev_set_msglevel,
.get_wol = rhine_get_wol,
.set_wol = rhine_set_wol,
.get_sg = ethtool_op_get_sg,
.get_tx_csum = ethtool_op_get_tx_csum,
};
......@@ -1844,12 +1920,51 @@ static void __devexit rhine_remove_one(struct pci_dev *pdev)
pci_set_drvdata(pdev, NULL);
}
static void rhine_shutdown (struct device *gendev)
{
struct pci_dev *pdev = to_pci_dev(gendev);
struct net_device *dev = pci_get_drvdata(pdev);
struct rhine_private *rp = netdev_priv(dev);
long ioaddr = dev->base_addr;
rhine_power_init(dev);
/* Make sure we use pattern 0, 1 and not 4, 5 */
if (rp->quirks & rq6patterns)
writeb(0x04, ioaddr + 0xA7);
if (rp->wolopts & WAKE_MAGIC)
writeb(WOLmagic, ioaddr + WOLcrSet);
if (rp->wolopts & (WAKE_BCAST|WAKE_MCAST))
writeb(WOLbmcast, ioaddr + WOLcgSet);
if (rp->wolopts & WAKE_PHY)
writeb(WOLlnkon | WOLlnkoff, ioaddr + WOLcrSet);
if (rp->wolopts & WAKE_UCAST)
writeb(WOLucast, ioaddr + WOLcrSet);
/* Enable legacy WOL (for old motherboards) */
writeb(0x01, ioaddr + PwcfgSet);
writeb(readb(ioaddr + StickyHW) | 0x04, ioaddr + StickyHW);
/* Hit power state D3 (sleep) */
writeb(readb(ioaddr + StickyHW) | 0x03, ioaddr + StickyHW);
/* TODO: Check use of pci_enable_wake() */
}
static struct pci_driver rhine_driver = {
.name = DRV_NAME,
.id_table = rhine_pci_tbl,
.probe = rhine_init_one,
.remove = __devexit_p(rhine_remove_one),
.driver = {
.shutdown = rhine_shutdown,
}
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment