diff options
author | David Shaohua Li <shaohua.li@intel.com> | 2005-03-19 00:15:48 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2005-07-11 23:46:10 -0400 |
commit | 0f64474b8f7f1f7f3af5b24ef997baa35f923509 (patch) | |
tree | 562570b58ae67bb4d341b2c9ff864a0a64eb4c28 | |
parent | 84df749f364209c9623304b7a94ddb954dc343bb (diff) |
[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>
-rw-r--r-- | drivers/pci/pci-acpi.c | 47 | ||||
-rw-r--r-- | drivers/pci/pci.c | 11 | ||||
-rw-r--r-- | drivers/pci/pci.h | 3 |
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 | ||
21 | static u32 ctrlset_buf[3] = {0, 0, 0}; | 22 | static u32 ctrlset_buf[3] = {0, 0, 0}; |
22 | static u32 global_ctrlsets = 0; | 23 | static u32 global_ctrlsets = 0; |
@@ -209,6 +210,49 @@ acpi_status pci_osc_control_set(u32 flags) | |||
209 | } | 210 | } |
210 | EXPORT_SYMBOL(pci_osc_control_set); | 211 | EXPORT_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 | |||
238 | static 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 */ |
213 | static int pci_acpi_find_device(struct device *dev, acpi_handle *handle) | 257 | static 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 | } |
260 | arch_initcall(pci_acpi_init); | 305 | arch_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 | ||
307 | int (*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 | ||
317 | pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) | 319 | pci_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 */ | ||
15 | extern 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 |
16 | extern int pci_proc_attach_device(struct pci_dev *dev); | 19 | extern int pci_proc_attach_device(struct pci_dev *dev); |