From: David Brownell <david-b@pacbell.net>
To: linux-pm@lists.linux-foundation.org
Cc: linux-acpi@vger.kernel.org
Subject: [patch 2.6.21-git] pci_choose_state() works, does ACPI magic
Date: Wed, 9 May 2007 12:22:29 -0700 [thread overview]
Message-ID: <200705091222.29561.david-b@pacbell.net> (raw)
Provide new ACPI method tracking the target system state, for use
during suspend() and other PM calls. It returns ACPI_STATE_S0
except during true suspend paths.
Use that to finally implement the platform_pci_choose_state() hook
on ACPI platforms. It calls "_S3D" and similar methods, and uses
the result appropriately.
Fix pci_choose_state() to finally behave sanely too.
Minor whitespace fixes.
Lightly tested -- STR only, with only USB affected by the new code.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
drivers/acpi/sleep/main.c | 29 +++++++++++-
drivers/pci/pci-acpi.c | 107 +++++++++++++++++++++++++++++++++-------------
drivers/pci/pci.c | 51 ++++++++++++---------
include/acpi/acpixf.h | 2
4 files changed, 135 insertions(+), 54 deletions(-)
--- g26.orig/include/acpi/acpixf.h 2007-05-09 08:57:37.000000000 -0700
+++ g26/include/acpi/acpixf.h 2007-05-09 08:58:33.000000000 -0700
@@ -329,6 +329,8 @@ acpi_get_sleep_type_data(u8 sleep_state,
acpi_status acpi_enter_sleep_state_prep(u8 sleep_state);
+int acpi_get_target_sleep_state(void);
+
acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state);
acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void);
--- g26.orig/drivers/acpi/sleep/main.c 2007-05-09 08:57:37.000000000 -0700
+++ g26/drivers/acpi/sleep/main.c 2007-05-09 08:58:33.000000000 -0700
@@ -35,6 +35,20 @@ static u32 acpi_suspend_states[] = {
static int init_8259A_after_S1;
+static u8 acpi_target_sleep_state = ACPI_STATE_S0;
+
+/**
+ * acpi_get_target_sleep_state - return target ACPI S-state
+ *
+ * When used during suspend processing, this returns the target state
+ * such as ACPI_STATE_S3. Otherwise it returns ACPI_STATE_S0.
+ */
+int acpi_get_target_sleep_state(void)
+{
+ return acpi_target_sleep_state;
+}
+/* EXPORT_SYMBOL(acpi_get_target_sleep_state); ... if you need it */
+
/**
* acpi_pm_prepare - Do preliminary suspend work.
* @pm_state: suspend state we're entering.
@@ -50,12 +64,16 @@ extern void acpi_power_off(void);
static int acpi_pm_prepare(suspend_state_t pm_state)
{
u32 acpi_state = acpi_suspend_states[pm_state];
+ int status;
if (!sleep_states[acpi_state]) {
printk("acpi_pm_prepare does not support %d \n", pm_state);
return -EPERM;
}
- return acpi_sleep_prepare(acpi_state);
+ status = acpi_sleep_prepare(acpi_state);
+ if (status == 0)
+ acpi_target_sleep_state = acpi_state;
+ return status;
}
/**
@@ -78,8 +96,10 @@ static int acpi_pm_enter(suspend_state_t
/* Do arch specific saving of state. */
if (pm_state > PM_SUSPEND_STANDBY) {
int error = acpi_save_state_mem();
- if (error)
+ if (error) {
+ acpi_target_sleep_state = ACPI_STATE_S0;
return error;
+ }
}
local_irq_save(flags);
@@ -103,6 +123,10 @@ static int acpi_pm_enter(suspend_state_t
break;
default:
+ /* "should never happen" */
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ local_irq_restore(flags);
+ WARN_ON(1);
return -EINVAL;
}
@@ -113,6 +137,7 @@ static int acpi_pm_enter(suspend_state_t
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
+ acpi_target_sleep_state = ACPI_STATE_S0;
local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n");
--- g26.orig/drivers/pci/pci-acpi.c 2007-05-09 08:57:37.000000000 -0700
+++ g26/drivers/pci/pci-acpi.c 2007-05-09 12:16:25.000000000 -0700
@@ -225,52 +225,99 @@ acpi_status pci_osc_control_set(acpi_han
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".
+ * PCI devices in the ACPI tables may have various PM-related methods,
+ * like _SxD and _SxW (where 'x' is a target S-state).
*
- * 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.
+ * Not all devices are listed in the ACPI tables; PCI/Cardbus/.. addon
+ * devices are not known to ACPI. We ignore those devices. However,
+ * support for PME# depends on the PCI root bridge, which *is* known
+ * to ACPI ... and which may need to enable a GPE if any child devices
+ * are wakeup-enabled.
*
- * 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.
+ * All PM-enabled PCI devices can support PCI_D3.
*
- * 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.
+ * For now, prefer lowest power state unless ACPI suggests otherwise.
*/
-
static int acpi_pci_choose_state(struct pci_dev *pdev, pm_message_t state)
{
- /* TBD */
+ static const u8 state_conv[] = {
+ [ACPI_STATE_D0] = PCI_D0, /* highest power */
+ [ACPI_STATE_D1] = PCI_D1,
+ [ACPI_STATE_D2] = PCI_D2,
+ [ACPI_STATE_D3] = PCI_D3hot,
+ };
+
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&pdev->dev);
+ struct acpi_device *adev;
+ char sxd[] = "_SxD";
+ unsigned long d_min, d_max;
+ int s_state;
+
+ /* Device isn't known to ACPI? */
+ if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev)))
+ return -ENODEV;
+
+ s_state = acpi_get_target_sleep_state();
+ if (s_state > ACPI_STATE_S3)
+ return PCI_D3cold;
+
+ /* Any device can handle D0 and D3; we'll assume that if it can
+ * wake, it can wake from D3 *or* ACPI will tell us.
+ */
+ d_min = ACPI_STATE_D3;
+ d_max = ACPI_STATE_D3;
+
+ /* If present, _SxD methods give the minimum D-state we may use
+ * for each S-state ... with lowest latency state switching.
+ *
+ * We rely on acpi_evaluate_integer() not clobbering the integer
+ * provided -- that's our fault recovery, we ignore retval.
+ */
+ sxd[2] = '0' + s_state;
+ if (s_state > ACPI_STATE_S0)
+ acpi_evaluate_integer(handle, sxd, NULL, &d_min);
+
+ /* If _PRW says we can wake from the upcoming system state, the
+ * _SxD value can wake ... and we'll assume a wakeup-aware driver.
+ * If _SxW methods exist (ACPI 3.x), they give the lowest power
+ * D-state that can also wake the system. _S0W can be valid.
+ */
+ if (device_may_wakeup(&pdev->dev)
+ && adev->wakeup.sleep_state <= s_state) {
+ d_max = d_min;
+ sxd[3] = 'W';
+ acpi_evaluate_integer(handle, sxd, NULL, &d_max);
+ }
- return -ENODEV;
+ /* Use the lowest power option ACPI allows */
+ return state_conv[d_max];
}
static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
{
acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
- static int state_conv[] = {
- [0] = 0,
- [1] = 1,
- [2] = 2,
- [3] = 3,
- [4] = 3
+
+ static const u8 state_conv[] = {
+ [PCI_D0] = ACPI_STATE_D0,
+ [PCI_D1] = ACPI_STATE_D1,
+ [PCI_D2] = ACPI_STATE_D2,
+ [PCI_D3hot] = ACPI_STATE_D3,
+ [PCI_D3cold] = ACPI_STATE_D3
};
- int acpi_state = state_conv[(int __force) state];
if (!handle)
return -ENODEV;
- return acpi_bus_set_power(handle, acpi_state);
+
+ switch (state) {
+ case PCI_D0:
+ case PCI_D1:
+ case PCI_D2:
+ case PCI_D3hot:
+ case PCI_D3cold:
+ return acpi_bus_set_power(handle, state_conv[state]);
+ }
+
+ return -EINVAL;
}
--- g26.orig/drivers/pci/pci.c 2007-05-09 08:57:37.000000000 -0700
+++ g26/drivers/pci/pci.c 2007-05-09 12:17:08.000000000 -0700
@@ -500,43 +500,50 @@ pci_set_power_state(struct pci_dev *dev,
}
int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
-
+
/**
* pci_choose_state - Choose the power state of a PCI device
* @dev: PCI device to be suspended
- * @state: target sleep state for the whole system. This is the value
- * that is passed to suspend() function.
+ * @mesg: The value passed to the suspend() function.
*
* Returns PCI power state suitable for given device and given system
- * message.
+ * power transition.
*/
-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 mesg)
{
- int ret;
-
- if (!pci_find_capability(dev, PCI_CAP_ID_PM))
- return PCI_D0;
-
- if (platform_pci_choose_state) {
- ret = platform_pci_choose_state(dev, state);
- if (ret >= 0)
- state.event = ret;
- }
+ pci_power_t state = PCI_D0;
+ int pos;
- switch (state.event) {
+ switch (mesg.event) {
case PM_EVENT_ON:
- return PCI_D0;
case PM_EVENT_FREEZE:
case PM_EVENT_PRETHAW:
- /* REVISIT both freeze and pre-thaw "should" use D0 */
+ break;
+
+ /* Speed things up by only using PCI power states when going into
+ * a real system suspend state.
+ */
case PM_EVENT_SUSPEND:
- return PCI_D3hot;
+ pos = pci_find_capability(dev, PCI_CAP_ID_PM);
+ if (pos) {
+ int ret = -ENOSYS;
+
+ if (platform_pci_choose_state && !pci_no_d1d2(dev))
+ ret = platform_pci_choose_state(dev, mesg);
+
+ /* FIXME if the device is wakeup-enabled, check its
+ * PM capabilities. Never return a state that can't
+ * issue wakeup events.
+ */
+ if (ret < 0)
+ state = PCI_D3hot;
+ }
+ break;
default:
- printk("Unrecognized suspend event %d\n", state.event);
- BUG();
+ WARN_ON(1);
}
- return PCI_D0;
+ return state;
}
EXPORT_SYMBOL(pci_choose_state);
next reply other threads:[~2007-05-09 19:42 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-05-09 19:22 David Brownell [this message]
2007-05-10 20:23 ` [patch 2.6.21-git] pci_choose_state() works, does ACPI magic Rafael J. Wysocki
2007-05-10 20:35 ` David Brownell
2007-05-10 20:35 ` David Brownell
2007-05-10 20:23 ` Rafael J. Wysocki
2007-05-14 9:39 ` [linux-pm] " Pavel Machek
2007-05-14 15:01 ` David Brownell
2007-05-14 15:01 ` [linux-pm] " David Brownell
2007-05-14 9:39 ` Pavel Machek
2007-05-21 17:34 ` Bjorn Helgaas
2007-05-21 17:55 ` David Brownell
2007-05-21 18:21 ` Bjorn Helgaas
2007-05-21 18:58 ` David Brownell
2007-05-21 18:58 ` David Brownell
2007-05-21 18:21 ` Bjorn Helgaas
2007-05-21 17:55 ` David Brownell
2007-05-21 17:34 ` Bjorn Helgaas
-- strict thread matches above, loose matches on Subject: below --
2007-05-09 19:22 David Brownell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200705091222.29561.david-b@pacbell.net \
--to=david-b@pacbell.net \
--cc=linux-acpi@vger.kernel.org \
--cc=linux-pm@lists.linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.