diff options
| author | Jeffy Chen <jeffy.chen@rock-chips.com> | 2018-03-08 19:03:27 -0500 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2018-03-14 13:13:22 -0400 |
| commit | 83fc580dcc2f0f36114477c4ac7adbe5c32329a3 (patch) | |
| tree | 848ad4e17ccb5afa3e056de894955b4b6efd28d2 /drivers/input | |
| parent | 7085123333e2ee2686086fc49bc3117bacd86105 (diff) | |
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>
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 145 |
1 files changed, 131 insertions, 14 deletions
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 @@ | |||
| 30 | #include <linux/of.h> | 30 | #include <linux/of.h> |
| 31 | #include <linux/of_irq.h> | 31 | #include <linux/of_irq.h> |
| 32 | #include <linux/spinlock.h> | 32 | #include <linux/spinlock.h> |
| 33 | #include <dt-bindings/input/gpio-keys.h> | ||
| 33 | 34 | ||
| 34 | struct gpio_button_data { | 35 | struct gpio_button_data { |
| 35 | const struct gpio_keys_button *button; | 36 | const struct gpio_keys_button *button; |
| @@ -45,6 +46,7 @@ struct gpio_button_data { | |||
| 45 | unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ | 46 | unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ |
| 46 | 47 | ||
| 47 | unsigned int irq; | 48 | unsigned int irq; |
| 49 | unsigned int wakeup_trigger_type; | ||
| 48 | spinlock_t lock; | 50 | spinlock_t lock; |
| 49 | bool disabled; | 51 | bool disabled; |
| 50 | bool key_pressed; | 52 | bool key_pressed; |
| @@ -540,6 +542,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev, | |||
| 540 | } | 542 | } |
| 541 | 543 | ||
| 542 | if (bdata->gpiod) { | 544 | if (bdata->gpiod) { |
| 545 | bool active_low = gpiod_is_active_low(bdata->gpiod); | ||
| 546 | |||
| 543 | if (button->debounce_interval) { | 547 | if (button->debounce_interval) { |
| 544 | error = gpiod_set_debounce(bdata->gpiod, | 548 | error = gpiod_set_debounce(bdata->gpiod, |
| 545 | button->debounce_interval * 1000); | 549 | button->debounce_interval * 1000); |
| @@ -568,6 +572,24 @@ static int gpio_keys_setup_key(struct platform_device *pdev, | |||
| 568 | isr = gpio_keys_gpio_isr; | 572 | isr = gpio_keys_gpio_isr; |
| 569 | irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | 573 | irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; |
| 570 | 574 | ||
| 575 | switch (button->wakeup_event_action) { | ||
| 576 | case EV_ACT_ASSERTED: | ||
| 577 | bdata->wakeup_trigger_type = active_low ? | ||
| 578 | IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING; | ||
| 579 | break; | ||
| 580 | case EV_ACT_DEASSERTED: | ||
| 581 | bdata->wakeup_trigger_type = active_low ? | ||
| 582 | IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING; | ||
| 583 | break; | ||
| 584 | case EV_ACT_ANY: | ||
| 585 | /* fall through */ | ||
| 586 | default: | ||
| 587 | /* | ||
| 588 | * For other cases, we are OK letting suspend/resume | ||
| 589 | * not reconfigure the trigger type. | ||
| 590 | */ | ||
| 591 | break; | ||
| 592 | } | ||
| 571 | } else { | 593 | } else { |
| 572 | if (!button->irq) { | 594 | if (!button->irq) { |
| 573 | dev_err(dev, "Found button without gpio or irq\n"); | 595 | dev_err(dev, "Found button without gpio or irq\n"); |
| @@ -586,6 +608,11 @@ static int gpio_keys_setup_key(struct platform_device *pdev, | |||
| 586 | 608 | ||
| 587 | isr = gpio_keys_irq_isr; | 609 | isr = gpio_keys_irq_isr; |
| 588 | irqflags = 0; | 610 | irqflags = 0; |
| 611 | |||
| 612 | /* | ||
| 613 | * For IRQ buttons, there is no interrupt for release. | ||
| 614 | * So we don't need to reconfigure the trigger type for wakeup. | ||
| 615 | */ | ||
| 589 | } | 616 | } |
| 590 | 617 | ||
| 591 | bdata->code = &ddata->keymap[idx]; | 618 | bdata->code = &ddata->keymap[idx]; |
| @@ -718,6 +745,9 @@ gpio_keys_get_devtree_pdata(struct device *dev) | |||
| 718 | /* legacy name */ | 745 | /* legacy name */ |
| 719 | fwnode_property_read_bool(child, "gpio-key,wakeup"); | 746 | fwnode_property_read_bool(child, "gpio-key,wakeup"); |
| 720 | 747 | ||
| 748 | fwnode_property_read_u32(child, "wakeup-event-action", | ||
| 749 | &button->wakeup_event_action); | ||
| 750 | |||
| 721 | button->can_disable = | 751 | button->can_disable = |
| 722 | fwnode_property_read_bool(child, "linux,can-disable"); | 752 | fwnode_property_read_bool(child, "linux,can-disable"); |
| 723 | 753 | ||
| @@ -845,19 +875,112 @@ static int gpio_keys_probe(struct platform_device *pdev) | |||
| 845 | return 0; | 875 | return 0; |
| 846 | } | 876 | } |
| 847 | 877 | ||
| 878 | static int __maybe_unused | ||
| 879 | gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata) | ||
| 880 | { | ||
| 881 | int error; | ||
| 882 | |||
| 883 | error = enable_irq_wake(bdata->irq); | ||
| 884 | if (error) { | ||
| 885 | dev_err(bdata->input->dev.parent, | ||
| 886 | "failed to configure IRQ %d as wakeup source: %d\n", | ||
| 887 | bdata->irq, error); | ||
| 888 | return error; | ||
| 889 | } | ||
| 890 | |||
| 891 | if (bdata->wakeup_trigger_type) { | ||
| 892 | error = irq_set_irq_type(bdata->irq, | ||
| 893 | bdata->wakeup_trigger_type); | ||
| 894 | if (error) { | ||
| 895 | dev_err(bdata->input->dev.parent, | ||
| 896 | "failed to set wakeup trigger %08x for IRQ %d: %d\n", | ||
| 897 | bdata->wakeup_trigger_type, bdata->irq, error); | ||
| 898 | disable_irq_wake(bdata->irq); | ||
| 899 | return error; | ||
| 900 | } | ||
| 901 | } | ||
| 902 | |||
| 903 | return 0; | ||
| 904 | } | ||
| 905 | |||
| 906 | static void __maybe_unused | ||
| 907 | gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata) | ||
| 908 | { | ||
| 909 | int error; | ||
| 910 | |||
| 911 | /* | ||
| 912 | * The trigger type is always both edges for gpio-based keys and we do | ||
| 913 | * not support changing wakeup trigger for interrupt-based keys. | ||
| 914 | */ | ||
| 915 | if (bdata->wakeup_trigger_type) { | ||
| 916 | error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH); | ||
| 917 | if (error) | ||
| 918 | dev_warn(bdata->input->dev.parent, | ||
| 919 | "failed to restore interrupt trigger for IRQ %d: %d\n", | ||
| 920 | bdata->irq, error); | ||
| 921 | } | ||
| 922 | |||
| 923 | error = disable_irq_wake(bdata->irq); | ||
| 924 | if (error) | ||
| 925 | dev_warn(bdata->input->dev.parent, | ||
| 926 | "failed to disable IRQ %d as wake source: %d\n", | ||
| 927 | bdata->irq, error); | ||
| 928 | } | ||
| 929 | |||
| 930 | static int __maybe_unused | ||
| 931 | gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata) | ||
| 932 | { | ||
| 933 | struct gpio_button_data *bdata; | ||
| 934 | int error; | ||
| 935 | int i; | ||
| 936 | |||
| 937 | for (i = 0; i < ddata->pdata->nbuttons; i++) { | ||
| 938 | bdata = &ddata->data[i]; | ||
| 939 | if (bdata->button->wakeup) { | ||
| 940 | error = gpio_keys_button_enable_wakeup(bdata); | ||
| 941 | if (error) | ||
| 942 | goto err_out; | ||
| 943 | } | ||
| 944 | bdata->suspended = true; | ||
| 945 | } | ||
| 946 | |||
| 947 | return 0; | ||
| 948 | |||
| 949 | err_out: | ||
| 950 | while (i--) { | ||
| 951 | bdata = &ddata->data[i]; | ||
| 952 | if (bdata->button->wakeup) | ||
| 953 | gpio_keys_button_disable_wakeup(bdata); | ||
| 954 | bdata->suspended = false; | ||
| 955 | } | ||
| 956 | |||
| 957 | return error; | ||
| 958 | } | ||
| 959 | |||
| 960 | static void __maybe_unused | ||
| 961 | gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata) | ||
| 962 | { | ||
| 963 | struct gpio_button_data *bdata; | ||
| 964 | int i; | ||
| 965 | |||
| 966 | for (i = 0; i < ddata->pdata->nbuttons; i++) { | ||
| 967 | bdata = &ddata->data[i]; | ||
| 968 | bdata->suspended = false; | ||
| 969 | if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq))) | ||
| 970 | gpio_keys_button_disable_wakeup(bdata); | ||
| 971 | } | ||
| 972 | } | ||
| 973 | |||
| 848 | static int __maybe_unused gpio_keys_suspend(struct device *dev) | 974 | static int __maybe_unused gpio_keys_suspend(struct device *dev) |
| 849 | { | 975 | { |
| 850 | struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); | 976 | struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); |
| 851 | struct input_dev *input = ddata->input; | 977 | struct input_dev *input = ddata->input; |
| 852 | int i; | 978 | int error; |
| 853 | 979 | ||
| 854 | if (device_may_wakeup(dev)) { | 980 | if (device_may_wakeup(dev)) { |
| 855 | for (i = 0; i < ddata->pdata->nbuttons; i++) { | 981 | error = gpio_keys_enable_wakeup(ddata); |
| 856 | struct gpio_button_data *bdata = &ddata->data[i]; | 982 | if (error) |
| 857 | if (bdata->button->wakeup) | 983 | return error; |
| 858 | enable_irq_wake(bdata->irq); | ||
| 859 | bdata->suspended = true; | ||
| 860 | } | ||
| 861 | } else { | 984 | } else { |
| 862 | mutex_lock(&input->mutex); | 985 | mutex_lock(&input->mutex); |
| 863 | if (input->users) | 986 | if (input->users) |
| @@ -873,15 +996,9 @@ static int __maybe_unused gpio_keys_resume(struct device *dev) | |||
| 873 | struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); | 996 | struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev); |
| 874 | struct input_dev *input = ddata->input; | 997 | struct input_dev *input = ddata->input; |
| 875 | int error = 0; | 998 | int error = 0; |
| 876 | int i; | ||
| 877 | 999 | ||
| 878 | if (device_may_wakeup(dev)) { | 1000 | if (device_may_wakeup(dev)) { |
| 879 | for (i = 0; i < ddata->pdata->nbuttons; i++) { | 1001 | gpio_keys_disable_wakeup(ddata); |
| 880 | struct gpio_button_data *bdata = &ddata->data[i]; | ||
| 881 | if (bdata->button->wakeup) | ||
| 882 | disable_irq_wake(bdata->irq); | ||
| 883 | bdata->suspended = false; | ||
| 884 | } | ||
| 885 | } else { | 1002 | } else { |
| 886 | mutex_lock(&input->mutex); | 1003 | mutex_lock(&input->mutex); |
| 887 | if (input->users) | 1004 | if (input->users) |
