|
@@ -44,6 +44,7 @@
|
|
|
#include <linux/cpu.h>
|
|
|
#include <linux/smp.h>
|
|
|
#include <linux/pm_qos_params.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/aer.h>
|
|
|
|
|
|
#include "e1000.h"
|
|
@@ -3083,12 +3084,15 @@ static int e1000_open(struct net_device *netdev)
|
|
|
{
|
|
|
struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
struct e1000_hw *hw = &adapter->hw;
|
|
|
+ struct pci_dev *pdev = adapter->pdev;
|
|
|
int err;
|
|
|
|
|
|
/* disallow open during test */
|
|
|
if (test_bit(__E1000_TESTING, &adapter->state))
|
|
|
return -EBUSY;
|
|
|
|
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
|
+
|
|
|
netif_carrier_off(netdev);
|
|
|
|
|
|
/* allocate transmit descriptors */
|
|
@@ -3149,6 +3153,9 @@ static int e1000_open(struct net_device *netdev)
|
|
|
|
|
|
netif_start_queue(netdev);
|
|
|
|
|
|
+ adapter->idle_check = true;
|
|
|
+ pm_runtime_put(&pdev->dev);
|
|
|
+
|
|
|
/* fire a link status change interrupt to start the watchdog */
|
|
|
ew32(ICS, E1000_ICS_LSC);
|
|
|
|
|
@@ -3162,6 +3169,7 @@ err_setup_rx:
|
|
|
e1000e_free_tx_resources(adapter);
|
|
|
err_setup_tx:
|
|
|
e1000e_reset(adapter);
|
|
|
+ pm_runtime_put_sync(&pdev->dev);
|
|
|
|
|
|
return err;
|
|
|
}
|
|
@@ -3180,11 +3188,17 @@ err_setup_tx:
|
|
|
static int e1000_close(struct net_device *netdev)
|
|
|
{
|
|
|
struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct pci_dev *pdev = adapter->pdev;
|
|
|
|
|
|
WARN_ON(test_bit(__E1000_RESETTING, &adapter->state));
|
|
|
- e1000e_down(adapter);
|
|
|
+
|
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
|
+
|
|
|
+ if (!test_bit(__E1000_DOWN, &adapter->state)) {
|
|
|
+ e1000e_down(adapter);
|
|
|
+ e1000_free_irq(adapter);
|
|
|
+ }
|
|
|
e1000_power_down_phy(adapter);
|
|
|
- e1000_free_irq(adapter);
|
|
|
|
|
|
e1000e_free_tx_resources(adapter);
|
|
|
e1000e_free_rx_resources(adapter);
|
|
@@ -3206,6 +3220,8 @@ static int e1000_close(struct net_device *netdev)
|
|
|
if (adapter->flags & FLAG_HAS_AMT)
|
|
|
e1000_release_hw_control(adapter);
|
|
|
|
|
|
+ pm_runtime_put_sync(&pdev->dev);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
/**
|
|
@@ -3550,6 +3566,9 @@ static void e1000_watchdog_task(struct work_struct *work)
|
|
|
|
|
|
link = e1000e_has_link(adapter);
|
|
|
if ((netif_carrier_ok(netdev)) && link) {
|
|
|
+ /* Cancel scheduled suspend requests. */
|
|
|
+ pm_runtime_resume(netdev->dev.parent);
|
|
|
+
|
|
|
e1000e_enable_receives(adapter);
|
|
|
goto link_up;
|
|
|
}
|
|
@@ -3561,6 +3580,10 @@ static void e1000_watchdog_task(struct work_struct *work)
|
|
|
if (link) {
|
|
|
if (!netif_carrier_ok(netdev)) {
|
|
|
bool txb2b = 1;
|
|
|
+
|
|
|
+ /* Cancel scheduled suspend requests. */
|
|
|
+ pm_runtime_resume(netdev->dev.parent);
|
|
|
+
|
|
|
/* update snapshot of PHY registers on LSC */
|
|
|
e1000_phy_read_status(adapter);
|
|
|
mac->ops.get_link_up_info(&adapter->hw,
|
|
@@ -3676,6 +3699,9 @@ static void e1000_watchdog_task(struct work_struct *work)
|
|
|
|
|
|
if (adapter->flags & FLAG_RX_NEEDS_RESTART)
|
|
|
schedule_work(&adapter->reset_task);
|
|
|
+ else
|
|
|
+ pm_schedule_suspend(netdev->dev.parent,
|
|
|
+ LINK_TIMEOUT);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -4473,13 +4499,15 @@ out:
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake)
|
|
|
+static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake,
|
|
|
+ bool runtime)
|
|
|
{
|
|
|
struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
struct e1000_hw *hw = &adapter->hw;
|
|
|
u32 ctrl, ctrl_ext, rctl, status;
|
|
|
- u32 wufc = adapter->wol;
|
|
|
+ /* Runtime suspend should only enable wakeup for link changes */
|
|
|
+ u32 wufc = runtime ? E1000_WUFC_LNKC : adapter->wol;
|
|
|
int retval = 0;
|
|
|
|
|
|
netif_device_detach(netdev);
|
|
@@ -4637,41 +4665,65 @@ static void e1000e_disable_l1aspm(struct pci_dev *pdev)
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
-static int e1000_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
+static bool e1000e_pm_ready(struct e1000_adapter *adapter)
|
|
|
+{
|
|
|
+ return !!adapter->tx_ring->buffer_info;
|
|
|
+}
|
|
|
+
|
|
|
+static int e1000_idle(struct device *dev)
|
|
|
{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
+ struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ if (!e1000e_pm_ready(adapter))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (adapter->idle_check) {
|
|
|
+ adapter->idle_check = false;
|
|
|
+ if (!e1000e_has_link(adapter))
|
|
|
+ pm_schedule_suspend(dev, MSEC_PER_SEC);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -EBUSY;
|
|
|
+}
|
|
|
+
|
|
|
+static int e1000_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
int retval;
|
|
|
bool wake;
|
|
|
|
|
|
- retval = __e1000_shutdown(pdev, &wake);
|
|
|
+ retval = __e1000_shutdown(pdev, &wake, false);
|
|
|
if (!retval)
|
|
|
e1000_complete_shutdown(pdev, true, wake);
|
|
|
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-static int e1000_resume(struct pci_dev *pdev)
|
|
|
+static int e1000_runtime_suspend(struct device *dev)
|
|
|
{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
- struct e1000_hw *hw = &adapter->hw;
|
|
|
- u32 err;
|
|
|
|
|
|
- pci_set_power_state(pdev, PCI_D0);
|
|
|
- pci_restore_state(pdev);
|
|
|
- pci_save_state(pdev);
|
|
|
- e1000e_disable_l1aspm(pdev);
|
|
|
+ if (e1000e_pm_ready(adapter)) {
|
|
|
+ bool wake;
|
|
|
|
|
|
- err = pci_enable_device_mem(pdev);
|
|
|
- if (err) {
|
|
|
- dev_err(&pdev->dev,
|
|
|
- "Cannot enable PCI device from suspend\n");
|
|
|
- return err;
|
|
|
+ __e1000_shutdown(pdev, &wake, true);
|
|
|
}
|
|
|
|
|
|
- pci_set_master(pdev);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
- pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
|
- pci_enable_wake(pdev, PCI_D3cold, 0);
|
|
|
+static int __e1000_resume(struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
+ struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+ struct e1000_hw *hw = &adapter->hw;
|
|
|
+ u32 err;
|
|
|
+
|
|
|
+ e1000e_disable_l1aspm(pdev);
|
|
|
|
|
|
e1000e_set_interrupt_capability(adapter);
|
|
|
if (netif_running(netdev)) {
|
|
@@ -4730,13 +4782,38 @@ static int e1000_resume(struct pci_dev *pdev)
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+static int e1000_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
+ struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ if (e1000e_pm_ready(adapter))
|
|
|
+ adapter->idle_check = true;
|
|
|
+
|
|
|
+ return __e1000_resume(pdev);
|
|
|
+}
|
|
|
+
|
|
|
+static int e1000_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
|
+ struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
+ struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+
|
|
|
+ if (!e1000e_pm_ready(adapter))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ adapter->idle_check = !dev->power.runtime_auto;
|
|
|
+ return __e1000_resume(pdev);
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
static void e1000_shutdown(struct pci_dev *pdev)
|
|
|
{
|
|
|
bool wake = false;
|
|
|
|
|
|
- __e1000_shutdown(pdev, &wake);
|
|
|
+ __e1000_shutdown(pdev, &wake, false);
|
|
|
|
|
|
if (system_state == SYSTEM_POWER_OFF)
|
|
|
e1000_complete_shutdown(pdev, false, wake);
|
|
@@ -4809,8 +4886,8 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
|
|
|
result = PCI_ERS_RESULT_DISCONNECT;
|
|
|
} else {
|
|
|
pci_set_master(pdev);
|
|
|
+ pdev->state_saved = true;
|
|
|
pci_restore_state(pdev);
|
|
|
- pci_save_state(pdev);
|
|
|
|
|
|
pci_enable_wake(pdev, PCI_D3hot, 0);
|
|
|
pci_enable_wake(pdev, PCI_D3cold, 0);
|
|
@@ -5217,6 +5294,12 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
|
|
|
|
|
|
e1000_print_device_info(adapter);
|
|
|
|
|
|
+ if (pci_dev_run_wake(pdev)) {
|
|
|
+ pm_runtime_set_active(&pdev->dev);
|
|
|
+ pm_runtime_enable(&pdev->dev);
|
|
|
+ }
|
|
|
+ pm_schedule_suspend(&pdev->dev, MSEC_PER_SEC);
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
err_register:
|
|
@@ -5259,12 +5342,16 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
|
|
|
{
|
|
|
struct net_device *netdev = pci_get_drvdata(pdev);
|
|
|
struct e1000_adapter *adapter = netdev_priv(netdev);
|
|
|
+ bool down = test_bit(__E1000_DOWN, &adapter->state);
|
|
|
+
|
|
|
+ pm_runtime_get_sync(&pdev->dev);
|
|
|
|
|
|
/*
|
|
|
* flush_scheduled work may reschedule our watchdog task, so
|
|
|
* explicitly disable watchdog tasks from being rescheduled
|
|
|
*/
|
|
|
- set_bit(__E1000_DOWN, &adapter->state);
|
|
|
+ if (!down)
|
|
|
+ set_bit(__E1000_DOWN, &adapter->state);
|
|
|
del_timer_sync(&adapter->watchdog_timer);
|
|
|
del_timer_sync(&adapter->phy_info_timer);
|
|
|
|
|
@@ -5278,8 +5365,17 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
|
|
|
if (!(netdev->flags & IFF_UP))
|
|
|
e1000_power_down_phy(adapter);
|
|
|
|
|
|
+ /* Don't lie to e1000_close() down the road. */
|
|
|
+ if (!down)
|
|
|
+ clear_bit(__E1000_DOWN, &adapter->state);
|
|
|
unregister_netdev(netdev);
|
|
|
|
|
|
+ if (pci_dev_run_wake(pdev)) {
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
|
+ pm_runtime_set_suspended(&pdev->dev);
|
|
|
+ }
|
|
|
+ pm_runtime_put_noidle(&pdev->dev);
|
|
|
+
|
|
|
/*
|
|
|
* Release control of h/w to f/w. If f/w is AMT enabled, this
|
|
|
* would have already happened in close and is redundant.
|
|
@@ -5379,6 +5475,18 @@ static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = {
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
|
|
|
|
|
|
+static const struct dev_pm_ops e1000_pm_ops = {
|
|
|
+ .suspend = e1000_suspend,
|
|
|
+ .resume = e1000_resume,
|
|
|
+ .freeze = e1000_suspend,
|
|
|
+ .thaw = e1000_resume,
|
|
|
+ .poweroff = e1000_suspend,
|
|
|
+ .restore = e1000_resume,
|
|
|
+ .runtime_suspend = e1000_runtime_suspend,
|
|
|
+ .runtime_resume = e1000_runtime_resume,
|
|
|
+ .runtime_idle = e1000_idle,
|
|
|
+};
|
|
|
+
|
|
|
/* PCI Device API Driver */
|
|
|
static struct pci_driver e1000_driver = {
|
|
|
.name = e1000e_driver_name,
|
|
@@ -5386,9 +5494,7 @@ static struct pci_driver e1000_driver = {
|
|
|
.probe = e1000_probe,
|
|
|
.remove = __devexit_p(e1000_remove),
|
|
|
#ifdef CONFIG_PM
|
|
|
- /* Power Management Hooks */
|
|
|
- .suspend = e1000_suspend,
|
|
|
- .resume = e1000_resume,
|
|
|
+ .driver.pm = &e1000_pm_ops,
|
|
|
#endif
|
|
|
.shutdown = e1000_shutdown,
|
|
|
.err_handler = &e1000_err_handler
|