sundance: Enable WoL support
authorDenis Kirjanov <kda@linux-powerpc.org>
Sat, 1 Dec 2012 08:39:19 +0000 (08:39 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 3 Dec 2012 18:32:15 +0000 (13:32 -0500)
Enable WoL support.

Signed-off-by: Denis Kirjanov <kda@linux-powerpc.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/dlink/sundance.c

index 3b83588e51f6ad41ac112b06d9f219c33eb957d2..bbeb4c7a85bab05dac62ac6046ccb8154bd005bc 100644 (file)
@@ -259,6 +259,7 @@ enum alta_offsets {
        EECtrl = 0x36,
        FlashAddr = 0x40,
        FlashData = 0x44,
+       WakeEvent = 0x45,
        TxStatus = 0x46,
        TxFrameId = 0x47,
        DownCounter = 0x18,
@@ -333,6 +334,14 @@ enum mac_ctrl1_bits {
        RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000,
 };
 
+/* Bits in WakeEvent register. */
+enum wake_event_bits {
+       WakePktEnable = 0x01,
+       MagicPktEnable = 0x02,
+       LinkEventEnable = 0x04,
+       WolEnable = 0x80,
+};
+
 /* The Rx and Tx buffer descriptors. */
 /* Note that using only 32 bit fields simplifies conversion to big-endian
    architectures. */
@@ -392,6 +401,7 @@ struct netdev_private {
        unsigned int default_port:4;            /* Last dev->if_port value. */
        unsigned int an_enable:1;
        unsigned int speed;
+       unsigned int wol_enabled:1;                     /* Wake on LAN enabled */
        struct tasklet_struct rx_tasklet;
        struct tasklet_struct tx_tasklet;
        int budget;
@@ -829,7 +839,7 @@ static int netdev_open(struct net_device *dev)
        unsigned long flags;
        int i;
 
-       /* Do we need to reset the chip??? */
+       sundance_reset(dev, 0x00ff << 16);
 
        i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
        if (i)
@@ -877,6 +887,10 @@ static int netdev_open(struct net_device *dev)
 
        iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1);
 
+       /* Disable Wol */
+       iowrite8(ioread8(ioaddr + WakeEvent) | 0x00, ioaddr + WakeEvent);
+       np->wol_enabled = 0;
+
        if (netif_msg_ifup(np))
                printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x "
                           "MAC Control %x, %4.4x %4.4x.\n",
@@ -1715,6 +1729,60 @@ static void get_ethtool_stats(struct net_device *dev,
        data[i++] = np->xstats.rx_mcasts;
 }
 
+#ifdef CONFIG_PM
+
+static void sundance_get_wol(struct net_device *dev,
+               struct ethtool_wolinfo *wol)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->base;
+       u8 wol_bits;
+
+       wol->wolopts = 0;
+
+       wol->supported = (WAKE_PHY | WAKE_MAGIC);
+       if (!np->wol_enabled)
+               return;
+
+       wol_bits = ioread8(ioaddr + WakeEvent);
+       if (wol_bits & MagicPktEnable)
+               wol->wolopts |= WAKE_MAGIC;
+       if (wol_bits & LinkEventEnable)
+               wol->wolopts |= WAKE_PHY;
+}
+
+static int sundance_set_wol(struct net_device *dev,
+       struct ethtool_wolinfo *wol)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->base;
+       u8 wol_bits;
+
+       if (!device_can_wakeup(&np->pci_dev->dev))
+               return -EOPNOTSUPP;
+
+       np->wol_enabled = !!(wol->wolopts);
+       wol_bits = ioread8(ioaddr + WakeEvent);
+       wol_bits &= ~(WakePktEnable | MagicPktEnable |
+                       LinkEventEnable | WolEnable);
+
+       if (np->wol_enabled) {
+               if (wol->wolopts & WAKE_MAGIC)
+                       wol_bits |= (MagicPktEnable | WolEnable);
+               if (wol->wolopts & WAKE_PHY)
+                       wol_bits |= (LinkEventEnable | WolEnable);
+       }
+       iowrite8(wol_bits, ioaddr + WakeEvent);
+
+       device_set_wakeup_enable(&np->pci_dev->dev, np->wol_enabled);
+
+       return 0;
+}
+#else
+#define sundance_get_wol NULL
+#define sundance_set_wol NULL
+#endif /* CONFIG_PM */
+
 static const struct ethtool_ops ethtool_ops = {
        .begin = check_if_running,
        .get_drvinfo = get_drvinfo,
@@ -1722,6 +1790,8 @@ static const struct ethtool_ops ethtool_ops = {
        .set_settings = set_settings,
        .nway_reset = nway_reset,
        .get_link = get_link,
+       .get_wol = sundance_get_wol,
+       .set_wol = sundance_set_wol,
        .get_msglevel = get_msglevel,
        .set_msglevel = set_msglevel,
        .get_strings = get_strings,
@@ -1867,6 +1937,8 @@ static void __devexit sundance_remove1 (struct pci_dev *pdev)
 static int sundance_suspend(struct pci_dev *pci_dev, pm_message_t state)
 {
        struct net_device *dev = pci_get_drvdata(pci_dev);
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->base;
 
        if (!netif_running(dev))
                return 0;
@@ -1875,6 +1947,12 @@ static int sundance_suspend(struct pci_dev *pci_dev, pm_message_t state)
        netif_device_detach(dev);
 
        pci_save_state(pci_dev);
+       if (np->wol_enabled) {
+               iowrite8(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode);
+               iowrite16(RxEnable, ioaddr + MACCtrl1);
+       }
+       pci_enable_wake(pci_dev, pci_choose_state(pci_dev, state),
+                       np->wol_enabled);
        pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
 
        return 0;
@@ -1890,6 +1968,7 @@ static int sundance_resume(struct pci_dev *pci_dev)
 
        pci_set_power_state(pci_dev, PCI_D0);
        pci_restore_state(pci_dev);
+       pci_enable_wake(pci_dev, PCI_D0, 0);
 
        err = netdev_open(dev);
        if (err) {