Skip to content

Commit

Permalink
Merge branch 'topic/huang-d3cold-v7' into next
Browse files Browse the repository at this point in the history
* topic/huang-d3cold-v7:
  PCI/PM: add PCIe runtime D3cold support
  PCI: do not call pci_set_power_state with PCI_D3cold
  PCI/PM: add runtime PM support to PCIe port
  ACPI/PM: specify lowest allowed state for device sleep state
  • Loading branch information
Bjorn Helgaas committed Jun 23, 2012
2 parents e5028b5 + 448bd85 commit 35e7f73
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 29 deletions.
2 changes: 1 addition & 1 deletion arch/x86/pci/mrst.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_d3delay_fixup);

static void __devinit mrst_power_off_unused_dev(struct pci_dev *dev)
{
pci_set_power_state(dev, PCI_D3cold);
pci_set_power_state(dev, PCI_D3hot);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0801, mrst_power_off_unused_dev);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0809, mrst_power_off_unused_dev);
Expand Down
24 changes: 19 additions & 5 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -716,8 +716,9 @@ int acpi_suspend(u32 acpi_state)
* @dev: device to examine; its driver model wakeup flags control
* whether it should be able to wake up the system
* @d_min_p: used to store the upper limit of allowed states range
* Return value: preferred power state of the device on success, -ENODEV on
* failure (ie. if there's no 'struct acpi_device' for @dev)
* @d_max_in: specify the lowest allowed states
* Return value: preferred power state of the device on success, -ENODEV
* (ie. if there's no 'struct acpi_device' for @dev) or -EINVAL on failure
*
* Find the lowest power (highest number) ACPI device power state that
* device @dev can be in while the system is in the sleep state represented
Expand All @@ -732,22 +733,26 @@ int acpi_suspend(u32 acpi_state)
* via @wake.
*/

int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
{
acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
struct acpi_device *adev;
char acpi_method[] = "_SxD";
unsigned long long d_min, d_max;

if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3)
return -EINVAL;
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
printk(KERN_DEBUG "ACPI handle has no context!\n");
return -ENODEV;
}

acpi_method[2] = '0' + acpi_target_sleep_state;
/*
* If the sleep state is S0, we will return D3, but if the device has
* _S0W, we will use the value from _S0W
* If the sleep state is S0, the lowest limit from ACPI is D3,
* but if the device has _S0W, we will use the value from _S0W
* as the lowest limit from ACPI. Finally, we will constrain
* the lowest limit with the specified one.
*/
d_min = ACPI_STATE_D0;
d_max = ACPI_STATE_D3;
Expand Down Expand Up @@ -791,8 +796,17 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
}
}

if (d_max_in < d_min)
return -EINVAL;
if (d_min_p)
*d_min_p = d_min;
/* constrain d_max with specified lowest limit (max number) */
if (d_max > d_max_in) {
for (d_max = d_max_in; d_max > d_min; d_max--) {
if (adev->power.states[d_max].flags.valid)
break;
}
}
return d_max;
}
#endif /* CONFIG_PM */
Expand Down
2 changes: 1 addition & 1 deletion drivers/misc/cb710/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
pci_save_state(pdev);
pci_disable_device(pdev);
if (state.event & PM_EVENT_SLEEP)
pci_set_power_state(pdev, PCI_D3cold);
pci_set_power_state(pdev, PCI_D3hot);
return 0;
}

Expand Down
22 changes: 19 additions & 3 deletions drivers/pci/pci-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
return;

if (pci_dev->current_state == PCI_D3cold) {
pci_wakeup_event(pci_dev);
pm_runtime_resume(&pci_dev->dev);
return;
}

if (!pci_dev->pm_cap || !pci_dev->pme_support
|| pci_check_pme_status(pci_dev)) {
if (pci_dev->pme_poll)
Expand Down Expand Up @@ -201,9 +207,13 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)

static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
{
int acpi_state;
int acpi_state, d_max;

acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL);
if (pdev->no_d3cold)
d_max = ACPI_STATE_D3_HOT;
else
d_max = ACPI_STATE_D3_COLD;
acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max);
if (acpi_state < 0)
return PCI_POWER_ERROR;

Expand Down Expand Up @@ -310,7 +320,13 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)

static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
{
if (dev->pme_interrupt)
/*
* Per PCI Express Base Specification Revision 2.0 section
* 5.3.3.2 Link Wakeup, platform support is needed for D3cold
* waking up to power on the main link even if there is PME
* support for D3cold
*/
if (dev->pme_interrupt && !dev->runtime_d3cold)
return 0;

if (!acpi_pm_device_run_wake(&dev->dev, enable))
Expand Down
10 changes: 9 additions & 1 deletion drivers/pci/pci-driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1019,10 +1019,13 @@ static int pci_pm_runtime_suspend(struct device *dev)
if (!pm || !pm->runtime_suspend)
return -ENOSYS;

pci_dev->no_d3cold = false;
error = pm->runtime_suspend(dev);
suspend_report_result(pm->runtime_suspend, error);
if (error)
return error;
if (!pci_dev->d3cold_allowed)
pci_dev->no_d3cold = true;

pci_fixup_device(pci_fixup_suspend, pci_dev);

Expand All @@ -1044,6 +1047,7 @@ static int pci_pm_runtime_suspend(struct device *dev)

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

Expand All @@ -1054,7 +1058,11 @@ static int pci_pm_runtime_resume(struct device *dev)
__pci_enable_wake(pci_dev, PCI_D0, true, false);
pci_fixup_device(pci_fixup_resume, pci_dev);

return pm->runtime_resume(dev);
rc = pm->runtime_resume(dev);

pci_dev->runtime_d3cold = false;

return rc;
}

static int pci_pm_runtime_idle(struct device *dev)
Expand Down
29 changes: 29 additions & 0 deletions drivers/pci/pci-sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/pci-aspm.h>
#include <linux/slab.h>
#include <linux/vgaarb.h>
#include <linux/pm_runtime.h>
#include "pci.h"

static int sysfs_initialized; /* = 0 */
Expand Down Expand Up @@ -378,6 +379,31 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,

#endif

#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
static ssize_t d3cold_allowed_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;

if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;

pdev->d3cold_allowed = !!val;
pm_runtime_resume(dev);

return count;
}

static ssize_t d3cold_allowed_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct pci_dev *pdev = to_pci_dev(dev);
return sprintf (buf, "%u\n", pdev->d3cold_allowed);
}
#endif

struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
__ATTR_RO(vendor),
Expand All @@ -401,6 +427,9 @@ struct device_attribute pci_dev_attrs[] = {
#ifdef CONFIG_HOTPLUG
__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store),
#endif
#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI)
__ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store),
#endif
__ATTR_NULL,
};
Expand Down
Loading

0 comments on commit 35e7f73

Please sign in to comment.