|
@@ -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);
|
|
|
}
|