Skip to content

Commit

Permalink
r8169 / PCI / PM: Add simplified runtime PM support (rev. 3)
Browse files Browse the repository at this point in the history
Use the PCI runtime power management framework to add basic PCI
runtime PM support to the r8169 driver.  Namely, make the driver
suspend the device when the link is not present and set it up for
generating a wakeup event after the link has been detected again.
[This feature is disabled until the user space enables it with the
help of the /sys/devices/.../power/contol device attribute.]

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Rafael J. Wysocki authored and David S. Miller committed Mar 17, 2010
1 parent ff6e216 commit e175944
Showing 1 changed file with 125 additions and 27 deletions.
152 changes: 125 additions & 27 deletions drivers/net/r8169.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <linux/tcp.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>

#include <asm/system.h>
#include <asm/io.h>
Expand Down Expand Up @@ -504,6 +505,7 @@ struct rtl8169_private {

struct mii_if_info mii;
struct rtl8169_counters counters;
u32 saved_wolopts;
};

MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>");
Expand Down Expand Up @@ -744,53 +746,61 @@ static void rtl8169_check_link_status(struct net_device *dev,

spin_lock_irqsave(&tp->lock, flags);
if (tp->link_ok(ioaddr)) {
/* This is to cancel a scheduled suspend if there's one. */
pm_request_resume(&tp->pci_dev->dev);
netif_carrier_on(dev);
netif_info(tp, ifup, dev, "link up\n");
} else {
netif_carrier_off(dev);
netif_info(tp, ifdown, dev, "link down\n");
pm_schedule_suspend(&tp->pci_dev->dev, 100);
}
spin_unlock_irqrestore(&tp->lock, flags);
}

static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)

static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
{
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);
u32 wolopts = 0;

options = RTL_R8(Config1);
if (!(options & PMEnable))
goto out_unlock;
return 0;

options = RTL_R8(Config3);
if (options & LinkUp)
wol->wolopts |= WAKE_PHY;
wolopts |= WAKE_PHY;
if (options & MagicPacket)
wol->wolopts |= WAKE_MAGIC;
wolopts |= WAKE_MAGIC;

options = RTL_R8(Config5);
if (options & UWF)
wol->wolopts |= WAKE_UCAST;
wolopts |= WAKE_UCAST;
if (options & BWF)
wol->wolopts |= WAKE_BCAST;
wolopts |= WAKE_BCAST;
if (options & MWF)
wol->wolopts |= WAKE_MCAST;
wolopts |= WAKE_MCAST;

out_unlock:
spin_unlock_irq(&tp->lock);
return wolopts;
}

static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rtl8169_private *tp = netdev_priv(dev);

spin_lock_irq(&tp->lock);

wol->supported = WAKE_ANY;
wol->wolopts = __rtl8169_get_wol(tp);

spin_unlock_irq(&tp->lock);
}

static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
{
void __iomem *ioaddr = tp->mmio_addr;
unsigned int i;
static const struct {
Expand All @@ -807,23 +817,29 @@ static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{ 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)
if (wolopts & cfg[i].opt)
options |= cfg[i].mask;
RTL_W8(cfg[i].reg, options);
}

RTL_W8(Cfg9346, Cfg9346_Lock);
}

static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct rtl8169_private *tp = netdev_priv(dev);

spin_lock_irq(&tp->lock);

if (wol->wolopts)
tp->features |= RTL_FEATURE_WOL;
else
tp->features &= ~RTL_FEATURE_WOL;
__rtl8169_set_wol(tp, wol->wolopts);
device_set_wakeup_enable(&tp->pci_dev->dev, wol->wolopts);

spin_unlock_irq(&tp->lock);
Expand Down Expand Up @@ -3189,6 +3205,12 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)

device_set_wakeup_enable(&pdev->dev, tp->features & RTL_FEATURE_WOL);

if (pci_dev_run_wake(pdev)) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
}
pm_runtime_idle(&pdev->dev);

out:
return rc;

Expand All @@ -3211,10 +3233,18 @@ static void __devexit rtl8169_remove_one(struct pci_dev *pdev)
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);

pm_runtime_get_sync(&pdev->dev);

flush_scheduled_work();

unregister_netdev(dev);

if (pci_dev_run_wake(pdev)) {
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
}
pm_runtime_put_noidle(&pdev->dev);

