diff mbox

[5/5] acpi-based wakeup event detection

Message ID 1250666661.23178.120.camel@sli10-desk.sh.intel.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Shaohua Li Aug. 19, 2009, 7:24 a.m. UTC
In ACPI platform, if native PME isn't enabled, GPE is used to report wakeup event.
---
 drivers/acpi/Kconfig        |    9 ++++++
 drivers/acpi/bus.c          |   15 +++++++++++
 drivers/acpi/sleep/wakeup.c |   60 ++++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_bus.h     |    4 ++
 4 files changed, 88 insertions(+)



--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Matthew Garrett Aug. 19, 2009, 11:42 a.m. UTC | #1
On Wed, Aug 19, 2009 at 03:24:21PM +0800, Shaohua Li wrote:

> +	ret = acpi_gpe_pme_check(dev);
> +
> +	acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);

I don't think we want to unconditionally disable the GPE.

> +
> +	acpi_init_gpe_pme();

I'm also not keen on haing the notifier being at the ACPI level. Are we 
guaranteed that the GPE will only be used for wakeups, and will never 
trigger any other sort of notification? Keeping this at the bus level 
may be more practical.
Shaohua Li Aug. 20, 2009, 3:16 a.m. UTC | #2
On Wed, Aug 19, 2009 at 07:42:20PM +0800, Matthew Garrett wrote:
> On Wed, Aug 19, 2009 at 03:24:21PM +0800, Shaohua Li wrote:
> 
> > +	ret = acpi_gpe_pme_check(dev);
> > +
> > +	acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
> 
> I don't think we want to unconditionally disable the GPE.
yes, we need something you proposed to add reference for GPE disable/enable.
 
> > +
> > +	acpi_init_gpe_pme();
> 
> I'm also not keen on haing the notifier being at the ACPI level. Are we 
> guaranteed that the GPE will only be used for wakeups, and will never 
> trigger any other sort of notification? Keeping this at the bus level 
> may be more practical.
the notification handler checks if this is a wakeup event.
Because the ACPI wakeup event can be sent to any kind of buses, so move the
code to acpi level can reduce a lot of duplicate code, otherwise you must
implement the same mechanism for every bus.

Thanks,
Shaohua
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael Wysocki Aug. 20, 2009, 8:03 p.m. UTC | #3
On Wednesday 19 August 2009, Shaohua Li wrote:
> 
> In ACPI platform, if native PME isn't enabled, GPE is used to report wakeup event.
> ---
>  drivers/acpi/Kconfig        |    9 ++++++
>  drivers/acpi/bus.c          |   15 +++++++++++
>  drivers/acpi/sleep/wakeup.c |   60 ++++++++++++++++++++++++++++++++++++++++++++
>  include/acpi/acpi_bus.h     |    4 ++
>  4 files changed, 88 insertions(+)
> 
> Index: linux/drivers/acpi/Kconfig
> ===================================================================
> --- linux.orig/drivers/acpi/Kconfig	2009-08-05 11:00:06.000000000 +0800
> +++ linux/drivers/acpi/Kconfig	2009-08-19 10:07:05.000000000 +0800
> @@ -45,6 +45,15 @@ config ACPI_SLEEP
>  	depends on SUSPEND || HIBERNATION
>  	default y
>  
> +config ACPI_GPE_WAKEUP
> +	bool "ACPI wakeup event support"
> +	depends on PM_SLEEP && EXPERIMENTAL

It shouldn't depend on PM_SLEEP.

> +	help
> +	  Enable ACPI to detect wakeup event. For example, PCI device can
> +	  invoke PME, and in ACPI platform, the PME will invoke a GPE. With
> +	  the option, we can detect which device invokes wakeup event.
> +
> +

I don't think we need yet another CONFIG switch for this purpose.  It would
be better to make it depend on CONFIG_PM_RUNTIME or just on CONFIG_PM.

>  config ACPI_PROCFS
>  	bool "Deprecated /proc/acpi files"
>  	depends on PROC_FS
> Index: linux/drivers/acpi/wakeup.c
> ===================================================================
> --- linux.orig/drivers/acpi/wakeup.c	2009-07-24 11:31:05.000000000 +0800
> +++ linux/drivers/acpi/wakeup.c	2009-08-19 10:16:27.000000000 +0800
> @@ -124,6 +124,63 @@ void acpi_disable_wakeup_device(u8 sleep
>  	}
>  }
>  
> +#ifdef CONFIG_ACPI_GPE_WAKEUP
> +static int acpi_gpe_pme_check(struct acpi_device *dev)

acpi_gpe_wakeup_check() would be a better name IMO.  Everywhere below too.

