r8169: enable wake on lan
authorFrancois Romieu <romieu@fr.zoreil.com>
Wed, 22 Feb 2006 23:55:25 +0000 (00:55 +0100)
committerFrancois Romieu <romieu@fr.zoreil.com>
Thu, 23 Feb 2006 22:06:48 +0000 (23:06 +0100)
Similar to 8139cp code but more inspired/lucky.

Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
drivers/net/r8169.c

index 999fd6cef77eebc17de2e20e7be5098f4ec39ce3..8cc0d0bbdf50f10143b6b64cac0afc54aaae944a 100644 (file)
@@ -290,7 +290,15 @@ enum RTL8169_register_content {
        /* Config1 register p.24 */
        PMEnable        = (1 << 0),     /* Power Management Enable */
 
+       /* Config3 register p.25 */
+       MagicPacket     = (1 << 5),     /* Wake up when receives a Magic Packet */
+       LinkUp          = (1 << 4),     /* Wake up when the cable connection is re-established */
+
        /* Config5 register p.27 */
+       BWF             = (1 << 6),     /* Accept Broadcast wakeup frame */
+       MWF             = (1 << 5),     /* Accept Multicast wakeup frame */
+       UWF             = (1 << 4),     /* Accept Unicast wakeup frame */
+       LanWake         = (1 << 1),     /* LanWake enable/disable */
        PMEStatus       = (1 << 0),     /* PME status can be reset by PCI RST# */
 
        /* TBICSR p.28 */
@@ -439,6 +447,7 @@ struct rtl8169_private {
        unsigned int (*phy_reset_pending)(void __iomem *);
        unsigned int (*link_ok)(void __iomem *);
        struct work_struct task;
+       unsigned wol_enabled : 1;
 };
 
 MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
@@ -613,6 +622,80 @@ static void rtl8169_link_option(int idx, u8 *autoneg, u16 *speed, u8 *duplex)
        *duplex = p->duplex;
 }
 
+static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
+       u8 options;
+
+       wol->wolopts = 0;
+
+#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
+       wol->supported = WAKE_ANY;
+
+       spin_lock_irq(&tp->lock);
+
+       options = RTL_R8(Config1);
+       if (!(options & PMEnable))
+               goto out_unlock;
+
+       options = RTL_R8(Config3);
+       if (options & LinkUp)
+               wol->wolopts |= WAKE_PHY;
+       if (options & MagicPacket)
+               wol->wolopts |= WAKE_MAGIC;
+
+       options = RTL_R8(Config5);
+       if (options & UWF)
+               wol->wolopts |= WAKE_UCAST;
+       if (options & BWF)
+               wol->wolopts |= WAKE_BCAST;
+       if (options & MWF)
+               wol->wolopts |= WAKE_MCAST;
+
+out_unlock:
+       spin_unlock_irq(&tp->lock);
+}
+
+static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       void __iomem *ioaddr = tp->mmio_addr;
+       int i;
+       static struct {
+               u32 opt;
+               u16 reg;
+               u8  mask;
+       } cfg[] = {
+               { WAKE_ANY,   Config1, PMEnable },
+               { WAKE_PHY,   Config3, LinkUp },
+               { WAKE_MAGIC, Config3, MagicPacket },
+               { WAKE_UCAST, Config5, UWF },
+               { WAKE_BCAST, Config5, BWF },
+               { WAKE_MCAST, Config5, MWF },
+               { WAKE_ANY,   Config5, LanWake }
+       };
+
+       spin_lock_irq(&tp->lock);
+
+       RTL_W8(Cfg9346, Cfg9346_Unlock);
+
+       for (i = 0; i < ARRAY_SIZE(cfg); i++) {
+               u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask;
+               if (wol->wolopts & cfg[i].opt)
+                       options |= cfg[i].mask;
+               RTL_W8(cfg[i].reg, options);
+       }
+
+       RTL_W8(Cfg9346, Cfg9346_Lock);
+
+       tp->wol_enabled = (wol->wolopts) ? 1 : 0;
+
+       spin_unlock_irq(&tp->lock);
+
+       return 0;
+}
+
 static void rtl8169_get_drvinfo(struct net_device *dev,
                                struct ethtool_drvinfo *info)
 {
@@ -1031,6 +1114,8 @@ static struct ethtool_ops rtl8169_ethtool_ops = {
        .get_tso                = ethtool_op_get_tso,
        .set_tso                = ethtool_op_set_tso,
        .get_regs               = rtl8169_get_regs,
+       .get_wol                = rtl8169_get_wol,
+       .set_wol                = rtl8169_set_wol,
        .get_strings            = rtl8169_get_strings,
        .get_stats_count        = rtl8169_get_stats_count,
        .get_ethtool_stats      = rtl8169_get_ethtool_stats,
@@ -2692,6 +2777,7 @@ static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state)
        spin_unlock_irq(&tp->lock);
 
        pci_save_state(pdev);
+       pci_enable_wake(pdev, pci_choose_state(pdev, state), tp->wol_enabled);
        pci_set_power_state(pdev, pci_choose_state(pdev, state));
 out:
        return 0;
@@ -2708,6 +2794,7 @@ static int rtl8169_resume(struct pci_dev *pdev)
 
        pci_set_power_state(pdev, PCI_D0);
        pci_restore_state(pdev);
+       pci_enable_wake(pdev, PCI_D0, 0);
 
        rtl8169_schedule_work(dev, rtl8169_reset_task);
 out: