| From c92fcd890e67fe678f7c85d231a9f32f6761b93c Mon Sep 17 00:00:00 2001 |
| From: Jeffy Chen <jeffy.chen@rock-chips.com> |
| Date: Thu, 8 Mar 2018 16:03:27 -0800 |
| Subject: [PATCH 0786/1795] Input: gpio-keys - add support for wakeup event |
| action |
| |
| Add support for specifying event actions to trigger wakeup when using |
| the gpio-keys input device as a wakeup source. |
| |
| This would allow the device to configure when to wakeup the system. For |
| example a gpio-keys input device for pen insert, may only want to wakeup |
| the system when ejecting the pen. |
| |
| Suggested-by: Brian Norris <briannorris@chromium.org> |
| Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com> |
| Reviewed-by: Rob Herring <robh@kernel.org> |
| Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> |
| (cherry picked from commit 83fc580dcc2f0f36114477c4ac7adbe5c32329a3) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| .../devicetree/bindings/input/gpio-keys.txt | 8 + |
| drivers/input/keyboard/gpio_keys.c | 145 ++++++++++++++++-- |
| include/dt-bindings/input/gpio-keys.h | 13 ++ |
| include/linux/gpio_keys.h | 2 + |
| 4 files changed, 154 insertions(+), 14 deletions(-) |
| create mode 100644 include/dt-bindings/input/gpio-keys.h |
| |
| diff --git a/Documentation/devicetree/bindings/input/gpio-keys.txt b/Documentation/devicetree/bindings/input/gpio-keys.txt |
| index a94940481e55..996ce84352cb 100644 |
| --- a/Documentation/devicetree/bindings/input/gpio-keys.txt |
| +++ b/Documentation/devicetree/bindings/input/gpio-keys.txt |
| @@ -26,6 +26,14 @@ Optional subnode-properties: |
| If not specified defaults to 5. |
| - wakeup-source: Boolean, button can wake-up the system. |
| (Legacy property supported: "gpio-key,wakeup") |
| + - wakeup-event-action: Specifies whether the key should wake the |
| + system when asserted, when deasserted, or both. This property is |
| + only valid for keys that wake up the system (e.g., when the |
| + "wakeup-source" property is also provided). |
| + Supported values are defined in linux-event-codes.h: |
| + EV_ACT_ASSERTED - asserted |
| + EV_ACT_DEASSERTED - deasserted |
| + EV_ACT_ANY - both asserted and deasserted |
| - linux,can-disable: Boolean, indicates that button is connected |
| to dedicated (not shared) interrupt which can be disabled to |
| suppress events from the button. |
| diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c |
| index 87e613dc33b8..052e37675086 100644 |
| --- a/drivers/input/keyboard/gpio_keys.c |
| +++ b/drivers/input/keyboard/gpio_keys.c |
| @@ -30,6 +30,7 @@ |
| #include <linux/of.h> |
| #include <linux/of_irq.h> |
| #include <linux/spinlock.h> |
| +#include <dt-bindings/input/gpio-keys.h> |
| |
| struct gpio_button_data { |
| const struct gpio_keys_button *button; |
| @@ -45,6 +46,7 @@ struct gpio_button_data { |
| unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ |
| |
| unsigned int irq; |
| + unsigned int wakeup_trigger_type; |
| spinlock_t lock; |
| bool disabled; |
| bool key_pressed; |
| @@ -540,6 +542,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev, |
| } |
| |
| if (bdata->gpiod) { |
| + bool active_low = gpiod_is_active_low(bdata->gpiod); |
| + |
| if (button->debounce_interval) { |
| error = gpiod_set_debounce(bdata->gpiod, |
| button->debounce_interval * 1000); |
| @@ -568,6 +572,24 @@ static int gpio_keys_setup_key(struct platform_device *pdev, |
| isr = gpio_keys_gpio_isr; |
| irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; |
| |
| + switch (button->wakeup_event_action) { |
| + case EV_ACT_ASSERTED: |
| + bdata->wakeup_trigger_type = active_low ? |
| + IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; |
| + break; |
| + case EV_ACT_DEASSERTED: |
| + bdata->wakeup_trigger_type = active_low ? |
| + IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING; |
| + break; |
| + case EV_ACT_ANY: |
| + /* fall through */ |
| + default: |
| + /* |
| + * For other cases, we are OK letting suspend/resume |
| + * not reconfigure the trigger type. |
| + */ |
| + break; |
| + } |
| } else { |
| if (!button->irq) { |
| dev_err(dev, "Found button without gpio or irq\n"); |
| @@ -586,6 +608,11 @@ static int gpio_keys_setup_key(struct platform_device *pdev, |
| |
| isr = gpio_keys_irq_isr; |
| irqflags = 0; |
| + |
| + /* |
| + * For IRQ buttons, there is no interrupt for release. |
| + * So we don't need to reconfigure the trigger type for wakeup. |
| + */ |
| } |
| |
| bdata->code = &ddata->keymap[idx]; |
| @@ -718,6 +745,9 @@ gpio_keys_get_devtree_pdata(struct device *dev) |
| /* legacy name */ |
| fwnode_property_read_bool(child, "gpio-key,wakeup"); |
| |
| + fwnode_property_read_u32(child, "wakeup-event-action", |
| + &button->wakeup_event_action); |
| + |
| button->can_disable = |
| fwnode_property_read_bool(child, "linux,can-disable"); |
| |
| @@ -845,19 +875,112 @@ static int gpio_keys_probe(struct platform_device *pdev) |
| return 0; |
| } |
| |
| +static int __maybe_unused |
| +gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata) |
| +{ |
| + int error; |
| + |
| + error = enable_irq_wake(bdata->irq); |
| + if (error) { |
| + dev_err(bdata->input->dev.parent, |
| + "failed to configure IRQ %d as wakeup source: %d\n", |
| + bdata->irq, error); |
| + return error; |
| + } |
| + |
| + if (bdata->wakeup_trigger_type) { |
| + error = irq_set_irq_type(bdata->irq, |
| + bdata->wakeup_trigger_type); |
| + if (error) { |
| + dev_err(bdata->input->dev.parent, |
| + "failed to set wakeup trigger %08x for IRQ %d: %d\n", |
| + bdata->wakeup_trigger_type, bdata->irq, error); |
| + disable_irq_wake(bdata->irq); |
| + return error; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static void __maybe_unused |
| +gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata) |
| +{ |
| + int error; |
| + |
| + /* |
| + * The trigger type is always both edges for gpio-based keys and we do |
| + * not support changing wakeup trigger for interrupt-based keys. |
| + */ |
| + if (bdata->wakeup_trigger_type) { |
| + error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH); |
| + if (error) |
| + dev_warn(bdata->input->dev.parent, |
| + "failed to restore interrupt trigger for IRQ %d: %d\n", |
| + bdata->irq, error); |
| + } |
| + |
| + error = disable_irq_wake(bdata->irq); |
| + if (error) |
| + dev_warn(bdata->input->dev.parent, |
| + "failed to disable IRQ %d as wake source: %d\n", |
| + bdata->irq, error); |
| +} |
| + |
| +static int __maybe_unused |
| +gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata) |
| +{ |
| + struct gpio_button_data *bdata; |
| + int error; |
| + int i; |
| + |
| + for (i = 0; i < ddata->pdata->nbuttons; i++) { |
| + bdata = &ddata->data[i]; |
| + if (bdata->button->wakeup) { |
| + error = gpio_keys_button_enable_wakeup(bdata); |
| + if (error) |
| + goto err_out; |
| + } |
| + bdata->suspended = true; |
| + } |
| + |
| + return 0; |
| + |
| +err_out: |
| + while (i--) { |
| + bdata = &ddata->data[i]; |
| + if (bdata->button->wakeup) |
| + gpio_keys_button_disable_wakeup(bdata); |
| + bdata->suspended = false; |
| + } |
| + |
| + return error; |
| +} |
| + |
| +static void __maybe_unused |
| +gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata) |
| +{ |
| + struct gpio_button_data *bdata; |
| + int i; |
| + |
| + for (i = 0; i < ddata->pdata->nbuttons; i++) { |
| + bdata = &ddata->data[i]; |
| + bdata->suspended = false; |
| + if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq))) |
| + gpio_keys_button_disable_wakeup(bdata); |
| + } |
| +} |
| + |
| static int __maybe_unused gpio_keys_suspend(struct device *dev) |
| { |
| struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); |
| struct input_dev *input = ddata->input; |
| - int i; |
| + int error; |
| |
| if (device_may_wakeup(dev)) { |
| - for (i = 0; i < ddata->pdata->nbuttons; i++) { |
| - struct gpio_button_data *bdata = &ddata->data[i]; |
| - if (bdata->button->wakeup) |
| - enable_irq_wake(bdata->irq); |
| - bdata->suspended = true; |
| - } |
| + error = gpio_keys_enable_wakeup(ddata); |
| + if (error) |
| + return error; |
| } else { |
| mutex_lock(&input->mutex); |
| if (input->users) |
| @@ -873,15 +996,9 @@ static int __maybe_unused gpio_keys_resume(struct device *dev) |
| struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); |
| struct input_dev *input = ddata->input; |
| int error = 0; |
| - int i; |
| |
| if (device_may_wakeup(dev)) { |
| - for (i = 0; i < ddata->pdata->nbuttons; i++) { |
| - struct gpio_button_data *bdata = &ddata->data[i]; |
| - if (bdata->button->wakeup) |
| - disable_irq_wake(bdata->irq); |
| - bdata->suspended = false; |
| - } |
| + gpio_keys_disable_wakeup(ddata); |
| } else { |
| mutex_lock(&input->mutex); |
| if (input->users) |
| diff --git a/include/dt-bindings/input/gpio-keys.h b/include/dt-bindings/input/gpio-keys.h |
| new file mode 100644 |
| index 000000000000..8962df79e753 |
| --- /dev/null |
| +++ b/include/dt-bindings/input/gpio-keys.h |
| @@ -0,0 +1,13 @@ |
| +/* SPDX-License-Identifier: GPL-2.0 */ |
| +/* |
| + * This header provides constants for gpio keys bindings. |
| + */ |
| + |
| +#ifndef _DT_BINDINGS_GPIO_KEYS_H |
| +#define _DT_BINDINGS_GPIO_KEYS_H |
| + |
| +#define EV_ACT_ANY 0x00 /* asserted or deasserted */ |
| +#define EV_ACT_ASSERTED 0x01 /* asserted */ |
| +#define EV_ACT_DEASSERTED 0x02 /* deasserted */ |
| + |
| +#endif /* _DT_BINDINGS_GPIO_KEYS_H */ |
| diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h |
| index d06bf77400f1..7160df54a6fe 100644 |
| --- a/include/linux/gpio_keys.h |
| +++ b/include/linux/gpio_keys.h |
| @@ -13,6 +13,7 @@ struct device; |
| * @desc: label that will be attached to button's gpio |
| * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS) |
| * @wakeup: configure the button as a wake-up source |
| + * @wakeup_event_action: event action to trigger wakeup |
| * @debounce_interval: debounce ticks interval in msecs |
| * @can_disable: %true indicates that userspace is allowed to |
| * disable button via sysfs |
| @@ -26,6 +27,7 @@ struct gpio_keys_button { |
| const char *desc; |
| unsigned int type; |
| int wakeup; |
| + int wakeup_event_action; |
| int debounce_interval; |
| bool can_disable; |
| int value; |
| -- |
| 2.19.0 |
| |