> +{
> +	struct device *ldev;
> +
> +	ldev = acpi_get_physical_device(dev->handle);
> +	if (!ldev)
> +		return -ENODEV;
> +	/*
> +	 * AML code might already clear the event, so ignore the return value.
> +	 * Actually we can't correctly detect which device invokes GPE if the
> +	 * event is cleared.
> +	 */

I'm not sure what the comment is for.  Does it mean we should ignore the
result of the callback below?

> +	if (ldev->bus->pm && ldev->bus->pm->wakeup_event)

Is ldev->bus guaranteed not to be NULL?

> +		ldev->bus->pm->wakeup_event(ldev);

You're supposed to call pm_runtime_resume() in such cases (or
pm_request_resume() in case it can't sleep).

> +	put_device(ldev);
> +	return 0;
> +}
> +
> +static int acpi_gpe_pme_handler(struct notifier_block *nb,
> +	unsigned long type, void *data)
> +{
> +	int ret;
> +	acpi_handle handle = data;
> +	struct acpi_device *dev;
> +
> +	if (type != ACPI_NOTIFY_DEVICE_WAKE)
> +		return NOTIFY_DONE;
> +
> +	if (acpi_bus_get_device(handle, &dev))
> +		return NOTIFY_DONE;
> +
> +	ret = acpi_gpe_pme_check(dev);
> +
> +	acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);

I agree with Matthew on this.

> +	/* FIXME: spurious interrupt, disables it? */

Why FIXME?

> +	if (ret)
> +		printk(KERN_ERR"Spurious GPE %d detected\n",
> +			dev->wakeup.gpe_number);
> +
> +	return NOTIFY_OK;
> +}
> +
> +static struct notifier_block acpi_gpe_pme_nb = {
> +	.notifier_call = acpi_gpe_pme_handler,
> +};
> +
> +static void acpi_init_gpe_pme(void)
> +{
> +	register_acpi_bus_notifier(&acpi_gpe_pme_nb);
> +}

What exactly is the mechanism for invoking these notifiers?

You don't seem to set up the GPE like the Matthew's code does.  When and
where is the GPE actually set up?

> +#else
> +static inline void acpi_init_gpe_pme(void) {}
> +#endif
> +
>  int __init acpi_wakeup_device_init(void)
>  {
>  	struct list_head *node, *next;
> @@ -144,5 +201,7 @@ int __init acpi_wakeup_device_init(void)
>  		dev->wakeup.state.enabled = 1;
>  	}
>  	mutex_unlock(&acpi_device_lock);
> +
> +	acpi_init_gpe_pme();
>  	return 0;
>  }

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

Index: linux/drivers/acpi/Kconfig
===================================================================
--- linux.orig/drivers/acpi/Kconfig	2009-08-05 11:00:06.000000000 +0800
+++ linux/drivers/acpi/Kconfig	2009-08-19 10:07:05.000000000 +0800
@@ -45,6 +45,15 @@  config ACPI_SLEEP
 	depends on SUSPEND || HIBERNATION
 	default y
 
+config ACPI_GPE_WAKEUP
+	bool "ACPI wakeup event support"
+	depends on PM_SLEEP && EXPERIMENTAL
+	help
+	  Enable ACPI to detect wakeup event. For example, PCI device can
+	  invoke PME, and in ACPI platform, the PME will invoke a GPE. With
+	  the option, we can detect which device invokes wakeup event.
+
+
 config ACPI_PROCFS
 	bool "Deprecated /proc/acpi files"
 	depends on PROC_FS
Index: linux/drivers/acpi/wakeup.c
===================================================================
--- linux.orig/drivers/acpi/wakeup.c	2009-07-24 11:31:05.000000000 +0800
+++ linux/drivers/acpi/wakeup.c	2009-08-19 10:16:27.000000000 +0800
@@ -124,6 +124,63 @@  void acpi_disable_wakeup_device(u8 sleep
 	}
 }
 
+#ifdef CONFIG_ACPI_GPE_WAKEUP
+static int acpi_gpe_pme_check(struct acpi_device *dev)
+{
+	struct device *ldev;
+
+	ldev = acpi_get_physical_device(dev->handle);
+	if (!ldev)
+		return -ENODEV;
+	/*
+	 * AML code might already clear the event, so ignore the return value.
+	 * Actually we can't correctly detect which device invokes GPE if the
+	 * event is cleared.
+	 */
+	if (ldev->bus->pm && ldev->bus->pm->wakeup_event)
+		ldev->bus->pm->wakeup_event(ldev);
+
+	put_device(ldev);
+	return 0;
+}
+
+static int acpi_gpe_pme_handler(struct notifier_block *nb,
+	unsigned long type, void *data)
+{
+	int ret;
+	acpi_handle handle = data;
+	struct acpi_device *dev;
+
+	if (type != ACPI_NOTIFY_DEVICE_WAKE)
+		return NOTIFY_DONE;
+
+	if (acpi_bus_get_device(handle, &dev))
+		return NOTIFY_DONE;
+
+	ret = acpi_gpe_pme_check(dev);
+
+	acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
+
+	/* FIXME: spurious interrupt, disables it? */
+	if (ret)
+		printk(KERN_ERR"Spurious GPE %d detected\n",
+			dev->wakeup.gpe_number);
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block acpi_gpe_pme_nb = {
+	.notifier_call = acpi_gpe_pme_handler,
+};
+
+static void acpi_init_gpe_pme(void)
+{
+	register_acpi_bus_notifier(&acpi_gpe_pme_nb);
+}
+#else
+static inline void acpi_init_gpe_pme(void) {}
+#endif
+
 int __init acpi_wakeup_device_init(void)
 {
 	struct list_head *node, *next;
@@ -144,5 +201,7 @@  int __init acpi_wakeup_device_init(void)
 		dev->wakeup.state.enabled = 1;
 	}
 	mutex_unlock(&acpi_device_lock);
+
+	acpi_init_gpe_pme();
 	return 0;
 }