Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 180992
b: refs/heads/master
c: 6cbf821
h: refs/heads/master
v: v3
  • Loading branch information
Rafael J. Wysocki authored and Jesse Barnes committed Feb 23, 2010
1 parent 6d7d298 commit 4875d19
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 35 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 552be54cc4232dc5acc49ccb372129d6f1b6923f
refs/heads/master: 6cbf82148ff286ec22a55be6836c3a5bffc489c1
160 changes: 133 additions & 27 deletions trunk/drivers/pci/pci-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/cpu.h>
#include <linux/pm_runtime.h>
#include "pci.h"

struct pci_dynid {
Expand Down Expand Up @@ -404,6 +405,35 @@ static void pci_device_shutdown(struct device *dev)
pci_msix_shutdown(pci_dev);
}

#ifdef CONFIG_PM_OPS

/* Auxiliary functions used for system resume and run-time resume. */

/**
* pci_restore_standard_config - restore standard config registers of PCI device
* @pci_dev: PCI device to handle
*/
static int pci_restore_standard_config(struct pci_dev *pci_dev)
{
pci_update_current_state(pci_dev, PCI_UNKNOWN);

if (pci_dev->current_state != PCI_D0) {
int error = pci_set_power_state(pci_dev, PCI_D0);
if (error)
return error;
}

return pci_restore_state(pci_dev);
}

static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
{
pci_restore_standard_config(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev);
}

#endif

#ifdef CONFIG_PM_SLEEP

/*
Expand Down Expand Up @@ -520,29 +550,6 @@ static int pci_legacy_resume(struct device *dev)

/* Auxiliary functions used by the new power management framework */

/**
* pci_restore_standard_config - restore standard config registers of PCI device
* @pci_dev: PCI device to handle
*/
static int pci_restore_standard_config(struct pci_dev *pci_dev)
{
pci_update_current_state(pci_dev, PCI_UNKNOWN);

if (pci_dev->current_state != PCI_D0) {
int error = pci_set_power_state(pci_dev, PCI_D0);
if (error)
return error;
}

return pci_restore_state(pci_dev);
}

static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
{
pci_restore_standard_config(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev);
}

static void pci_pm_default_resume(struct pci_dev *pci_dev)
{
pci_fixup_device(pci_fixup_resume, pci_dev);
Expand Down Expand Up @@ -581,6 +588,17 @@ static int pci_pm_prepare(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;

/*
* PCI devices suspended at run time need to be resumed at this
* point, because in general it is necessary to reconfigure them for
* system suspend. Namely, if the device is supposed to wake up the
* system from the sleep state, we may need to reconfigure it for this
* purpose. In turn, if the device is not supposed to wake up the
* system from the sleep state, we'll have to prevent it from signaling
* wake-up.
*/
pm_runtime_resume(dev);

if (drv && drv->pm && drv->pm->prepare)
error = drv->pm->prepare(dev);

Expand All @@ -595,6 +613,13 @@ static void pci_pm_complete(struct device *dev)
drv->pm->complete(dev);
}

#else /* !CONFIG_PM_SLEEP */

#define pci_pm_prepare NULL
#define pci_pm_complete NULL

#endif /* !CONFIG_PM_SLEEP */

#ifdef CONFIG_SUSPEND

static int pci_pm_suspend(struct device *dev)
Expand Down Expand Up @@ -681,7 +706,7 @@ static int pci_pm_resume_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;

pci_pm_default_resume_noirq(pci_dev);
pci_pm_default_resume_early(pci_dev);

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
Expand Down Expand Up @@ -879,7 +904,7 @@ static int pci_pm_restore_noirq(struct device *dev)
struct device_driver *drv = dev->driver;
int error = 0;

pci_pm_default_resume_noirq(pci_dev);
pci_pm_default_resume_early(pci_dev);

if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume_early(dev);
Expand Down Expand Up @@ -931,6 +956,84 @@ static int pci_pm_restore(struct device *dev)

#endif /* !CONFIG_HIBERNATION */

#ifdef CONFIG_PM_RUNTIME

static int pci_pm_runtime_suspend(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
pci_power_t prev = pci_dev->current_state;
int error;

if (!pm || !pm->runtime_suspend)
return -ENOSYS;

error = pm->runtime_suspend(dev);
suspend_report_result(pm->runtime_suspend, error);
if (error)
return error;

pci_fixup_device(pci_fixup_suspend, pci_dev);

if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
WARN_ONCE(pci_dev->current_state != prev,
"PCI PM: State of device not saved by %pF\n",
pm->runtime_suspend);
return 0;
}

if (!pci_dev->state_saved)
pci_save_state(pci_dev);

pci_finish_runtime_suspend(pci_dev);

return 0;
}

static int pci_pm_runtime_resume(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

if (!pm || !pm->runtime_resume)
return -ENOSYS;

pci_pm_default_resume_early(pci_dev);
__pci_enable_wake(pci_dev, PCI_D0, true, false);
pci_fixup_device(pci_fixup_resume, pci_dev);

return pm->runtime_resume(dev);
}

static int pci_pm_runtime_idle(struct device *dev)
{
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;

if (!pm)
return -ENOSYS;

if (pm->runtime_idle) {
int ret = pm->runtime_idle(dev);
if (ret)
return ret;
}

pm_runtime_suspend(dev);

return 0;
}

#else /* !CONFIG_PM_RUNTIME */

#define pci_pm_runtime_suspend NULL
#define pci_pm_runtime_resume NULL
#define pci_pm_runtime_idle NULL

#endif /* !CONFIG_PM_RUNTIME */

#ifdef CONFIG_PM_OPS

const struct dev_pm_ops pci_dev_pm_ops = {
.prepare = pci_pm_prepare,
.complete = pci_pm_complete,
Expand All @@ -946,15 +1049,18 @@ const struct dev_pm_ops pci_dev_pm_ops = {
.thaw_noirq = pci_pm_thaw_noirq,
.poweroff_noirq = pci_pm_poweroff_noirq,
.restore_noirq = pci_pm_restore_noirq,
.runtime_suspend = pci_pm_runtime_suspend,
.runtime_resume = pci_pm_runtime_resume,
.runtime_idle = pci_pm_runtime_idle,
};

#define PCI_PM_OPS_PTR (&pci_dev_pm_ops)

#else /* !CONFIG_PM_SLEEP */
#else /* !COMFIG_PM_OPS */

#define PCI_PM_OPS_PTR NULL

#endif /* !CONFIG_PM_SLEEP */
#endif /* !COMFIG_PM_OPS */

/**
* __pci_register_driver - register a new pci driver
Expand Down
43 changes: 37 additions & 6 deletions trunk/drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1302,9 +1302,10 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
}

/**
* pci_enable_wake - enable PCI device as wakeup event source
* __pci_enable_wake - enable PCI device as wakeup event source
* @dev: PCI device affected
* @state: PCI state from which device will issue wakeup events
* @runtime: True if the events are to be generated at run time
* @enable: True to enable event generation; false to disable
*
* This enables the device as a wakeup event source, or disables it.
Expand All @@ -1320,11 +1321,12 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
* Error code depending on the platform is returned if both the platform and
* the native mechanism fail to enable the generation of wake-up events
*/
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
bool runtime, bool enable)
{
int ret = 0;

if (enable && !device_may_wakeup(&dev->dev))
if (enable && !runtime && !device_may_wakeup(&dev->dev))
return -EINVAL;

/* Don't do the same thing twice in a row for one device. */
Expand All @@ -1344,19 +1346,24 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable)
pci_pme_active(dev, true);
else
ret = 1;
error = platform_pci_sleep_wake(dev, true);
error = runtime ? platform_pci_run_wake(dev, true) :
platform_pci_sleep_wake(dev, true);
if (ret)
ret = error;
if (!ret)
dev->wakeup_prepared = true;
} else {
platform_pci_sleep_wake(dev, false);
if (runtime)
platform_pci_run_wake(dev, false);
else
platform_pci_sleep_wake(dev, false);
pci_pme_active(dev, false);
dev->wakeup_prepared = false;
}

return ret;
}
EXPORT_SYMBOL(__pci_enable_wake);

/**
* pci_wake_from_d3 - enable/disable device to wake up from D3_hot or D3_cold
Expand Down Expand Up @@ -1465,6 +1472,31 @@ int pci_back_from_sleep(struct pci_dev *dev)
return pci_set_power_state(dev, PCI_D0);
}

/**
* pci_finish_runtime_suspend - Carry out PCI-specific part of runtime suspend.
* @dev: PCI device being suspended.
*
* Prepare @dev to generate wake-up events at run time and put it into a low
* power state.
*/
int pci_finish_runtime_suspend(struct pci_dev *dev)
{
pci_power_t target_state = pci_target_state(dev);
int error;

if (target_state == PCI_POWER_ERROR)
return -EIO;

__pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev));

error = pci_set_power_state(dev, target_state);

if (error)
__pci_enable_wake(dev, target_state, true, false);

return error;
}

/**
* pci_dev_run_wake - Check if device can generate run-time wake-up events.
* @dev: Device to check.
Expand Down Expand Up @@ -2978,7 +3010,6 @@ EXPORT_SYMBOL(pci_save_state);
EXPORT_SYMBOL(pci_restore_state);
EXPORT_SYMBOL(pci_pme_capable);
EXPORT_SYMBOL(pci_pme_active);
EXPORT_SYMBOL(pci_enable_wake);
EXPORT_SYMBOL(pci_wake_from_d3);
EXPORT_SYMBOL(pci_target_state);
EXPORT_SYMBOL(pci_prepare_to_sleep);
Expand Down
1 change: 1 addition & 0 deletions trunk/drivers/pci/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops);
extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state);
extern void pci_disable_enabled_device(struct pci_dev *dev);
extern bool pci_check_pme_status(struct pci_dev *dev);
extern int pci_finish_runtime_suspend(struct pci_dev *dev);
extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
extern void pci_pme_wakeup_bus(struct pci_bus *bus);
extern void pci_pm_init(struct pci_dev *dev);
Expand Down
9 changes: 8 additions & 1 deletion trunk/include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -777,13 +777,20 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state);
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state);
bool pci_pme_capable(struct pci_dev *dev, pci_power_t state);
void pci_pme_active(struct pci_dev *dev, bool enable);
int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable);
int __pci_enable_wake(struct pci_dev *dev, pci_power_t state,
bool runtime, bool enable);
int pci_wake_from_d3(struct pci_dev *dev, bool enable);
pci_power_t pci_target_state(struct pci_dev *dev);
int pci_prepare_to_sleep(struct pci_dev *dev);
int pci_back_from_sleep(struct pci_dev *dev);
bool pci_dev_run_wake(struct pci_dev *dev);

static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state,
bool enable)
{
return __pci_enable_wake(dev, state, false, enable);
}

/* For use by arch with custom probe code */
void set_pcie_port_type(struct pci_dev *pdev);
void set_pcie_hotplug_bridge(struct pci_dev *pdev);
Expand Down
5 changes: 5 additions & 0 deletions trunk/kernel/power/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,8 @@ config PM_RUNTIME
and the bus type drivers of the buses the devices are on are
responsible for the actual handling of the autosuspend requests and
wake-up events.

config PM_OPS
bool
depends on PM_SLEEP || PM_RUNTIME
default y

0 comments on commit 4875d19

Please sign in to comment.