#include <linux/if_ether.h>
 #include <linux/aer.h>
 #include <linux/prefetch.h>
+#include <linux/pm_runtime.h>
 #ifdef CONFIG_IGB_DCA
 #include <linux/dca.h>
 #endif
 #endif
 
 #ifdef CONFIG_PM
-static int igb_suspend(struct pci_dev *, pm_message_t);
-static int igb_resume(struct pci_dev *);
+static int igb_suspend(struct device *);
+static int igb_resume(struct device *);
+#ifdef CONFIG_PM_RUNTIME
+static int igb_runtime_suspend(struct device *dev);
+static int igb_runtime_resume(struct device *dev);
+static int igb_runtime_idle(struct device *dev);
+#endif
+static const struct dev_pm_ops igb_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(igb_suspend, igb_resume)
+       SET_RUNTIME_PM_OPS(igb_runtime_suspend, igb_runtime_resume,
+                       igb_runtime_idle)
+};
 #endif
 static void igb_shutdown(struct pci_dev *);
 #ifdef CONFIG_IGB_DCA
        .probe    = igb_probe,
        .remove   = __devexit_p(igb_remove),
 #ifdef CONFIG_PM
-       /* Power Management Hooks */
-       .suspend  = igb_suspend,
-       .resume   = igb_resume,
+       .driver.pm = &igb_pm_ops,
 #endif
        .shutdown = igb_shutdown,
        .err_handler = &igb_err_handler
        default:
                break;
        }
+
+       pm_runtime_put_noidle(&pdev->dev);
        return 0;
 
 err_register:
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
 
+       pm_runtime_get_noresume(&pdev->dev);
+
        /*
         * The watchdog timer may be rescheduled, so explicitly
         * disable watchdog from being rescheduled.
  * handler is registered with the OS, the watchdog timer is started,
  * and the stack is notified that the interface is ready.
  **/
-static int igb_open(struct net_device *netdev)
+static int __igb_open(struct net_device *netdev, bool resuming)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
+       struct pci_dev *pdev = adapter->pdev;
        int err;
        int i;
 
        /* disallow open during test */
-       if (test_bit(__IGB_TESTING, &adapter->state))
+       if (test_bit(__IGB_TESTING, &adapter->state)) {
+               WARN_ON(resuming);
                return -EBUSY;
+       }
+
+       if (!resuming)
+               pm_runtime_get_sync(&pdev->dev);
 
        netif_carrier_off(netdev);
 
 
        netif_tx_start_all_queues(netdev);
 
+       if (!resuming)
+               pm_runtime_put(&pdev->dev);
+
        /* start the watchdog. */
        hw->mac.get_link_status = 1;
        schedule_work(&adapter->watchdog_task);
        igb_free_all_tx_resources(adapter);
 err_setup_tx:
        igb_reset(adapter);
+       if (!resuming)
+               pm_runtime_put(&pdev->dev);
 
        return err;
 }
 
+static int igb_open(struct net_device *netdev)
+{
+       return __igb_open(netdev, false);
+}
+
 /**
  * igb_close - Disables a network interface
  * @netdev: network interface device structure
  * needs to be disabled.  A global MAC reset is issued to stop the
  * hardware, and all transmit and receive resources are freed.
  **/
-static int igb_close(struct net_device *netdev)
+static int __igb_close(struct net_device *netdev, bool suspending)
 {
        struct igb_adapter *adapter = netdev_priv(netdev);
+       struct pci_dev *pdev = adapter->pdev;
 
        WARN_ON(test_bit(__IGB_RESETTING, &adapter->state));
-       igb_down(adapter);
 
+       if (!suspending)
+               pm_runtime_get_sync(&pdev->dev);
+
+       igb_down(adapter);
        igb_free_irq(adapter);
 
        igb_free_all_tx_resources(adapter);
        igb_free_all_rx_resources(adapter);
 
+       if (!suspending)
+               pm_runtime_put_sync(&pdev->dev);
        return 0;
 }
 
