From: huangjun huangjun63@huawei.com
If we want acpi ged to support wake from freeze, we need to implement the suspend/resume function. In these two methods, ACPI's _GPO, GPP method is called to realize the setting of sleep flag and anti-shake.
Signed-off-by: Huangjun huangjun63@huawei.com --- drivers/acpi/evged.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+)
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c index f13ba2c07667..84656fc09d15 100644 --- a/drivers/acpi/evged.c +++ b/drivers/acpi/evged.c @@ -46,11 +46,14 @@ #include <linux/list.h> #include <linux/platform_device.h> #include <linux/acpi.h> +#include <linux/timer.h> +#include <linux/jiffies.h>
#define MODULE_NAME "acpi-ged"
struct acpi_ged_device { struct device *dev; + struct timer_list timer; struct list_head event_list; };
@@ -148,6 +151,8 @@ static int ged_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to parse the _CRS record\n"); return -EINVAL; } + + timer_setup(&geddev->timer, NULL, 0); platform_set_drvdata(pdev, geddev);
return 0; @@ -164,6 +169,7 @@ static void ged_shutdown(struct platform_device *pdev) dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n", event->gsi, event->irq); } + del_timer(&geddev->timer); }
static int ged_remove(struct platform_device *pdev) @@ -177,6 +183,78 @@ static const struct acpi_device_id ged_acpi_ids[] = { {}, };
+#ifdef CONFIG_PM_SLEEP +static acpi_status ged_acpi_execute(struct device *dev, char* method, u64 arg) +{ + acpi_status acpi_ret; + acpi_handle method_handle; + + acpi_ret = acpi_get_handle(ACPI_HANDLE(dev), method, &method_handle); + + if (ACPI_FAILURE(acpi_ret)) { + dev_err(dev, "cannot locate %s method\n", method); + return AE_NOT_FOUND; + } + + acpi_ret = acpi_execute_simple_method(method_handle, NULL, arg); + if (ACPI_FAILURE(acpi_ret)) { + dev_err(dev, "%s method execution failed\n", method); + return AE_ERROR; + } + + return AE_OK; +} + +static void ged_timer_callback(struct timer_list *t) +{ + struct acpi_ged_device *geddev = from_timer(geddev, t, timer); + struct acpi_ged_event *event, *next; + + list_for_each_entry_safe(event, next, &geddev->event_list, node) { + ged_acpi_execute(geddev->dev, "_GPP", event->gsi); + } +} + +static int ged_suspend(struct device *dev) +{ + struct acpi_ged_device *geddev = dev_get_drvdata(dev); + struct acpi_ged_event *event, *next; + acpi_status acpi_ret; + + list_for_each_entry_safe(event, next, &geddev->event_list, node) { + acpi_ret = ged_acpi_execute(dev, "_GPO", event->gsi); + + if (acpi_ret == AE_ERROR) + return -EINVAL; + + enable_irq_wake(event->irq); + } + return 0; +} + +static int ged_resume(struct device *dev) +{ + struct acpi_ged_device *geddev = dev_get_drvdata(dev); + struct acpi_ged_event *event, *next; + + list_for_each_entry_safe(event, next, &geddev->event_list, node) { + disable_irq_wake(event->irq); + } + + /* use timer to complete 4s anti-shake */ + geddev->timer.expires = jiffies + (4 * HZ); + geddev->timer.function = ged_timer_callback; + add_timer(&geddev->timer); + + return 0; +} + +static const struct dev_pm_ops ged_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ged_suspend, ged_resume) +}; +#endif + + static struct platform_driver ged_driver = { .probe = ged_probe, .remove = ged_remove, @@ -184,6 +262,10 @@ static struct platform_driver ged_driver = { .driver = { .name = MODULE_NAME, .acpi_match_table = ACPI_PTR(ged_acpi_ids), +#ifdef CONFIG_PM_SLEEP + .pm = &ged_pm_ops, +#endif + }, }; builtin_platform_driver(ged_driver);
From: huangjun huangjun63@huawei.com
The acpi_get_handle function uses a mutex, so it cannot be called in the timer. We put the initialization of acpi_handle in init_ged_handle which will be called in the ged_probe.
Signed-off-by: Huangjun huangjun63@huawei.com --- drivers/acpi/evged.c | 123 ++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 48 deletions(-)
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c index 84656fc09d15..ef5167c0aa87 100644 --- a/drivers/acpi/evged.c +++ b/drivers/acpi/evged.c @@ -51,9 +51,15 @@
#define MODULE_NAME "acpi-ged"
+struct acpi_ged_handle { + struct timer_list timer; /* For 4s anti-shake of power button */ + acpi_handle gpp_handle; /* ACPI Handle: enable shutdown */ + acpi_handle gpo_handle; /* ACPI Handle: set sleep flag */ +}; + struct acpi_ged_device { struct device *dev; - struct timer_list timer; + struct acpi_ged_handle *wakeup_handle; struct list_head event_list; };
@@ -134,6 +140,34 @@ static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, return AE_OK; }
+#ifdef CONFIG_PM_SLEEP +static void init_ged_handle(struct acpi_ged_device *geddev) { + struct acpi_ged_handle *wakeup_handle; + acpi_handle gpo_handle = NULL; + acpi_handle gpp_handle = NULL; + acpi_status acpi_ret; + + wakeup_handle = devm_kzalloc(geddev->dev, sizeof(*wakeup_handle), GFP_KERNEL); + if (!wakeup_handle) + return; + + geddev->wakeup_handle = wakeup_handle; + + /* Initialize wakeup_handle, prepare for ged suspend and resume */ + timer_setup(&wakeup_handle->timer, NULL, 0); + + acpi_ret = acpi_get_handle(ACPI_HANDLE(geddev->dev), "_GPO", &gpo_handle); + if (ACPI_FAILURE(acpi_ret)) + dev_info(geddev->dev, "cannot locate _GPO method\n"); + wakeup_handle->gpo_handle = gpo_handle; + + acpi_ret = acpi_get_handle(ACPI_HANDLE(geddev->dev), "_GPP", &gpp_handle); + if (ACPI_FAILURE(acpi_ret)) + dev_info(geddev->dev, "cannot locate _GPP method\n"); + wakeup_handle->gpp_handle = gpp_handle; +} +#endif + static int ged_probe(struct platform_device *pdev) { struct acpi_ged_device *geddev; @@ -152,8 +186,10 @@ static int ged_probe(struct platform_device *pdev) return -EINVAL; }
- timer_setup(&geddev->timer, NULL, 0); platform_set_drvdata(pdev, geddev); +#ifdef CONFIG_PM_SLEEP + init_ged_handle(geddev); +#endif
return 0; } @@ -169,7 +205,9 @@ static void ged_shutdown(struct platform_device *pdev) dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n", event->gsi, event->irq); } - del_timer(&geddev->timer); + if (geddev->wakeup_handle) + del_timer(&geddev->wakeup_handle->timer); + }
static int ged_remove(struct platform_device *pdev) @@ -184,67 +222,56 @@ static const struct acpi_device_id ged_acpi_ids[] = { };
#ifdef CONFIG_PM_SLEEP -static acpi_status ged_acpi_execute(struct device *dev, char* method, u64 arg) -{ - acpi_status acpi_ret; - acpi_handle method_handle; - - acpi_ret = acpi_get_handle(ACPI_HANDLE(dev), method, &method_handle); - - if (ACPI_FAILURE(acpi_ret)) { - dev_err(dev, "cannot locate %s method\n", method); - return AE_NOT_FOUND; - } - - acpi_ret = acpi_execute_simple_method(method_handle, NULL, arg); - if (ACPI_FAILURE(acpi_ret)) { - dev_err(dev, "%s method execution failed\n", method); - return AE_ERROR; - } - - return AE_OK; -} - static void ged_timer_callback(struct timer_list *t) { - struct acpi_ged_device *geddev = from_timer(geddev, t, timer); - struct acpi_ged_event *event, *next; + struct acpi_ged_handle *wakeup_handle = from_timer(wakeup_handle, t, timer); + acpi_status acpi_ret;
- list_for_each_entry_safe(event, next, &geddev->event_list, node) { - ged_acpi_execute(geddev->dev, "_GPP", event->gsi); - } + /* _GPP method enable power button */ + if (wakeup_handle && wakeup_handle->gpp_handle) { + acpi_ret = acpi_execute_simple_method(wakeup_handle->gpp_handle, NULL, ACPI_IRQ_MODEL_GIC); + if (ACPI_FAILURE(acpi_ret)) + pr_warn("_GPP method execution failed\n"); + } }
static int ged_suspend(struct device *dev) { - struct acpi_ged_device *geddev = dev_get_drvdata(dev); - struct acpi_ged_event *event, *next; - acpi_status acpi_ret; + struct acpi_ged_device *geddev = dev_get_drvdata(dev); + struct acpi_ged_handle *wakeup_handle = geddev->wakeup_handle; + struct acpi_ged_event *event, *next; + acpi_status acpi_ret;
- list_for_each_entry_safe(event, next, &geddev->event_list, node) { - acpi_ret = ged_acpi_execute(dev, "_GPO", event->gsi); + /* _GPO method set sleep flag */ + if (wakeup_handle && wakeup_handle->gpo_handle) { + acpi_ret = acpi_execute_simple_method(wakeup_handle->gpo_handle, NULL, ACPI_IRQ_MODEL_GIC); + if (ACPI_FAILURE(acpi_ret)) { + pr_warn("_GPO method execution failed\n"); + return AE_ERROR; + } + }
- if (acpi_ret == AE_ERROR) - return -EINVAL; + list_for_each_entry_safe(event, next, &geddev->event_list, node) + enable_irq_wake(event->irq);
- enable_irq_wake(event->irq); - } - return 0; + return 0; }
static int ged_resume(struct device *dev) { - struct acpi_ged_device *geddev = dev_get_drvdata(dev); - struct acpi_ged_event *event, *next; - - list_for_each_entry_safe(event, next, &geddev->event_list, node) { - disable_irq_wake(event->irq); - } + struct acpi_ged_device *geddev = dev_get_drvdata(dev); + struct acpi_ged_handle *wakeup_handle = geddev->wakeup_handle; + struct acpi_ged_event *event, *next;
/* use timer to complete 4s anti-shake */ - geddev->timer.expires = jiffies + (4 * HZ); - geddev->timer.function = ged_timer_callback; - add_timer(&geddev->timer); + if (wakeup_handle && wakeup_handle->gpp_handle) { + wakeup_handle->timer.expires = jiffies + (4 * HZ); + wakeup_handle->timer.function = ged_timer_callback; + add_timer(&wakeup_handle->timer); + } + + list_for_each_entry_safe(event, next, &geddev->event_list, node) + disable_irq_wake(event->irq);
return 0; }