/* restore original MAC address */
rtl_rar_set(tp, dev->perm_addr);

Expand All @@ -3237,6 +3267,7 @@ static int rtl8169_open(struct net_device *dev)
struct pci_dev *pdev = tp->pci_dev;
int retval = -ENOMEM;

pm_runtime_get_sync(&pdev->dev);

rtl8169_set_rxbufsize(tp, dev);

Expand All @@ -3247,7 +3278,7 @@ static int rtl8169_open(struct net_device *dev)
tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES,
&tp->TxPhyAddr);
if (!tp->TxDescArray)
goto out;
goto err_pm_runtime_put;

tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES,
&tp->RxPhyAddr);
Expand All @@ -3274,6 +3305,9 @@ static int rtl8169_open(struct net_device *dev)

rtl8169_request_timer(dev);

tp->saved_wolopts = 0;
pm_runtime_put_noidle(&pdev->dev);

rtl8169_check_link_status(dev, tp, tp->mmio_addr);
out:
return retval;
Expand All @@ -3283,9 +3317,13 @@ static int rtl8169_open(struct net_device *dev)
err_free_rx_1:
pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray,
tp->RxPhyAddr);
tp->RxDescArray = NULL;
err_free_tx_0:
pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray,
tp->TxPhyAddr);
tp->TxDescArray = NULL;
err_pm_runtime_put:
pm_runtime_put_noidle(&pdev->dev);
goto out;
}

Expand Down Expand Up @@ -4692,6 +4730,8 @@ static int rtl8169_close(struct net_device *dev)
struct rtl8169_private *tp = netdev_priv(dev);
struct pci_dev *pdev = tp->pci_dev;

pm_runtime_get_sync(&pdev->dev);

/* update counters before going down */
rtl8169_update_counters(dev);

Expand All @@ -4706,6 +4746,8 @@ static int rtl8169_close(struct net_device *dev)
tp->TxDescArray = NULL;
tp->RxDescArray = NULL;

pm_runtime_put_sync(&pdev->dev);

return 0;
}

Expand Down Expand Up @@ -4804,28 +4846,84 @@ static int rtl8169_suspend(struct device *device)
return 0;
}

static void __rtl8169_resume(struct net_device *dev)
{
netif_device_attach(dev);
rtl8169_schedule_work(dev, rtl8169_reset_task);
}

static int rtl8169_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct net_device *dev = pci_get_drvdata(pdev);

if (!netif_running(dev))
goto out;
if (netif_running(dev))
__rtl8169_resume(dev);

netif_device_attach(dev);
return 0;
}

static int rtl8169_runtime_suspend(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);

if (!tp->TxDescArray)
return 0;

spin_lock_irq(&tp->lock);
tp->saved_wolopts = __rtl8169_get_wol(tp);
__rtl8169_set_wol(tp, WAKE_ANY);
spin_unlock_irq(&tp->lock);

rtl8169_net_suspend(dev);

return 0;
}

static int rtl8169_runtime_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);

if (!tp->TxDescArray)
return 0;

spin_lock_irq(&tp->lock);
__rtl8169_set_wol(tp, tp->saved_wolopts);
tp->saved_wolopts = 0;
spin_unlock_irq(&tp->lock);

__rtl8169_resume(dev);

rtl8169_schedule_work(dev, rtl8169_reset_task);
out:
return 0;
}

static int rtl8169_runtime_idle(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
struct net_device *dev = pci_get_drvdata(pdev);
struct rtl8169_private *tp = netdev_priv(dev);

if (!tp->TxDescArray)
return 0;

rtl8169_check_link_status(dev, tp, tp->mmio_addr);
return -EBUSY;
}

static const struct dev_pm_ops rtl8169_pm_ops = {
.suspend = rtl8169_suspend,
.resume = rtl8169_resume,
.freeze = rtl8169_suspend,
.thaw = rtl8169_resume,
.poweroff = rtl8169_suspend,
.restore = rtl8169_resume,
.runtime_suspend = rtl8169_runtime_suspend,
.runtime_resume = rtl8169_runtime_resume,
.runtime_idle = rtl8169_runtime_idle,
};

#define RTL8169_PM_OPS (&rtl8169_pm_ops)
Expand Down

0 comments on commit e175944

Please sign in to comment.