aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci-acpi.c47
-rw-r--r--drivers/pci/pci.c11
-rw-r--r--drivers/pci/pci.h3
3 files changed, 59 insertions, 2 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index f94c86fbc669..8eb599708de8 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * File: pci-acpi.c 2 * File: pci-acpi.c
3 * Purpose: Provide PCI support in ACPI 3 * Purpose: Provde PCI support in ACPI
4 * 4 *
5 * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com> 5 * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
6 * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com> 6 * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
@@ -17,6 +17,7 @@
17#include <acpi/acpi_bus.h> 17#include <acpi/acpi_bus.h>
18 18
19#include <linux/pci-acpi.h> 19#include <linux/pci-acpi.h>
20#include "pci.h"
20 21
21static u32 ctrlset_buf[3] = {0, 0, 0}; 22static u32 ctrlset_buf[3] = {0, 0, 0};
22static u32 global_ctrlsets = 0; 23static u32 global_ctrlsets = 0;
@@ -209,6 +210,49 @@ acpi_status pci_osc_control_set(u32 flags)
209} 210}
210EXPORT_SYMBOL(pci_osc_control_set); 211EXPORT_SYMBOL(pci_osc_control_set);
211 212
213/*
214 * _SxD returns the D-state with the highest power
215 * (lowest D-state number) supported in the S-state "x".
216 *
217 * If the devices does not have a _PRW
218 * (Power Resources for Wake) supporting system wakeup from "x"
219 * then the OS is free to choose a lower power (higher number
220 * D-state) than the return value from _SxD.
221 *
222 * But if _PRW is enabled at S-state "x", the OS
223 * must not choose a power lower than _SxD --
224 * unless the device has an _SxW method specifying
225 * the lowest power (highest D-state number) the device
226 * may enter while still able to wake the system.
227 *
228 * ie. depending on global OS policy:
229 *
230 * if (_PRW at S-state x)
231 * choose from highest power _SxD to lowest power _SxW
232 * else // no _PRW at S-state x
233 * choose highest power _SxD or any lower power
234 *
235 * currently we simply return _SxD, if present.
236 */
237
238static int acpi_pci_choose_state(struct pci_dev *pdev, pm_message_t state)
239{
240 char dstate_str[] = "_S0D";
241 acpi_status status;
242 unsigned long val;
243 struct device *dev = &pdev->dev;
244
245 /* Fixme: the check is wrong after pm_message_t is a struct */
246 if ((state >= PM_SUSPEND_MAX) || !DEVICE_ACPI_HANDLE(dev))
247 return -EINVAL;
248 dstate_str[2] += state; /* _S1D, _S2D, _S3D, _S4D */
249 status = acpi_evaluate_integer(DEVICE_ACPI_HANDLE(dev), dstate_str,
250 NULL, &val);
251 if (ACPI_SUCCESS(status))
252 return val;
253 return -ENODEV;
254}
255
212/* ACPI bus type */ 256/* ACPI bus type */
213static int pci_acpi_find_device(struct device *dev, acpi_handle *handle) 257static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
214{ 258{
@@ -255,6 +299,7 @@ static int __init pci_acpi_init(void)
255 ret = register_acpi_bus_type(&pci_acpi_bus); 299 ret = register_acpi_bus_type(&pci_acpi_bus);
256 if (ret) 300 if (ret)
257 return 0; 301 return 0;
302 platform_pci_choose_state = acpi_pci_choose_state;
258 return 0; 303 return 0;
259} 304}
260arch_initcall(pci_acpi_init); 305arch_initcall(pci_acpi_init);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f04b9ffe4153..5af941807785 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -304,6 +304,8 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
304 return 0; 304 return 0;
305} 305}
306 306
307int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state) = NULL;
308
307/** 309/**
308 * pci_choose_state - Choose the power state of a PCI device 310 * pci_choose_state - Choose the power state of a PCI device
309 * @dev: PCI device to be suspended 311 * @dev: PCI device to be suspended
@@ -316,10 +318,17 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
316 318
317pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) 319pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
318{ 320{
321 int ret;
322
319 if (!pci_find_capability(dev, PCI_CAP_ID_PM)) 323 if (!pci_find_capability(dev, PCI_CAP_ID_PM))
320 return PCI_D0; 324 return PCI_D0;
321 325
322 switch (state) { 326 if (platform_pci_choose_state) {
327 ret = platform_pci_choose_state(dev, state);
328 if (ret >= 0)
329 state = ret;
330 }
331 switch (state) {
323 case 0: return PCI_D0; 332 case 0: return PCI_D0;
324 case 3: return PCI_D3hot; 333 case 3: return PCI_D3hot;
325 default: 334 default:
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 744da0d4ae5f..25c44922f7db 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -11,6 +11,9 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
11 void (*alignf)(void *, struct resource *, 11 void (*alignf)(void *, struct resource *,
12 unsigned long, unsigned long), 12 unsigned long, unsigned long),
13 void *alignf_data); 13 void *alignf_data);
14/* Firmware callbacks */
15extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
16
14/* PCI /proc functions */ 17/* PCI /proc functions */
15#ifdef CONFIG_PROC_FS 18#ifdef CONFIG_PROC_FS
16extern int pci_proc_attach_device(struct pci_dev *dev); 19extern int pci_proc_attach_device(struct pci_dev *dev);