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>
This commit is contained in:
parent
7085123333
commit
83fc580dcc
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue