[ACPI] PCI can now get suspend state from firmware
authorDavid Shaohua Li <shaohua.li@intel.com>
Sat, 19 Mar 2005 05:15:48 +0000 (00:15 -0500)
committerLen Brown <len.brown@intel.com>
Tue, 12 Jul 2005 03:46:10 +0000 (23:46 -0400)
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>
drivers/pci/pci-acpi.c
drivers/pci/pci.c
drivers/pci/pci.h

index f94c86fbc6695cb0e8662d253c23341c3977559f..8eb599708de8b9053bddeb88419da294eec8f9b8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
@@ -17,6 +17,7 @@
 #include <acpi/acpi_bus.h>
 
 #include <linux/pci-acpi.h>
+#include "pci.h"
 
 static u32 ctrlset_buf[3] = {0, 0, 0};
 static u32 global_ctrlsets = 0;
@@ -209,6 +210,49 @@ acpi_status pci_osc_control_set(u32 flags)
 }
 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 */
 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);
        if (ret)
                return 0;
+       platform_pci_choose_state = acpi_pci_choose_state;
        return 0;
 }
 arch_initcall(pci_acpi_init);
index f04b9ffe41539a1a2a1acafa6ea5d16b15fbd64b..5af9418077850d8356e02c5d1abd19af92c204c2 100644 (file)
@@ -304,6 +304,8 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
        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
  * @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)
 {
+       int ret;
+
        if (!pci_find_capability(dev, PCI_CAP_ID_PM))
                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 3: return PCI_D3hot;
        default:
index 744da0d4ae5f90614a1f719299d23e23854c749e..25c44922f7dbc893044a0a3679eb167164851fa7 100644 (file)
@@ -11,6 +11,9 @@ extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
                                  void (*alignf)(void *, struct resource *,
                                                 unsigned long, unsigned long),
                                  void *alignf_data);
+/* Firmware callbacks */
+extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
+
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
 extern int pci_proc_attach_device(struct pci_dev *dev);