+static int igb_close(struct net_device *netdev)
+{
+       return __igb_close(netdev, false);
+}
+
 /**
  * igb_setup_tx_resources - allocate Tx resources (Descriptors)
  * @tx_ring: tx descriptor ring (for a specific queue) to setup
 
        link = igb_has_link(adapter);
        if (link) {
+               /* Cancel scheduled suspend requests. */
+               pm_runtime_resume(netdev->dev.parent);
+
                if (!netif_carrier_ok(netdev)) {
                        u32 ctrl;
                        hw->mac.ops.get_speed_and_duplex(hw,
                        if (!test_bit(__IGB_DOWN, &adapter->state))
                                mod_timer(&adapter->phy_info_timer,
                                          round_jiffies(jiffies + 2 * HZ));
+
+                       pm_schedule_suspend(netdev->dev.parent,
+                                           MSEC_PER_SEC * 5);
                }
        }
 
        return -EINVAL;
 }
 
-static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake)
+static int __igb_shutdown(struct pci_dev *pdev, bool *enable_wake,
+                         bool runtime)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
        u32 ctrl, rctl, status;
-       u32 wufc = adapter->wol;
+       u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol;
 #ifdef CONFIG_PM
        int retval = 0;
 #endif
        netif_device_detach(netdev);
 
        if (netif_running(netdev))
-               igb_close(netdev);
+               __igb_close(netdev, true);
 
        igb_clear_interrupt_scheme(adapter);
 
 }
 
 #ifdef CONFIG_PM
-static int igb_suspend(struct pci_dev *pdev, pm_message_t state)
+static int igb_suspend(struct device *dev)
 {
        int retval;
        bool wake;
+       struct pci_dev *pdev = to_pci_dev(dev);
 
-       retval = __igb_shutdown(pdev, &wake);
+       retval = __igb_shutdown(pdev, &wake, 0);
        if (retval)
                return retval;
 
        return 0;
 }
 
-static int igb_resume(struct pci_dev *pdev)
+static int igb_resume(struct device *dev)
 {
+       struct pci_dev *pdev = to_pci_dev(dev);
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct igb_adapter *adapter = netdev_priv(netdev);
        struct e1000_hw *hw = &adapter->hw;
        pci_enable_wake(pdev, PCI_D3hot, 0);
        pci_enable_wake(pdev, PCI_D3cold, 0);
 
-       if (igb_init_interrupt_scheme(adapter)) {
+       if (!rtnl_is_locked()) {
+               /*
+                * shut up ASSERT_RTNL() warning in
+                * netif_set_real_num_tx/rx_queues.
+                */
+               rtnl_lock();
+               err = igb_init_interrupt_scheme(adapter);
+               rtnl_unlock();
+       } else {
+               err = igb_init_interrupt_scheme(adapter);
+       }
+       if (err) {
                dev_err(&pdev->dev, "Unable to allocate memory for queues\n");
                return -ENOMEM;
        }
 
        wr32(E1000_WUS, ~0);
 
-       if (netif_running(netdev)) {
-               err = igb_open(netdev);
+       if (netdev->flags & IFF_UP) {
+               err = __igb_open(netdev, true);
                if (err)
                        return err;
        }
 
        netif_device_attach(netdev);
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int igb_runtime_idle(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct igb_adapter *adapter = netdev_priv(netdev);
+
+       if (!igb_has_link(adapter))
+               pm_schedule_suspend(dev, MSEC_PER_SEC * 5);
+
+       return -EBUSY;
+}
+
+static int igb_runtime_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       int retval;
+       bool wake;
+
+       retval = __igb_shutdown(pdev, &wake, 1);
+       if (retval)
+               return retval;
+
+       if (wake) {
+               pci_prepare_to_sleep(pdev);
+       } else {
+               pci_wake_from_d3(pdev, false);
+               pci_set_power_state(pdev, PCI_D3hot);
+       }
 
        return 0;
 }
+
+static int igb_runtime_resume(struct device *dev)
+{
+       return igb_resume(dev);
+}
+#endif /* CONFIG_PM_RUNTIME */
 #endif
 
 static void igb_shutdown(struct pci_dev *pdev)
 {
        bool wake;
 
-       __igb_shutdown(pdev, &wake);
+       __igb_shutdown(pdev, &wake, 0);
 
        if (system_state == SYSTEM_POWER_OFF) {
                pci_wake_from_d3(pdev, wake);