From patchwork Mon Aug 31 21:24:20 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rafael Wysocki X-Patchwork-Id: 44927 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n7VLNjUJ005761 for ; Mon, 31 Aug 2009 21:23:45 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751039AbZHaVXl (ORCPT ); Mon, 31 Aug 2009 17:23:41 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1750985AbZHaVXl (ORCPT ); Mon, 31 Aug 2009 17:23:41 -0400 Received: from ogre.sisk.pl ([217.79.144.158]:53988 "EHLO ogre.sisk.pl" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750929AbZHaVXk (ORCPT ); Mon, 31 Aug 2009 17:23:40 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by ogre.sisk.pl (Postfix) with ESMTP id 37551155860; Mon, 31 Aug 2009 20:23:12 +0200 (CEST) Received: from ogre.sisk.pl ([127.0.0.1]) by localhost (ogre.sisk.pl [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 01102-09; Mon, 31 Aug 2009 20:22:50 +0200 (CEST) Received: from tosh.localnet (220-bem-13.acn.waw.pl [82.210.184.220]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by ogre.sisk.pl (Postfix) with ESMTP id 8FEFA1547C8; Mon, 31 Aug 2009 20:22:50 +0200 (CEST) From: "Rafael J. Wysocki" To: linux-pm@lists.linux-foundation.org Subject: [RFC][PATCH update] ACPI / PM: Allow PCI root bridges to wake up the system Date: Mon, 31 Aug 2009 23:24:20 +0200 User-Agent: KMail/1.12.0 (Linux/2.6.31-rc8-rjw; KDE/4.3.0; x86_64; ; ) Cc: Linux PCI , Henrique de Moraes Holschuh , ACPI Devel Maling List , Jesse Barnes References: <200908300041.14541.rjw@sisk.pl> <200908312141.00737.rjw@sisk.pl> In-Reply-To: <200908312141.00737.rjw@sisk.pl> MIME-Version: 1.0 Message-Id: <200908312324.20850.rjw@sisk.pl> X-Virus-Scanned: amavisd-new at ogre.sisk.pl using MkS_Vir for Linux Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org On Monday 31 August 2009, Rafael J. Wysocki wrote: > On Sunday 30 August 2009, Rafael J. Wysocki wrote: > > From: Rafael J. Wysocki > > > > If PCI root bridge is declared in the ACPI tables as a wake-up > > device, allow it to wake up the system by default. > > > > This allows add-on PCI devices to work as wake-up devices on some > > systems, where PME# asserted by an add-on device causes the root > > bridge GPE to generate a wake-up event, without using > > /proc/acpi/wakeup to change the root bridge wake-up setting. Updated patch is appended, but there's a problem with it. Namely, when the root bridge GPE is shared with other devices, those devices are also enabled to wake up and they may be kind of "interesting" (on one of my test boxes they include the ethernet and audio adapters). I'm not sure what to do about that. Thanks, Rafael --- From: Rafael J. Wysocki Subject: ACPI / PM: Allow PCI root bridges to wake up the system If PCI root bridge is declared in the ACPI tables as a wake-up device, allow it to wake up the system by default. This allows add-on PCI devices to work as wake-up devices on some systems, where PME# asserted by an add-on device causes the root bridge GPE to generate a wake-up event, without using /proc/acpi/wakeup to change the root bridge wake-up setting. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/glue.c | 9 +++++++++ drivers/acpi/pci_root.c | 25 +++++++++++++------------ drivers/acpi/proc.c | 29 +---------------------------- drivers/acpi/sleep.h | 2 ++ drivers/acpi/wakeup.c | 35 +++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 1 + 6 files changed, 61 insertions(+), 40 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Index: linux-2.6/drivers/acpi/glue.c =================================================================== --- linux-2.6.orig/drivers/acpi/glue.c +++ linux-2.6/drivers/acpi/glue.c @@ -13,6 +13,8 @@ #include #include +#include "sleep.h" + #define ACPI_GLUE_DEBUG 0 #if ACPI_GLUE_DEBUG #define DBG(x...) printk(PREFIX x) @@ -168,6 +170,13 @@ static int acpi_bind_one(struct device * "physical_node"); if (acpi_dev->wakeup.flags.valid) { device_set_wakeup_capable(dev, true); + /* Allow PCI root bridges to wake up the system. */ + if (acpi_dev_is_root_bridge(acpi_dev)) { + mutex_lock(&acpi_device_lock); + acpi_dev->wakeup.state.enabled = true; + propagate_enable_wakeup(acpi_dev); + mutex_unlock(&acpi_device_lock); + } device_set_wakeup_enable(dev, acpi_dev->wakeup.state.enabled); } Index: linux-2.6/drivers/acpi/pci_root.c =================================================================== --- linux-2.6.orig/drivers/acpi/pci_root.c +++ linux-2.6/drivers/acpi/pci_root.c @@ -138,26 +138,27 @@ acpi_handle acpi_get_pci_rootbridge_hand EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); /** + * acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge + * @device: ACPI device to check. + */ +bool acpi_dev_is_root_bridge(struct acpi_device *device) +{ + return !acpi_match_device_ids(device, root_device_ids); +} + +/** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge - * @handle - the ACPI CA node in question. - * - * Note: we could make this API take a struct acpi_device * instead, but - * for now, it's more convenient to operate on an acpi_handle. + * @handle: the ACPI CA node in question. */ int acpi_is_root_bridge(acpi_handle handle) { int ret; struct acpi_device *device; - ret = acpi_bus_get_device(handle, &device); + ret = !acpi_bus_get_device(handle, &device); if (ret) - return 0; - - ret = acpi_match_device_ids(device, root_device_ids); - if (ret) - return 0; - else - return 1; + ret = acpi_dev_is_root_bridge(device); + return ret; } EXPORT_SYMBOL_GPL(acpi_is_root_bridge); Index: linux-2.6/include/acpi/acpi_bus.h =================================================================== --- linux-2.6.orig/include/acpi/acpi_bus.h +++ linux-2.6/include/acpi/acpi_bus.h @@ -371,6 +371,7 @@ struct device *acpi_get_physical_device( /* helper */ acpi_handle acpi_get_child(acpi_handle, acpi_integer); +bool acpi_dev_is_root_bridge(struct acpi_device *); int acpi_is_root_bridge(acpi_handle); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle)) Index: linux-2.6/drivers/acpi/proc.c =================================================================== --- linux-2.6.orig/drivers/acpi/proc.c +++ linux-2.6/drivers/acpi/proc.c @@ -377,14 +377,6 @@ acpi_system_wakeup_device_seq_show(struc return 0; } -static void physical_device_enable_wakeup(struct acpi_device *adev) -{ - struct device *dev = acpi_get_physical_device(adev->handle); - - if (dev && device_can_wakeup(dev)) - device_set_wakeup_enable(dev, adev->wakeup.state.enabled); -} - static ssize_t acpi_system_write_wakeup_device(struct file *file, const char __user * buffer, @@ -420,26 +412,7 @@ acpi_system_write_wakeup_device(struct f } if (found_dev) { physical_device_enable_wakeup(found_dev); - list_for_each_safe(node, next, &acpi_wakeup_device_list) { - struct acpi_device *dev = container_of(node, - struct - acpi_device, - wakeup_list); - - if ((dev != found_dev) && - (dev->wakeup.gpe_number == - found_dev->wakeup.gpe_number) - && (dev->wakeup.gpe_device == - found_dev->wakeup.gpe_device)) { - printk(KERN_WARNING - "ACPI: '%s' and '%s' have the same GPE, " - "can't disable/enable one seperately\n", - dev->pnp.bus_id, found_dev->pnp.bus_id); - dev->wakeup.state.enabled = - found_dev->wakeup.state.enabled; - physical_device_enable_wakeup(dev); - } - } + propagate_enable_wakeup(found_dev); } mutex_unlock(&acpi_device_lock); return count; Index: linux-2.6/drivers/acpi/wakeup.c =================================================================== --- linux-2.6.orig/drivers/acpi/wakeup.c +++ linux-2.6/drivers/acpi/wakeup.c @@ -124,6 +124,41 @@ void acpi_disable_wakeup_device(u8 sleep } } +void physical_device_enable_wakeup(struct acpi_device *adev) +{ + struct device *dev = acpi_get_physical_device(adev->handle); + + if (dev && device_can_wakeup(dev)) + device_set_wakeup_enable(dev, adev->wakeup.state.enabled); +} + +void propagate_enable_wakeup(struct acpi_device *wakeup_dev) +{ + struct list_head *node, *next; + + 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.flags.valid) + continue; + + if (dev == wakeup_dev) + continue; + + if (dev->wakeup.gpe_number != wakeup_dev->wakeup.gpe_number + || dev->wakeup.gpe_device != wakeup_dev->wakeup.gpe_device) + continue; + + printk(KERN_WARNING "ACPI: '%s' and '%s' share a GPE, " + "unable to disable/enable one seperately\n", + dev->pnp.bus_id, wakeup_dev->pnp.bus_id); + + dev->wakeup.state.enabled = wakeup_dev->wakeup.state.enabled; + physical_device_enable_wakeup(dev); + } +} + int __init acpi_wakeup_device_init(void) { struct list_head *node, *next; Index: linux-2.6/drivers/acpi/sleep.h =================================================================== --- linux-2.6.orig/drivers/acpi/sleep.h +++ linux-2.6/drivers/acpi/sleep.h @@ -5,6 +5,8 @@ extern int acpi_suspend (u32 state); extern void acpi_enable_wakeup_device_prep(u8 sleep_state); extern void acpi_enable_wakeup_device(u8 sleep_state); extern void acpi_disable_wakeup_device(u8 sleep_state); +extern void physical_device_enable_wakeup(struct acpi_device *adev); +extern void propagate_enable_wakeup(struct acpi_device *wakeup_dev); extern struct list_head acpi_wakeup_device_list; extern struct mutex acpi_device_lock;