Re: Status with pmdisk/swsusp merge ?
From: Olivier Galibert
Date: Thu Aug 12 2004 - 18:12:50 EST
On Thu, Aug 12, 2004 at 11:04:15PM +0200, Pavel Machek wrote:
> > FWIW I ported the 'old' pmdisk suspend code into swsusp2 and I can
> > suspend, but I can't resume. I haven't got a serial port (its a "modern"
> > laptop.. with parallel, etc but no serial..) so I'm also looking at
> > netconsole, digging around for a line printer, etc..
Can be a problem with a lack of wakeup devices. You could try this
patch (not by me, and considered ugly by its own author iirc) and
fiddle with echo "PWRB" >/proc/acpi/wakeup_devices and friends. It
was necessary (with some additional brutality) for the dell latitude
x300.
OG.
diff -Nur linux-2.6.0-test7/drivers/acpi/power.c linux-2.6.0-test7.new/drivers/acpi/power.c
--- linux-2.6.0-test7/drivers/acpi/power.c 2003-10-09 03:24:06.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/power.c 2003-10-24 23:39:40.000000000 +0800
@@ -176,7 +176,7 @@
}
-static int
+int
acpi_power_on (
acpi_handle handle)
{
@@ -193,7 +193,7 @@
resource->references++;
- if ((resource->references > 1)
+ if ((resource->references > 1)
|| (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
resource->name));
@@ -223,7 +223,7 @@
}
-static int
+int
acpi_power_off_device (
acpi_handle handle)
{
diff -Nur linux-2.6.0-test7/drivers/acpi/scan.c linux-2.6.0-test7.new/drivers/acpi/scan.c
--- linux-2.6.0-test7/drivers/acpi/scan.c 2003-10-09 03:24:04.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/scan.c 2003-10-24 23:39:40.000000000 +0800
@@ -23,6 +23,7 @@
#define ACPI_BUS_DEVICE_NAME "System Bus"
static LIST_HEAD(acpi_device_list);
+LIST_HEAD(acpi_wakeup_device_list);
static spinlock_t acpi_device_lock = SPIN_LOCK_UNLOCKED;
static void acpi_device_release(struct kobject * kobj)
@@ -45,7 +46,6 @@
.ktype = &ktype_acpi_ns,
};
-
static void acpi_device_register(struct acpi_device * device, struct acpi_device * parent)
{
/*
@@ -115,9 +115,6 @@
status = acpi_get_handle(device->handle, "_IRC", &handle);
if (ACPI_SUCCESS(status))
device->power.flags.inrush_current = 1;
- status = acpi_get_handle(device->handle, "_PRW", &handle);
- if (ACPI_SUCCESS(status))
- device->power.flags.wake_capable = 1;
/*
* Enumerate supported power management states
@@ -163,7 +160,83 @@
return 0;
}
+static acpi_status
+acpi_bus_extract_wakeup_device_power_package (
+ struct acpi_device *device,
+ union acpi_object *package)
+{
+ int i = 0;
+ union acpi_object *element = NULL;
+
+ if (!device || !package || (package->package.count < 2))
+ return AE_BAD_PARAMETER;
+
+ element = &(package->package.elements[0]);
+ if (element->type != ACPI_TYPE_INTEGER) {
+ /* TBD: support this feature*/
+ return AE_BAD_DATA;
+ }
+ device->wakeup_power.gpe = element->integer.value;
+
+ element = &(package->package.elements[1]);
+ if (element->type != ACPI_TYPE_INTEGER) {
+ return AE_BAD_DATA;
+ }
+ device->wakeup_power.sleep_state = element->integer.value;
+
+ if ((package->package.count - 2) > ACPI_MAX_HANDLES) {
+ return AE_NO_MEMORY;
+ }
+ device->wakeup_power.resources.count = package->package.count - 2;
+ for (i=0; i < device->wakeup_power.resources.count; i++) {
+ element = &(package->package.elements[i + 2]);
+ if (element->type != ACPI_TYPE_ANY ) {
+ return AE_BAD_DATA;
+ }
+
+ device->wakeup_power.resources.handles[i] = element->reference.handle;
+ }
+
+ return AE_OK;
+}
+
+static int
+acpi_bus_get_wakeup_device_flags (
+ struct acpi_device *device)
+{
+ acpi_status status = 0;
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *package = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_bus_get_wakeup_flags");
+ /* _PRW */
+ status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRW\n"));
+ goto end;
+ }
+
+ package = (union acpi_object *) buffer.pointer;
+ status = acpi_bus_extract_wakeup_device_power_package(device, package);
+ if (ACPI_FAILURE(status)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _PRW package\n"));
+ goto end;
+ }
+
+ acpi_os_free(buffer.pointer);
+
+ /*TBD: if exist PSW*/
+
+ /* TBD: lock */
+ INIT_LIST_HEAD(&device->wakeup_list);
+ list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
+
+end:
+ if (ACPI_FAILURE(status))
+ device->flags.wake_capable = 0;
+ return 0;
+}
/* --------------------------------------------------------------------------
Performance Management
-------------------------------------------------------------------------- */
@@ -464,6 +537,11 @@
if (ACPI_SUCCESS(status))
device->flags.power_manageable = 1;
+ /* Presence of _PRW indicates wake capable */
+ status = acpi_get_handle(device->handle, "_PRW", &temp);
+ if (ACPI_SUCCESS(status))
+ device->flags.wake_capable = 1;
+
/* TBD: Peformance management */
return_VALUE(0);
@@ -736,6 +814,16 @@
}
/*
+ *Wakeup device management
+ *-----------------------
+ */
+ if (device->flags.wake_capable) {
+ result = acpi_bus_get_wakeup_device_flags(device);
+ if (result)
+ goto end;
+ }
+
+ /*
* Performance Management
* ----------------------
*/
diff -Nur linux-2.6.0-test7/drivers/acpi/sleep/main.c linux-2.6.0-test7.new/drivers/acpi/sleep/main.c
--- linux-2.6.0-test7/drivers/acpi/sleep/main.c 2003-10-09 03:24:00.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/sleep/main.c 2003-10-24 23:39:40.000000000 +0800
@@ -56,6 +56,9 @@
(acpi_physical_address) acpi_wakeup_address);
}
ACPI_FLUSH_CPU_CACHE();
+
+ /* change a position for the function? */
+ acpi_enable_wakeup_device_prep(acpi_state);
acpi_enter_sleep_state_prep(acpi_state);
return 0;
}
@@ -87,6 +90,9 @@
local_irq_save(flags);
+ /* change a position for the function? */
+ acpi_enable_wakeup_device(acpi_state);
+
switch (state)
{
case PM_SUSPEND_STANDBY:
@@ -133,7 +139,10 @@
static int acpi_pm_finish(u32 state)
{
- acpi_leave_sleep_state(state);
+ u32 acpi_state = acpi_suspend_states[state];
+
+ acpi_leave_sleep_state(acpi_state);
+ acpi_disable_wakeup_device_power(acpi_state);
/* reset firmware waking vector */
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
diff -Nur linux-2.6.0-test7/drivers/acpi/sleep/Makefile linux-2.6.0-test7.new/drivers/acpi/sleep/Makefile
--- linux-2.6.0-test7/drivers/acpi/sleep/Makefile 2003-10-09 03:24:27.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/sleep/Makefile 2003-10-24 23:39:40.000000000 +0800
@@ -1,5 +1,5 @@
obj-y := poweroff.o
-obj-$(CONFIG_ACPI_SLEEP) += main.o
+obj-$(CONFIG_ACPI_SLEEP) += main.o wakeup.o
obj-$(CONFIG_ACPI_SLEEP_PROC_FS) += proc.o
EXTRA_CFLAGS += $(ACPI_CFLAGS)
diff -Nur linux-2.6.0-test7/drivers/acpi/sleep/proc.c linux-2.6.0-test7.new/drivers/acpi/sleep/proc.c
--- linux-2.6.0-test7/drivers/acpi/sleep/proc.c 2003-10-09 03:24:17.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/sleep/proc.c 2003-10-24 23:39:40.000000000 +0800
@@ -15,6 +15,7 @@
#define ACPI_SYSTEM_FILE_SLEEP "sleep"
#define ACPI_SYSTEM_FILE_ALARM "alarm"
+#define ACPI_SYSTEM_FILE_WAKEUP_DEVICE "wakeup_devices"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep")
@@ -352,6 +353,65 @@
return_VALUE(result ? result : count);
}
+extern struct list_head acpi_wakeup_device_list;
+static int
+acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
+{
+ struct list_head * node, * next;
+
+ seq_printf(seq, "Device Speep state Status\n");
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ seq_printf(seq, "%4s %4d %8s\n",
+ dev->pnp.bus_id, (u32) dev->wakeup_power.sleep_state,
+ dev->wakeup_power.state ? "enabled" : "disabled");
+ }
+ return 0;
+}
+
+static int
+acpi_system_write_wakeup_device (
+ struct file *file,
+ const char *buffer,
+ size_t count,
+ loff_t *ppos)
+{
+ struct list_head * node, * next;
+ char strbuf[5];
+ char str[5] = "";
+ int len = count;
+
+ if (len > 4) len = 4;
+
+ if (copy_from_user(strbuf, buffer, len))
+ return -EFAULT;
+ strbuf[len] = '\0';
+ sscanf(strbuf, "%s", str);
+
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ if (!strncmp(dev->pnp.bus_id, str, 4)) {
+ dev->wakeup_power.state = dev->wakeup_power.state ? 0 : 1;
+ }
+ }
+ return count;
+}
+
+static int
+acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
+{
+ return single_open(file, acpi_system_wakeup_device_seq_show, PDE(inode)->data);
+}
+
+static struct file_operations acpi_system_wakeup_device_fops = {
+ .open = acpi_system_wakeup_device_open_fs,
+ .read = seq_read,
+ .write = acpi_system_write_wakeup_device,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
static struct file_operations acpi_system_sleep_fops = {
.open = acpi_system_sleep_open_fs,
@@ -385,6 +445,13 @@
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
if (entry)
entry->proc_fops = &acpi_system_alarm_fops;
+
+ /* 'wakeup device' [R/W]*/
+ entry = create_proc_entry(ACPI_SYSTEM_FILE_WAKEUP_DEVICE,
+ S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
+ if (entry)
+ entry->proc_fops = &acpi_system_wakeup_device_fops;
+
return 0;
}
diff -Nur linux-2.6.0-test7/drivers/acpi/sleep/sleep.h linux-2.6.0-test7.new/drivers/acpi/sleep/sleep.h
--- linux-2.6.0-test7/drivers/acpi/sleep/sleep.h 2003-10-09 03:24:26.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/sleep/sleep.h 2003-10-24 23:39:40.000000000 +0800
@@ -1,4 +1,7 @@
extern u8 sleep_states[];
extern int acpi_suspend (u32 state);
+void acpi_enable_wakeup_device_prep(u8 sleep_state);
+void acpi_enable_wakeup_device(u8 sleep_state);
+void acpi_disable_wakeup_device_power (u8 sleep_state);
diff -Nur linux-2.6.0-test7/drivers/acpi/sleep/wakeup.c linux-2.6.0-test7.new/drivers/acpi/sleep/wakeup.c
--- linux-2.6.0-test7/drivers/acpi/sleep/wakeup.c 1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.0-test7.new/drivers/acpi/sleep/wakeup.c 2003-10-28 14:38:18.316108568 +0800
@@ -0,0 +1,157 @@
+/*
+* wakeup.c - support wakeup devices
+*/
+
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <acpi/acevents.h>
+
+#define _COMPONENT ACPI_SYSTEM_COMPONENT
+ACPI_MODULE_NAME ("wakeup_devices")
+
+/*
+* acpi_enable_wakeup_device_prep
+*/
+extern struct list_head acpi_wakeup_device_list;
+extern int acpi_power_on (acpi_handle handle);
+extern int acpi_power_off_device (acpi_handle handle);
+
+void
+acpi_enable_wakeup_device_prep(
+ u8 sleep_state)
+{
+ struct list_head * node, * next;
+ union acpi_object arg = {ACPI_TYPE_INTEGER};
+ struct acpi_object_list arg_list = {1, &arg};
+ acpi_status status = AE_OK;
+ int i;
+ int ret = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_prep");
+ arg.integer.value = 1;
+
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ if (dev->wakeup_power.state && (sleep_state >= (u32) dev->wakeup_power.sleep_state)) {
+ /* Open power resource */
+ for (i = 0; i < dev->wakeup_power.resources.count; i++) {
+ ret = acpi_power_on(dev->wakeup_power.resources.handles[i]);
+ if (ret) {
+ /* TBD: reverse changes */
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error transition power state\n"));
+ dev->wakeup_power.state = 0;
+ continue;
+ }
+ }
+
+ /* Execute PSW */
+ status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
+ if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+ /* TBD; reverse changes */
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n"));
+ dev->wakeup_power.state = 0;
+ continue;
+ }
+ }
+ }
+}
+
+/*
+* acpi_enable_wakeup_device
+*/
+void
+acpi_enable_wakeup_device(
+ u8 sleep_state)
+{
+ struct list_head * node, * next;
+ struct acpi_gpe_event_info * gpe_info = NULL;
+
+ ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device");
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ if (dev->wakeup_power.state && (sleep_state >= (u32) dev->wakeup_power.sleep_state)) {
+ /* TBD: support GPE block device */
+ gpe_info = acpi_ev_get_gpe_event_info(NULL, (u32) dev->wakeup_power.gpe);
+ if (!gpe_info) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error GPE info\n"));
+ continue;
+ }
+ acpi_hw_enable_gpe_for_wakeup(gpe_info);
+ acpi_hw_clear_gpe(gpe_info);
+ acpi_hw_enable_gpe(gpe_info);
+ }
+ }
+}
+
+/*
+* acpi_disable_wakeup_device_power
+*/
+void
+acpi_disable_wakeup_device_power (
+ u8 sleep_state)
+{
+ struct list_head * node, * next;
+ struct acpi_gpe_event_info * gpe_info = NULL;
+ union acpi_object arg = {ACPI_TYPE_INTEGER};
+ struct acpi_object_list arg_list = {1, &arg};
+ acpi_status status = AE_OK;
+ int i;
+ int ret = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device_power");
+ arg.integer.value = 0;
+
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ if (dev->wakeup_power.state && (sleep_state >= (u32) dev->wakeup_power.sleep_state)) {
+ /* Open power resource */
+ for (i = 0; i < dev->wakeup_power.resources.count; i++) {
+ ret = acpi_power_off_device(dev->wakeup_power.resources.handles[i]);
+ if (ret) {
+ /* TBD: reverse changes */
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error transition power state\n"));
+ dev->wakeup_power.state = 0;
+ continue;
+ }
+ }
+
+ /* Execute PSW */
+ status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
+ if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
+ /* TBD; reverse changes */
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n"));
+ dev->wakeup_power.state = 0;
+ continue;
+ }
+ /* TBD: support GPE block device */
+ gpe_info = acpi_ev_get_gpe_event_info(NULL, (u32) dev->wakeup_power.gpe);
+ if (!gpe_info) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error GPE info\n"));
+ continue;
+ }
+ acpi_hw_disable_gpe_for_wakeup(gpe_info);
+ }
+ }
+
+}
+
+static int __init acpi_wakeup_device_init()
+{
+ struct list_head * node, * next;
+
+ printk("Wake capable device:");
+ list_for_each_safe(node, next, &acpi_wakeup_device_list) {
+ struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list);
+
+ printk(" %4s", dev->pnp.bus_id);
+ }
+ printk("\n");
+
+ return 0;
+}
+
+late_initcall(acpi_wakeup_device_init);
diff -Nur linux-2.6.0-test7/include/acpi/acpi_bus.h linux-2.6.0-test7.new/include/acpi/acpi_bus.h
--- linux-2.6.0-test7/include/acpi/acpi_bus.h 2003-10-09 03:24:04.000000000 +0800
+++ linux-2.6.0-test7.new/include/acpi/acpi_bus.h 2003-10-24 23:39:40.000000000 +0800
@@ -159,7 +159,8 @@
u32 suprise_removal_ok:1;
u32 power_manageable:1;
u32 performance_manageable:1;
- u32 reserved:21;
+ u32 wake_capable:1;
+ u32 reserved:20;
};
@@ -205,10 +206,8 @@
u32 explicit_get:1; /* _PSC present? */
u32 power_resources:1; /* Power resources */
u32 inrush_current:1; /* Serialize Dx->D0 */
- u32 wake_capable:1; /* Wakeup supported? */
- u32 wake_enabled:1; /* Enabled for wakeup */
u32 power_removed:1; /* Optimize Dx->D0 */
- u32 reserved:26;
+ u32 reserved:28;
};
struct acpi_device_power_state {
@@ -228,6 +227,14 @@
struct acpi_device_power_state states[4]; /* Power states (D0-D3) */
};
+/* Wakeup Management */
+
+struct acpi_wakeup_power {
+ acpi_integer gpe;
+ int state;
+ acpi_integer sleep_state;
+ struct acpi_handle_list resources;
+};
/* Performance Management */
@@ -261,10 +268,12 @@
struct list_head children;
struct list_head node;
struct list_head g_list;
+ struct list_head wakeup_list;
struct acpi_device_status status;
struct acpi_device_flags flags;
struct acpi_device_pnp pnp;
struct acpi_device_power power;
+ struct acpi_wakeup_power wakeup_power;
struct acpi_device_perf performance;
struct acpi_device_dir dir;
struct acpi_device_ops ops;