diff --git a/drivers/input/input_gpio_keys.c b/drivers/input/input_gpio_keys.c index e9df3cb907d632c..648349e3c956a07 100644 --- a/drivers/input/input_gpio_keys.c +++ b/drivers/input/input_gpio_keys.c @@ -13,6 +13,7 @@ #include #include #include +#include LOG_MODULE_REGISTER(gpio_keys, CONFIG_INPUT_LOG_LEVEL); @@ -27,35 +28,43 @@ struct gpio_keys_pin_config { /** Zephyr code from devicetree */ uint32_t zephyr_code; }; + +struct gpio_keys_pin_data { + const struct device *dev; + struct gpio_keys_callback cb_data; + struct k_work_delayable work; + int8_t pin_state; +}; + struct gpio_keys_config { /** Debounce interval in milliseconds from devicetree */ uint32_t debounce_interval_ms; const int num_keys; const struct gpio_keys_pin_config *pin_cfg; + struct gpio_keys_pin_data *pin_data; + k_work_handler_t handler; + bool no_interrupts; }; -struct gpio_keys_pin_data { - const struct device *dev; - struct gpio_keys_callback cb_data; - struct k_work_delayable work; - int8_t pin_state; +struct gpio_keys_data { +#ifdef CONFIG_PM_DEVICE + atomic_t suspended; +#endif }; /** * Handle debounced gpio pin state. */ -static void gpio_keys_change_deferred(struct k_work *work) +static void gpio_keys_poll_pin(const struct device *dev, int key_index) { - struct k_work_delayable *dwork = k_work_delayable_from_work(work); - struct gpio_keys_pin_data *pin_data = CONTAINER_OF(dwork, struct gpio_keys_pin_data, work); - const struct device *dev = pin_data->dev; - int key_index = pin_data - (struct gpio_keys_pin_data *)dev->data; const struct gpio_keys_config *cfg = dev->config; const struct gpio_keys_pin_config *pin_cfg = &cfg->pin_cfg[key_index]; + struct gpio_keys_pin_data *pin_data = &cfg->pin_data[key_index]; + int new_pressed; - const int new_pressed = gpio_pin_get(pin_cfg->spec.port, pin_cfg->spec.pin); + new_pressed = gpio_pin_get(pin_cfg->spec.port, pin_cfg->spec.pin); - LOG_DBG("gpio_change_deferred %s pin_state=%d, new_pressed=%d, key_index=%d", dev->name, + LOG_DBG("%s: pin_state=%d, new_pressed=%d, key_index=%d", dev->name, pin_data->cb_data.pin_state, new_pressed, key_index); /* If gpio changed, report the event */ @@ -67,6 +76,39 @@ static void gpio_keys_change_deferred(struct k_work *work) } } +static __maybe_unused void gpio_keys_poll_pins(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct gpio_keys_pin_data *pin_data = CONTAINER_OF(dwork, struct gpio_keys_pin_data, work); + const struct device *dev = pin_data->dev; + const struct gpio_keys_config *cfg = dev->config; + +#ifdef CONFIG_PM_DEVICE + struct gpio_keys_data *data = dev->data; + + if (atomic_get(&data->suspended) == 1) { + return; + } +#endif + + for (int i = 0; i < cfg->num_keys; i++) { + gpio_keys_poll_pin(dev, i); + } + + k_work_reschedule(dwork, K_MSEC(cfg->debounce_interval_ms)); +} + +static __maybe_unused void gpio_keys_change_deferred(struct k_work *work) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(work); + struct gpio_keys_pin_data *pin_data = CONTAINER_OF(dwork, struct gpio_keys_pin_data, work); + const struct device *dev = pin_data->dev; + const struct gpio_keys_config *cfg = dev->config; + int key_index = pin_data - (struct gpio_keys_pin_data *)cfg->pin_data; + + gpio_keys_poll_pin(dev, key_index); +} + static void gpio_keys_interrupt(const struct device *dev, struct gpio_callback *cbdata, uint32_t pins) { @@ -97,7 +139,7 @@ static int gpio_keys_interrupt_configure(const struct gpio_dt_spec *gpio_spec, cb->pin_state = -1; - LOG_DBG("%s [0x%p, %d]", __func__, gpio_spec->port, gpio_spec->pin); + LOG_DBG("port=%s, pin=%d", gpio_spec->port->name, gpio_spec->pin); ret = gpio_pin_interrupt_configure_dt(gpio_spec, GPIO_INT_EDGE_BOTH); if (ret < 0) { @@ -110,8 +152,8 @@ static int gpio_keys_interrupt_configure(const struct gpio_dt_spec *gpio_spec, static int gpio_keys_init(const struct device *dev) { - struct gpio_keys_pin_data *pin_data = dev->data; const struct gpio_keys_config *cfg = dev->config; + struct gpio_keys_pin_data *pin_data = cfg->pin_data; int ret; for (int i = 0; i < cfg->num_keys; i++) { @@ -129,7 +171,11 @@ static int gpio_keys_init(const struct device *dev) } pin_data[i].dev = dev; - k_work_init_delayable(&pin_data[i].work, gpio_keys_change_deferred); + k_work_init_delayable(&pin_data[i].work, cfg->handler); + + if (cfg->no_interrupts) { + continue; + } ret = gpio_keys_interrupt_configure(&cfg->pin_cfg[i].spec, &pin_data[i].cb_data, @@ -140,6 +186,11 @@ static int gpio_keys_init(const struct device *dev) } } + if (cfg->no_interrupts) { + /* use pin 0 work to poll all the pins periodically */ + k_work_reschedule(&pin_data[0].work, K_MSEC(cfg->debounce_interval_ms)); + } + ret = pm_device_runtime_enable(dev); if (ret < 0) { LOG_ERR("Failed to enable runtime power management"); @@ -154,6 +205,8 @@ static int gpio_keys_pm_action(const struct device *dev, enum pm_device_action action) { const struct gpio_keys_config *cfg = dev->config; + struct gpio_keys_data *data = dev->data; + struct gpio_keys_pin_data *pin_data = cfg->pin_data; gpio_flags_t gpio_flags; gpio_flags_t int_flags; int ret; @@ -162,10 +215,12 @@ static int gpio_keys_pm_action(const struct device *dev, case PM_DEVICE_ACTION_SUSPEND: gpio_flags = GPIO_DISCONNECTED; int_flags = GPIO_INT_DISABLE; + atomic_set(&data->suspended, 1); break; case PM_DEVICE_ACTION_RESUME: gpio_flags = GPIO_INPUT; int_flags = GPIO_INT_EDGE_BOTH; + atomic_set(&data->suspended, 0); break; default: return -ENOTSUP; @@ -180,6 +235,10 @@ static int gpio_keys_pm_action(const struct device *dev, return ret; } + if (cfg->no_interrupts) { + continue; + } + ret = gpio_pin_interrupt_configure_dt(gpio, int_flags); if (ret < 0) { LOG_ERR("interrupt configuration failed: %d", ret); @@ -187,6 +246,11 @@ static int gpio_keys_pm_action(const struct device *dev, } } + if (action == PM_DEVICE_ACTION_RESUME && cfg->no_interrupts) { + k_work_reschedule(&pin_data[0].work, + K_MSEC(cfg->debounce_interval_ms)); + } + return 0; } #endif @@ -207,19 +271,25 @@ static int gpio_keys_pm_action(const struct device *dev, static const struct gpio_keys_pin_config gpio_keys_pin_config_##i[] = { \ DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(i, GPIO_KEYS_CFG_DEF, (,))}; \ \ + static struct gpio_keys_pin_data \ + gpio_keys_pin_data_##i[ARRAY_SIZE(gpio_keys_pin_config_##i)]; \ + \ static const struct gpio_keys_config gpio_keys_config_##i = { \ .debounce_interval_ms = DT_INST_PROP(i, debounce_interval_ms), \ .num_keys = ARRAY_SIZE(gpio_keys_pin_config_##i), \ .pin_cfg = gpio_keys_pin_config_##i, \ + .pin_data = gpio_keys_pin_data_##i, \ + .handler = COND_CODE_1(DT_INST_PROP(i, no_interrupts), \ + (gpio_keys_poll_pins), (gpio_keys_change_deferred)), \ + .no_interrupts = DT_INST_PROP(i, no_interrupts), \ }; \ \ - static struct gpio_keys_pin_data \ - gpio_keys_pin_data_##i[ARRAY_SIZE(gpio_keys_pin_config_##i)]; \ + static struct gpio_keys_data gpio_keys_data_##i; \ \ - PM_DEVICE_DT_INST_DEFINE(n, gpio_keys_pm_action); \ + PM_DEVICE_DT_INST_DEFINE(i, gpio_keys_pm_action); \ \ - DEVICE_DT_INST_DEFINE(i, &gpio_keys_init, PM_DEVICE_DT_INST_GET(n), \ - gpio_keys_pin_data_##i, &gpio_keys_config_##i, \ + DEVICE_DT_INST_DEFINE(i, &gpio_keys_init, PM_DEVICE_DT_INST_GET(i), \ + &gpio_keys_data_##i, &gpio_keys_config_##i, \ POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL); DT_INST_FOREACH_STATUS_OKAY(GPIO_KEYS_INIT) diff --git a/dts/bindings/input/gpio-keys.yaml b/dts/bindings/input/gpio-keys.yaml index 9b3581a1c13cb92..8035d76c9bbab31 100644 --- a/dts/bindings/input/gpio-keys.yaml +++ b/dts/bindings/input/gpio-keys.yaml @@ -35,15 +35,23 @@ properties: Debouncing interval time in milliseconds. If not specified defaults to 30. + no-interrupts: + type: boolean + description: | + Do not use interrupts for the key GPIOs, poll the pin periodically at the + specified debounce-interval-ms instead. + child-binding: description: GPIO KEYS child node properties: gpios: type: phandle-array required: true + label: type: string description: Descriptive name of the key + zephyr,code: type: int description: Key code to emit. diff --git a/tests/drivers/build_all/input/app.overlay b/tests/drivers/build_all/input/app.overlay index 0a005414667165f..b12dd6bfa16944a 100644 --- a/tests/drivers/build_all/input/app.overlay +++ b/tests/drivers/build_all/input/app.overlay @@ -26,6 +26,16 @@ }; }; + gpio-keys-polled { + compatible = "gpio-keys"; + debounce-interval-ms = <30>; + button_0 { + gpios = <&test_gpio 0 0>; + zephyr,code = <0>; + }; + no-interrupts; + }; + evdev { compatible = "zephyr,native-linux-evdev"; };