[PATCH] Input: gpio-keys - add hibernation support
From: Armando De Leon
Date: Mon Apr 06 2026 - 12:09:37 EST
The gpio-keys driver uses DEFINE_SIMPLE_DEV_PM_OPS which maps .freeze
and .restore to the same callbacks as .suspend and .resume. This is
insufficient for hibernation (suspend-to-disk) because the SoC is fully
powered off, unlike suspend-to-RAM where hardware state is preserved.
After hibernation resume, GPIO keys fail to function correctly because
the interrupt controller (e.g. GIC) is fully re-initialized during
restore, resetting all IRQ trigger type configurations to defaults.
GPIO keys require IRQ_TYPE_EDGE_BOTH for proper press/release detection,
but after the interrupt controller re-initialization only a single edge
remains active. This causes buttons to report only release events but
not press events, or vice versa.
The existing gpio_keys_button_disable_wakeup() does restore
IRQ_TYPE_EDGE_BOTH, but only when wakeup_trigger_type is non-zero.
When the device tree does not specify wakeup-event-action (the common
case), wakeup_trigger_type defaults to zero and the IRQ type
restoration is skipped entirely.
Fix this by implementing dedicated .freeze and .restore callbacks:
- gpio_keys_freeze(): Marks all buttons as suspended before the
hibernate image is created. Unlike .suspend, it does not configure
wakeup IRQs since the SoC will be powered off.
- gpio_keys_restore(): Re-applies the pinctrl default state to restore
GPIO pin configuration, explicitly reconfigures IRQ trigger type to
IRQ_TYPE_EDGE_BOTH for all GPIO-backed buttons, clears the suspended
flag, and re-syncs current GPIO state with the input subsystem.
The .thaw callback reuses gpio_keys_resume() since the SoC remains
powered when hibernation is aborted and hardware state is intact.
Signed-off-by: Armando De Leon <learmand@xxxxxxxxxx>
---
drivers/input/keyboard/gpio_keys.c | 60 +++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index e19617485..9fb77c9aa 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -27,6 +27,7 @@
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/spinlock.h>
#include <dt-bindings/input/gpio-keys.h>
@@ -1088,7 +1089,64 @@ static int gpio_keys_resume(struct device *dev)
return 0;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
+static int gpio_keys_freeze(struct device *dev)
+{
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
+ struct gpio_button_data *bdata;
+ int i;
+
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ bdata->suspended = true;
+ }
+
+ return 0;
+}
+
+static int gpio_keys_restore(struct device *dev)
+{
+ struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
+ struct gpio_button_data *bdata;
+ int error;
+ int i;
+
+ error = pinctrl_pm_select_default_state(dev);
+ if (error)
+ dev_warn(dev, "failed to restore pinctrl default state: %d\n",
+ error);
+
+ for (i = 0; i < ddata->pdata->nbuttons; i++) {
+ bdata = &ddata->data[i];
+ bdata->suspended = false;
+
+ /*
+ * After hibernation the interrupt controller is
+ * re-initialized and loses its configuration.
+ * Restore dual-edge triggering for GPIO-backed
+ * buttons so both press and release are detected.
+ */
+ if (bdata->gpiod) {
+ error = irq_set_irq_type(bdata->irq,
+ IRQ_TYPE_EDGE_BOTH);
+ if (error)
+ dev_warn(dev,
+ "failed to restore IRQ %d trigger: %d\n",
+ bdata->irq, error);
+ }
+ }
+
+ gpio_keys_report_state(ddata);
+ return 0;
+}
+
+static const struct dev_pm_ops gpio_keys_pm_ops = {
+ .suspend = gpio_keys_suspend,
+ .resume = gpio_keys_resume,
+ .freeze = gpio_keys_freeze,
+ .restore = gpio_keys_restore,
+ .thaw = gpio_keys_resume,
+ .poweroff = gpio_keys_suspend,
+};
static void gpio_keys_shutdown(struct platform_device *pdev)
{
--
2.43.0