Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: gpio-keys - add system suspend support for dedicated wakeirqs

Some SoCs have a separate dedicated wake-up interrupt controller that can
be used to wake up the system from deeper idle states. We already support
configuring a separate interrupt for a gpio-keys button to be used with a
gpio line. However, we are lacking support system suspend for cases where
a separate interrupt needs to be used in deeper sleep modes.

Because of it's nature, gpio-keys does not know about the runtime PM state
of the button gpios, and may have several gpio buttons configured for each
gpio-keys device instance. Implementing runtime PM support for gpio-keys
does not help, and we cannot use drivers/base/power/wakeirq.c support. We
need to implement custom wakeirq support for gpio-keys.

For handling a dedicated wakeirq for system suspend, we enable and disable
it with gpio_keys_enable_wakeup() and gpio_keys_disable_wakeup() that we
already use based on device_may_wakeup().

Some systems may have a dedicated wakeirq that can also be used as the
main interrupt, this is already working for gpio-keys. Let's add some
wakeirq related comments while at it as the usage with a gpio line and
separate interrupt line may not be obvious.

Tested-by: Dhruva Gole <d-gole@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Link: https://lore.kernel.org/r/20231129110618.27551-2-tony@atomide.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Tony Lindgren and committed by
Dmitry Torokhov
3717194f d2ff98b7

+67 -4
+65 -4
drivers/input/keyboard/gpio_keys.c
··· 45 45 unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */ 46 46 47 47 unsigned int irq; 48 + unsigned int wakeirq; 48 49 unsigned int wakeup_trigger_type; 50 + 49 51 spinlock_t lock; 50 52 bool disabled; 51 53 bool key_pressed; ··· 513 511 struct gpio_button_data *bdata = &ddata->data[idx]; 514 512 irq_handler_t isr; 515 513 unsigned long irqflags; 514 + const char *wakedesc; 516 515 int irq; 517 516 int error; 518 517 ··· 578 575 !gpiod_cansleep(bdata->gpiod); 579 576 } 580 577 578 + /* 579 + * If an interrupt was specified, use it instead of the gpio 580 + * interrupt and use the gpio for reading the state. A separate 581 + * interrupt may be used as the main button interrupt for 582 + * runtime PM to detect events also in deeper idle states. If a 583 + * dedicated wakeirq is used for system suspend only, see below 584 + * for bdata->wakeirq setup. 585 + */ 581 586 if (button->irq) { 582 587 bdata->irq = button->irq; 583 588 } else { ··· 683 672 return error; 684 673 } 685 674 675 + if (!button->wakeirq) 676 + return 0; 677 + 678 + /* Use :wakeup suffix like drivers/base/power/wakeirq.c does */ 679 + wakedesc = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", desc); 680 + if (!wakedesc) 681 + return -ENOMEM; 682 + 683 + bdata->wakeirq = button->wakeirq; 684 + irqflags |= IRQF_NO_SUSPEND; 685 + 686 + /* 687 + * Wakeirq shares the handler with the main interrupt, it's only 688 + * active during system suspend. See gpio_keys_button_enable_wakeup() 689 + * and gpio_keys_button_disable_wakeup(). 690 + */ 691 + error = devm_request_any_context_irq(dev, bdata->wakeirq, isr, 692 + irqflags, wakedesc, bdata); 693 + if (error < 0) { 694 + dev_err(dev, "Unable to claim wakeirq %d; error %d\n", 695 + bdata->irq, error); 696 + return error; 697 + } 698 + 699 + /* 700 + * Disable wakeirq until suspend. IRQF_NO_AUTOEN won't work if 701 + * IRQF_SHARED was set based on !button->can_disable. 702 + */ 703 + disable_irq(bdata->wakeirq); 704 + 686 705 return 0; 687 706 } 688 707 ··· 769 728 struct gpio_keys_platform_data *pdata; 770 729 struct gpio_keys_button *button; 771 730 struct fwnode_handle *child; 772 - int nbuttons; 731 + int nbuttons, irq; 773 732 774 733 nbuttons = device_get_child_node_count(dev); 775 734 if (nbuttons == 0) ··· 791 750 device_property_read_string(dev, "label", &pdata->name); 792 751 793 752 device_for_each_child_node(dev, child) { 794 - if (is_of_node(child)) 795 - button->irq = 796 - irq_of_parse_and_map(to_of_node(child), 0); 753 + if (is_of_node(child)) { 754 + irq = of_irq_get_byname(to_of_node(child), "irq"); 755 + if (irq > 0) 756 + button->irq = irq; 757 + 758 + irq = of_irq_get_byname(to_of_node(child), "wakeup"); 759 + if (irq > 0) 760 + button->wakeirq = irq; 761 + 762 + if (!button->irq && !button->wakeirq) 763 + button->irq = 764 + irq_of_parse_and_map(to_of_node(child), 0); 765 + } 797 766 798 767 if (fwnode_property_read_u32(child, "linux,code", 799 768 &button->code)) { ··· 972 921 } 973 922 } 974 923 924 + if (bdata->wakeirq) { 925 + enable_irq(bdata->wakeirq); 926 + disable_irq(bdata->irq); 927 + } 928 + 975 929 return 0; 976 930 } 977 931 ··· 984 928 gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata) 985 929 { 986 930 int error; 931 + 932 + if (bdata->wakeirq) { 933 + enable_irq(bdata->irq); 934 + disable_irq(bdata->wakeirq); 935 + } 987 936 988 937 /* 989 938 * The trigger type is always both edges for gpio-based keys and we do
+2
include/linux/gpio_keys.h
··· 21 21 * disable button via sysfs 22 22 * @value: axis value for %EV_ABS 23 23 * @irq: Irq number in case of interrupt keys 24 + * @wakeirq: Optional dedicated wake-up interrupt 24 25 */ 25 26 struct gpio_keys_button { 26 27 unsigned int code; ··· 35 34 bool can_disable; 36 35 int value; 37 36 unsigned int irq; 37 + unsigned int wakeirq; 38 38 }; 39 39 40 40 /**