|
@@ -290,7 +290,15 @@ enum RTL8169_register_content {
|
|
/* Config1 register p.24 */
|
|
/* Config1 register p.24 */
|
|
PMEnable = (1 << 0), /* Power Management Enable */
|
|
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 */
|
|
/* 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# */
|
|
PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */
|
|
|
|
|
|
/* TBICSR p.28 */
|
|
/* TBICSR p.28 */
|
|
@@ -439,6 +447,7 @@ struct rtl8169_private {
|
|
unsigned int (*phy_reset_pending)(void __iomem *);
|
|
unsigned int (*phy_reset_pending)(void __iomem *);
|
|
unsigned int (*link_ok)(void __iomem *);
|
|
unsigned int (*link_ok)(void __iomem *);
|
|
struct work_struct task;
|
|
struct work_struct task;
|
|
|
|
+ unsigned wol_enabled : 1;
|
|
};
|
|
};
|
|
|
|
|
|
MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
|
|
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;
|
|
*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,
|
|
static void rtl8169_get_drvinfo(struct net_device *dev,
|
|
struct ethtool_drvinfo *info)
|
|
struct ethtool_drvinfo *info)
|
|
{
|
|
{
|
|
@@ -1031,6 +1114,8 @@ static struct ethtool_ops rtl8169_ethtool_ops = {
|
|
.get_tso = ethtool_op_get_tso,
|
|
.get_tso = ethtool_op_get_tso,
|
|
.set_tso = ethtool_op_set_tso,
|
|
.set_tso = ethtool_op_set_tso,
|
|
.get_regs = rtl8169_get_regs,
|
|
.get_regs = rtl8169_get_regs,
|
|
|
|
+ .get_wol = rtl8169_get_wol,
|
|
|
|
+ .set_wol = rtl8169_set_wol,
|
|
.get_strings = rtl8169_get_strings,
|
|
.get_strings = rtl8169_get_strings,
|
|
.get_stats_count = rtl8169_get_stats_count,
|
|
.get_stats_count = rtl8169_get_stats_count,
|
|
.get_ethtool_stats = rtl8169_get_ethtool_stats,
|
|
.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);
|
|
spin_unlock_irq(&tp->lock);
|
|
|
|
|
|
pci_save_state(pdev);
|
|
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));
|
|
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
out:
|
|
out:
|
|
return 0;
|
|
return 0;
|
|
@@ -2708,6 +2794,7 @@ static int rtl8169_resume(struct pci_dev *pdev)
|
|
|
|
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
pci_restore_state(pdev);
|
|
pci_restore_state(pdev);
|
|
|
|
+ pci_enable_wake(pdev, PCI_D0, 0);
|
|
|
|
|
|
rtl8169_schedule_work(dev, rtl8169_reset_task);
|
|
rtl8169_schedule_work(dev, rtl8169_reset_task);
|
|
out:
|
|
out:
|