From: Huang jun <huangjun61(a)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(a)huawei.com>
---
drivers/acpi/evged.c | 108 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c
index f13ba2c07667..dde8fbff8d19 100644
--- a/drivers/acpi/evged.c
+++ b/drivers/acpi/evged.c
@@ -46,11 +46,20 @@
#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_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 acpi_ged_handle *wakeup_handle;
struct list_head event_list;
};
@@ -131,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;
@@ -149,6 +186,9 @@ static int ged_probe(struct platform_device *pdev)
return -EINVAL;
}
platform_set_drvdata(pdev, geddev);
+#ifdef CONFIG_PM_SLEEP
+ init_ged_handle(geddev);
+#endif
return 0;
}
@@ -164,6 +204,10 @@ static void ged_shutdown(struct platform_device *pdev)
dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n",
event->gsi, event->irq);
}
+
+ if (geddev->wakeup_handle)
+ del_timer(&geddev->wakeup_handle->timer);
+
}
static int ged_remove(struct platform_device *pdev)
@@ -172,6 +216,67 @@ static int ged_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static void ged_timer_callback(struct timer_list *t)
+{
+ struct acpi_ged_handle *wakeup_handle = from_timer(wakeup_handle, t, timer);
+ acpi_status acpi_ret;
+
+ /* _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_handle *wakeup_handle = geddev->wakeup_handle;
+ struct acpi_ged_event *event, *next;
+ acpi_status acpi_ret;
+
+ /* _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;
+ }
+ }
+
+ list_for_each_entry_safe(event, next, &geddev->event_list, node)
+ 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_handle *wakeup_handle = geddev->wakeup_handle;
+ struct acpi_ged_event *event, *next;
+
+ /* use timer to complete 4s anti-shake */
+ 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;
+}
+
+static const struct dev_pm_ops ged_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ged_suspend, ged_resume)
+};
+#endif
+
+
static const struct acpi_device_id ged_acpi_ids[] = {
{"ACPI0013"},
{},
@@ -184,6 +289,9 @@ 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);
--
2.20.1