Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61a4dcc2 authored by Francois Romieu's avatar Francois Romieu
Browse files

r8169: enable wake on lan



Similar to 8139cp code but more inspired/lucky.

Signed-off-by: default avatarFrancois Romieu <romieu@fr.zoreil.com>
parent 5d06a99f
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -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: