Add support for Wake on LAN (WOL) reception and waking the device up from
this signal via the ethtool interface. Currently we are only supporting
the magic-packet variant of wakeup.

WOL is enabled by specifying a second interrupt resource to the driver
which indicates where the interrupt for the WOL is being signalled. This
then enables the necessary ethtool calls to leave the device in a state
to receive WOL frames when going into suspend.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Simtec Linux Team <linux@simtec.co.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ben Dooks 2009-11-10 07:22:24 +00:00 коммит произвёл David S. Miller
Родитель f9254edaab
Коммит c029f4440f
2 изменённых файлов: 142 добавлений и 8 удалений

Просмотреть файл

@ -100,6 +100,7 @@ typedef struct board_info {
unsigned int flags;
unsigned int in_suspend :1;
unsigned int wake_supported :1;
int debug_level;
enum dm9000_type type;
@ -116,6 +117,8 @@ typedef struct board_info {
struct resource *data_req;
struct resource *irq_res;
int irq_wake;
struct mutex addr_lock; /* phy and eeprom access lock */
struct delayed_work phy_poll;
@ -125,6 +128,7 @@ typedef struct board_info {
struct mii_if_info mii;
u32 msg_enable;
u32 wake_state;
int rx_csum;
int can_csum;
@ -568,6 +572,54 @@ static int dm9000_set_eeprom(struct net_device *dev,
return 0;
}
static void dm9000_get_wol(struct net_device *dev, struct ethtool_wolinfo *w)
{
board_info_t *dm = to_dm9000_board(dev);
memset(w, 0, sizeof(struct ethtool_wolinfo));
/* note, we could probably support wake-phy too */
w->supported = dm->wake_supported ? WAKE_MAGIC : 0;
w->wolopts = dm->wake_state;
}
static int dm9000_set_wol(struct net_device *dev, struct ethtool_wolinfo *w)
{
board_info_t *dm = to_dm9000_board(dev);
unsigned long flags;
u32 opts = w->wolopts;
u32 wcr = 0;
if (!dm->wake_supported)
return -EOPNOTSUPP;
if (opts & ~WAKE_MAGIC)
return -EINVAL;
if (opts & WAKE_MAGIC)
wcr |= WCR_MAGICEN;
mutex_lock(&dm->addr_lock);
spin_lock_irqsave(&dm->lock, flags);
iow(dm, DM9000_WCR, wcr);
spin_unlock_irqrestore(&dm->lock, flags);
mutex_unlock(&dm->addr_lock);
if (dm->wake_state != opts) {
/* change in wol state, update IRQ state */
if (!dm->wake_state)
set_irq_wake(dm->irq_wake, 1);
else if (dm->wake_state & !opts)
set_irq_wake(dm->irq_wake, 0);
}
dm->wake_state = opts;
return 0;
}
static const struct ethtool_ops dm9000_ethtool_ops = {
.get_drvinfo = dm9000_get_drvinfo,
.get_settings = dm9000_get_settings,
@ -576,6 +628,8 @@ static const struct ethtool_ops dm9000_ethtool_ops = {
.set_msglevel = dm9000_set_msglevel,
.nway_reset = dm9000_nway_reset,
.get_link = dm9000_get_link,
.get_wol = dm9000_get_wol,
.set_wol = dm9000_set_wol,
.get_eeprom_len = dm9000_get_eeprom_len,
.get_eeprom = dm9000_get_eeprom,
.set_eeprom = dm9000_set_eeprom,
@ -722,6 +776,7 @@ dm9000_init_dm9000(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
unsigned int imr;
unsigned int ncr;
dm9000_dbg(db, 1, "entering %s\n", __func__);
@ -736,8 +791,15 @@ dm9000_init_dm9000(struct net_device *dev)
iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */
iow(db, DM9000_GPR, 0); /* Enable PHY */
if (db->flags & DM9000_PLATF_EXT_PHY)
iow(db, DM9000_NCR, NCR_EXT_PHY);
ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0;
/* if wol is needed, then always set NCR_WAKEEN otherwise we end
* up dumping the wake events if we disable this. There is already
* a wake-mask in DM9000_WCR */
if (db->wake_supported)
ncr |= NCR_WAKEEN;
iow(db, DM9000_NCR, ncr);
/* Program operating register */
iow(db, DM9000_TCR, 0); /* TX Polling clear */
@ -1045,6 +1107,41 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
board_info_t *db = netdev_priv(dev);
unsigned long flags;
unsigned nsr, wcr;
spin_lock_irqsave(&db->lock, flags);
nsr = ior(db, DM9000_NSR);
wcr = ior(db, DM9000_WCR);
dev_dbg(db->dev, "%s: NSR=0x%02x, WCR=0x%02x\n", __func__, nsr, wcr);
if (nsr & NSR_WAKEST) {
/* clear, so we can avoid */
iow(db, DM9000_NSR, NSR_WAKEST);
if (wcr & WCR_LINKST)
dev_info(db->dev, "wake by link status change\n");
if (wcr & WCR_SAMPLEST)
dev_info(db->dev, "wake by sample packet\n");
if (wcr & WCR_MAGICST )
dev_info(db->dev, "wake by magic packet\n");
if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST)))
dev_err(db->dev, "wake signalled with no reason? "
"NSR=0x%02x, WSR=0x%02x\n", nsr, wcr);
}
spin_unlock_irqrestore(&db->lock, flags);
return (nsr & NSR_WAKEST) ? IRQ_HANDLED : IRQ_NONE;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
/*
*Used by netconsole
@ -1299,6 +1396,29 @@ dm9000_probe(struct platform_device *pdev)
goto out;
}
db->irq_wake = platform_get_irq(pdev, 1);
if (db->irq_wake >= 0) {
dev_dbg(db->dev, "wakeup irq %d\n", db->irq_wake);
ret = request_irq(db->irq_wake, dm9000_wol_interrupt,
IRQF_SHARED, dev_name(db->dev), ndev);
if (ret) {
dev_err(db->dev, "cannot get wakeup irq (%d)\n", ret);
} else {
/* test to see if irq is really wakeup capable */
ret = set_irq_wake(db->irq_wake, 1);
if (ret) {
dev_err(db->dev, "irq %d cannot set wakeup (%d)\n",
db->irq_wake, ret);
ret = 0;
} else {
set_irq_wake(db->irq_wake, 0);
db->wake_supported = 1;
}
}
}
iosize = resource_size(db->addr_res);
db->addr_req = request_mem_region(db->addr_res->start, iosize,
pdev->name);
@ -1490,10 +1610,14 @@ dm9000_drv_suspend(struct device *dev)
db = netdev_priv(ndev);
db->in_suspend = 1;
if (netif_running(ndev)) {
netif_device_detach(ndev);
if (!netif_running(ndev))
return 0;
netif_device_detach(ndev);
/* only shutdown if not using WoL */
if (!db->wake_state)
dm9000_shutdown(ndev);
}
}
return 0;
}
@ -1506,10 +1630,13 @@ dm9000_drv_resume(struct device *dev)
board_info_t *db = netdev_priv(ndev);
if (ndev) {
if (netif_running(ndev)) {
dm9000_reset(db);
dm9000_init_dm9000(ndev);
/* reset if we were not in wake mode to ensure if
* the device was powered off it is in a known state */
if (!db->wake_state) {
dm9000_reset(db);
dm9000_init_dm9000(ndev);
}
netif_device_attach(ndev);
}

Просмотреть файл

@ -111,6 +111,13 @@
#define RSR_CE (1<<1)
#define RSR_FOE (1<<0)
#define WCR_LINKEN (1 << 5)
#define WCR_SAMPLEEN (1 << 4)
#define WCR_MAGICEN (1 << 3)
#define WCR_LINKST (1 << 2)
#define WCR_SAMPLEST (1 << 1)
#define WCR_MAGICST (1 << 0)
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot) ( ot & 0xf )