[ACPI] PCI can now get suspend state from firmware
pci_choose_state() can now call platform_pci_choose_state() and ACPI can answer http://bugzilla.kernel.org/show_bug.cgi?id=4277 Signed-off-by: David Shaohua Li <shaohua.li@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
parent
84df749f36
commit
0f64474b8f
3 changed files with 59 additions and 2 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* File: pci-acpi.c
|
* File: pci-acpi.c
|
||||||
* Purpose: Provide PCI support in ACPI
|
* Purpose: Provde PCI support in ACPI
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
|
* Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
|
||||||
* Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
|
* Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
#include <acpi/acpi_bus.h>
|
#include <acpi/acpi_bus.h>
|
||||||
|
|
||||||
#include <linux/pci-acpi.h>
|
#include <linux/pci-acpi.h>
|
||||||
|
#include "pci.h"
|
||||||
|
|
||||||
static u32 ctrlset_buf[3] = {0, 0, 0};
|
static u32 ctrlset_buf[3] = {0, 0, 0};
|
||||||
static u32 global_ctrlsets = 0;
|
static u32 global_ctrlsets = 0;
|
||||||
|
@ -209,6 +210,49 @@ acpi_status pci_osc_control_set(u32 flags)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_osc_control_set);
|
EXPORT_SYMBOL(pci_osc_control_set);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _SxD returns the D-state with the highest power
|
||||||
|
* (lowest D-state number) supported in the S-state "x".
|
||||||
|
*
|
||||||
|
* If the devices does not have a _PRW
|
||||||
|
* (Power Resources for Wake) supporting system wakeup from "x"
|
||||||
|
* then the OS is free to choose a lower power (higher number
|
||||||
|
* D-state) than the return value from _SxD.
|
||||||
|
*
|
||||||
|
* But if _PRW is enabled at S-state "x", the OS
|
||||||
|
* must not choose a power lower than _SxD --
|
||||||
|
* unless the device has an _SxW method specifying
|
||||||
|
* the lowest power (highest D-state number) the device
|
||||||
|
* may enter while still able to wake the system.
|
||||||
|
*
|
||||||
|
* ie. depending on global OS policy:
|
||||||
|
*
|
||||||
|
* if (_PRW at S-state x)
|
||||||
|
* choose from highest power _SxD to lowest power _SxW
|
||||||
|
* else // no _PRW at S-state x
|
||||||
|
* choose highest power _SxD or any lower power
|
||||||
|
*
|
||||||
|
* currently we simply return _SxD, if present.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int acpi_pci_choose_state(struct pci_dev *pdev, pm_message_t state)
|
||||||
|
{
|
||||||
|
char dstate_str[] = "_S0D";
|
||||||
|
acpi_status status;
|
||||||
|
unsigned long val;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
|
/* Fixme: the check is wrong after pm_message_t is a struct */
|
||||||
|
if ((state >= PM_SUSPEND_MAX) || !DEVICE_ACPI_HANDLE(dev))
|
||||||
|
return -EINVAL;
|
||||||
|
dstate_str[2] += state; /* _S1D, _S2D, _S3D, _S4D */
|
||||||
|
status = acpi_evaluate_integer(DEVICE_ACPI_HANDLE(dev), dstate_str,
|
||||||
|
NULL, &val);
|
||||||
|
if (ACPI_SUCCESS(status))
|
||||||
|
return val;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
/* ACPI bus type */
|
/* ACPI bus type */
|
||||||
static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
|
static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
|
||||||
{
|
{
|
||||||
|
@ -255,6 +299,7 @@ static int __init pci_acpi_init(void)
|
||||||
ret = register_acpi_bus_type(&pci_acpi_bus);
|
ret = register_acpi_bus_type(&pci_acpi_bus);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 0;
|
return 0;
|
||||||
|
platform_pci_choose_state = acpi_pci_choose_state;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
arch_initcall(pci_acpi_init);
|
arch_initcall(pci_acpi_init);
|
||||||
|
|
|
@ -304,6 +304,8 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state) = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_choose_state - Choose the power state of a PCI device
|
* pci_choose_state - Choose the power state of a PCI device
|
||||||
* @dev: PCI device to be suspended
|
* @dev: PCI device to be suspended
|
||||||
|
@ -316,10 +318,17 @@ 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)
|
pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!pci_find_capability(dev, PCI_CAP_ID_PM))
|
if (!pci_find_capability(dev, PCI_CAP_ID_PM))
|
||||||
return PCI_D0;
|
return PCI_D0;
|
||||||
|
|
||||||
switch (state) {
|
if (platform_pci_choose_state) {
|
||||||
|
ret = platform_pci_choose_state(dev, state);
|
||||||
|
if (ret >= 0)
|
||||||
|
state = ret;
|
||||||
|
}
|
||||||
|
switch (state) {
|
||||||
case 0: return PCI_D0;
|
case 0: return PCI_D0;
|
||||||
case 3: return PCI_D3hot;
|
case 3: return PCI_D3hot;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -11,6 +11,9 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
|
||||||
void (*alignf)(void *, struct resource *,
|
void (*alignf)(void *, struct resource *,
|
||||||
unsigned long, unsigned long),
|
unsigned long, unsigned long),
|
||||||
void *alignf_data);
|
void *alignf_data);
|
||||||
|
/* Firmware callbacks */
|
||||||
|
extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
|
||||||
|
|
||||||
/* PCI /proc functions */
|
/* PCI /proc functions */
|
||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
extern int pci_proc_attach_device(struct pci_dev *dev);
|
extern int pci_proc_attach_device(struct pci_dev *dev);
|
||||||
|
|
Loading…
Reference in a new issue