|
| 1 | +/* |
| 2 | + * Copyright (c) 2022 Google LLC |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <zephyr/device.h> |
| 8 | +#include <zephyr/drivers/gpio.h> |
| 9 | +#include <zephyr/drivers/gpio_keys.h> |
| 10 | +#include <zephyr/kernel.h> |
| 11 | +#include <zephyr/logging/log.h> |
| 12 | + |
| 13 | +LOG_MODULE_REGISTER(zephyr_gpio_keys, CONFIG_GPIO_LOG_LEVEL); |
| 14 | + |
| 15 | +#define DT_DRV_COMPAT zephyr_gpio_keys |
| 16 | + |
| 17 | +struct gpio_keys_pin_config { |
| 18 | + /** GPIO specification from devicetree */ |
| 19 | + struct gpio_dt_spec spec; |
| 20 | + /** Zephyr code from devicetree */ |
| 21 | + uint32_t zephyr_code; |
| 22 | +}; |
| 23 | +struct gpio_keys_config { |
| 24 | + /** Debounce interval in milliseconds from devicetree */ |
| 25 | + uint32_t debounce_interval_ms; |
| 26 | + const int num_keys; |
| 27 | + const struct gpio_keys_pin_config *pin_cfg; |
| 28 | +}; |
| 29 | + |
| 30 | +struct gpio_keys_pin_data { |
| 31 | + const struct device *dev; |
| 32 | + struct gpio_keys_callback cb_data; |
| 33 | + struct k_work_delayable work; |
| 34 | + int8_t pin_state; |
| 35 | +}; |
| 36 | + |
| 37 | +struct gpio_keys_data { |
| 38 | + gpio_keys_callback_handler_t callback; |
| 39 | + int num_keys; |
| 40 | + struct gpio_keys_pin_data *pin_data; |
| 41 | +}; |
| 42 | + |
| 43 | +/** |
| 44 | + * Handle debounced gpio pin state. |
| 45 | + */ |
| 46 | +static void gpio_keys_change_deferred(struct k_work *work) |
| 47 | +{ |
| 48 | + struct gpio_keys_pin_data *pin_data = CONTAINER_OF(work, struct gpio_keys_pin_data, work); |
| 49 | + const struct device *dev = pin_data->dev; |
| 50 | + struct gpio_keys_data *data = dev->data; |
| 51 | + int key_index = pin_data - &data->pin_data[0]; |
| 52 | + const struct gpio_keys_config *cfg = dev->config; |
| 53 | + const struct gpio_keys_pin_config *pin_cfg = &cfg->pin_cfg[key_index]; |
| 54 | + |
| 55 | + const int new_pressed = gpio_pin_get(pin_cfg->spec.port, pin_cfg->spec.pin); |
| 56 | + |
| 57 | + LOG_DBG("gpio_change_deferred %s pin_state=%d, new_pressed=%d, key_index=%d", dev->name, |
| 58 | + pin_data->cb_data.pin_state, new_pressed, key_index); |
| 59 | + |
| 60 | + /* If gpio changed, invoke callback */ |
| 61 | + if (new_pressed != pin_data->cb_data.pin_state) { |
| 62 | + pin_data->cb_data.pin_state = new_pressed; |
| 63 | + LOG_DBG("Calling callback %s %d, code=%d", dev->name, new_pressed, |
| 64 | + pin_cfg->zephyr_code); |
| 65 | + data->callback(dev, &pin_data->cb_data, BIT(pin_cfg->spec.pin)); |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +static void gpio_keys_change_call_deferred(struct gpio_keys_pin_data *data, uint32_t msec) |
| 70 | +{ |
| 71 | + int rv = k_work_reschedule(&data->work, K_MSEC(msec)); |
| 72 | + |
| 73 | + __ASSERT(rv >= 0, "Set wake mask work queue error"); |
| 74 | +} |
| 75 | + |
| 76 | +static void gpio_keys_interrupt(const struct device *dev, struct gpio_callback *cbdata, |
| 77 | + uint32_t pins) |
| 78 | +{ |
| 79 | + ARG_UNUSED(dev); /* This is a pointer to GPIO device, use dev pointer in |
| 80 | + * cbdata for pointer to gpio_debounce device node |
| 81 | + */ |
| 82 | + struct gpio_keys_pin_data *pin_data = |
| 83 | + CONTAINER_OF(cbdata, struct gpio_keys_pin_data, cb_data); |
| 84 | + const struct gpio_keys_config *cfg = pin_data->dev->config; |
| 85 | + struct gpio_keys_data *data = pin_data->dev->data; |
| 86 | + |
| 87 | + for (int i = 0; i < data->num_keys; i++) { |
| 88 | + if (pins & BIT(cfg->pin_cfg[i].spec.pin)) { |
| 89 | + gpio_keys_change_call_deferred(pin_data, cfg->debounce_interval_ms); |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +static int gpio_keys_interrupt_configure(const struct gpio_dt_spec *gpio_spec, |
| 95 | + struct gpio_keys_callback *cb, uint32_t zephyr_code) |
| 96 | +{ |
| 97 | + int retval = -ENODEV; |
| 98 | + gpio_flags_t flags; |
| 99 | + |
| 100 | + gpio_init_callback(&cb->gpio_cb, gpio_keys_interrupt, BIT(gpio_spec->pin)); |
| 101 | + gpio_add_callback(gpio_spec->port, &cb->gpio_cb); |
| 102 | + cb->zephyr_code = zephyr_code; |
| 103 | + cb->pin_state = -1; |
| 104 | + flags = GPIO_INT_EDGE_BOTH & ~GPIO_INT_MODE_DISABLED; |
| 105 | + |
| 106 | + LOG_DBG("%s [0x%p, %d]", __func__, gpio_spec->port, gpio_spec->pin); |
| 107 | + |
| 108 | + retval = z_impl_gpio_pin_interrupt_configure(gpio_spec->port, gpio_spec->pin, flags); |
| 109 | + |
| 110 | + return retval; |
| 111 | +} |
| 112 | + |
| 113 | +static int gpio_keys_zephyr_enable_interrupt(const struct device *dev, |
| 114 | + gpio_keys_callback_handler_t gpio_keys_cb) |
| 115 | +{ |
| 116 | + int retval = -ENODEV; |
| 117 | + const struct gpio_keys_config *cfg = dev->config; |
| 118 | + struct gpio_keys_data *data = dev->data; |
| 119 | + |
| 120 | + data->callback = gpio_keys_cb; |
| 121 | + for (int i = 0; i < data->num_keys; i++) { |
| 122 | + retval = gpio_keys_interrupt_configure(&cfg->pin_cfg[i].spec, |
| 123 | + &data->pin_data[i].cb_data, |
| 124 | + cfg->pin_cfg[i].zephyr_code); |
| 125 | + } |
| 126 | + |
| 127 | + return retval; |
| 128 | +} |
| 129 | + |
| 130 | +static int gpio_keys_zephyr_disable_interrupt(const struct device *dev) |
| 131 | +{ |
| 132 | + int retval = -ENODEV; |
| 133 | + const struct gpio_keys_config *cfg = dev->config; |
| 134 | + struct gpio_keys_data *data = dev->data; |
| 135 | + const struct gpio_dt_spec *gpio_spec; |
| 136 | + |
| 137 | + for (int i = 0; i < data->num_keys; i++) { |
| 138 | + gpio_spec = &cfg->pin_cfg[i].spec; |
| 139 | + retval = z_impl_gpio_pin_interrupt_configure(gpio_spec->port, gpio_spec->pin, |
| 140 | + GPIO_INT_MODE_DISABLED); |
| 141 | + if (data->pin_data[i].cb_data.gpio_cb.handler) { |
| 142 | + retval = gpio_remove_callback(gpio_spec->port, |
| 143 | + &data->pin_data[i].cb_data.gpio_cb); |
| 144 | + memset(&data->pin_data[i].cb_data, 0, sizeof(struct gpio_keys_callback)); |
| 145 | + } |
| 146 | + LOG_DBG("disable interrupt [0x%p, %d], rv=%d", gpio_spec->port, gpio_spec->pin, |
| 147 | + retval); |
| 148 | + } |
| 149 | + |
| 150 | + return retval; |
| 151 | +} |
| 152 | + |
| 153 | +static int gpio_keys_get_gpio_port_logical(const struct device *gpio_dev, gpio_port_value_t *value) |
| 154 | +{ |
| 155 | + const struct gpio_driver_data *const data = gpio_dev->data; |
| 156 | + |
| 157 | + int ret = z_impl_gpio_port_get_raw(gpio_dev, value); |
| 158 | + |
| 159 | + if (ret == 0) { |
| 160 | + *value ^= data->invert; |
| 161 | + } |
| 162 | + |
| 163 | + return ret; |
| 164 | +} |
| 165 | + |
| 166 | +static int gpio_keys_zephyr_get_pin(const struct device *dev, uint32_t idx) |
| 167 | +{ |
| 168 | + const struct gpio_keys_config *cfg = dev->config; |
| 169 | + const struct gpio_dt_spec *gpio_spec = &cfg->pin_cfg[idx].spec; |
| 170 | + const struct device *gpio_dev = gpio_spec->port; |
| 171 | + const struct gpio_driver_config *gpio_cfg = gpio_dev->config; |
| 172 | + int ret; |
| 173 | + gpio_port_value_t value; |
| 174 | + |
| 175 | + __ASSERT((gpio_cfg->port_pin_mask & (gpio_port_pins_t)BIT(gpio_spec->pin)) != 0U, |
| 176 | + "Unsupported pin"); |
| 177 | + |
| 178 | + ret = gpio_keys_get_gpio_port_logical(gpio_dev, &value); |
| 179 | + |
| 180 | + if (ret == 0) { |
| 181 | + ret = (value & (gpio_port_pins_t)BIT(gpio_spec->pin)) != 0 ? 1 : 0; |
| 182 | + } |
| 183 | + |
| 184 | + if (ret < 0) { |
| 185 | + LOG_ERR("Cannot read %s, ret=%d", dev->name, ret); |
| 186 | + ret = 0; |
| 187 | + } |
| 188 | + |
| 189 | + return ret; |
| 190 | +} |
| 191 | + |
| 192 | +static int gpio_keys_init(const struct device *dev) |
| 193 | +{ |
| 194 | + struct gpio_keys_data *data = dev->data; |
| 195 | + |
| 196 | + for (int i = 0; i < data->num_keys; i++) { |
| 197 | + data->pin_data[i].dev = dev; |
| 198 | + k_work_init_delayable(&data->pin_data[i].work, gpio_keys_change_deferred); |
| 199 | + } |
| 200 | + |
| 201 | + return 0; |
| 202 | +} |
| 203 | + |
| 204 | +static const struct gpio_keys_api gpio_keys_zephyr_api = { |
| 205 | + .enable_interrupt = gpio_keys_zephyr_enable_interrupt, |
| 206 | + .disable_interrupt = gpio_keys_zephyr_disable_interrupt, |
| 207 | + .get_pin = gpio_keys_zephyr_get_pin, |
| 208 | +}; |
| 209 | + |
| 210 | +#define GPIO_KEYS_CFG_DEF(node_id) \ |
| 211 | + { \ |
| 212 | + .spec = GPIO_DT_SPEC_GET(node_id, gpios), \ |
| 213 | + .zephyr_code = DT_PROP(node_id, zephyr_code), \ |
| 214 | + } |
| 215 | + |
| 216 | +#define GPIO_KEYS_INIT(i) \ |
| 217 | + static const struct gpio_keys_pin_config gpio_keys_pin_config_##i[] = { \ |
| 218 | + DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(i, GPIO_KEYS_CFG_DEF, (,))}; \ |
| 219 | + static struct gpio_keys_config gpio_keys_config_##i = { \ |
| 220 | + .debounce_interval_ms = DT_INST_PROP(i, debounce_interval_ms), \ |
| 221 | + .num_keys = ARRAY_SIZE(gpio_keys_pin_config_##i), \ |
| 222 | + .pin_cfg = gpio_keys_pin_config_##i, \ |
| 223 | + }; \ |
| 224 | + static struct gpio_keys_pin_data \ |
| 225 | + gpio_keys_pin_data_##i[ARRAY_SIZE(gpio_keys_pin_config_##i)]; \ |
| 226 | + static struct gpio_keys_data gpio_keys_data_##i = { \ |
| 227 | + .num_keys = ARRAY_SIZE(gpio_keys_pin_config_##i), \ |
| 228 | + .pin_data = gpio_keys_pin_data_##i, \ |
| 229 | + }; \ |
| 230 | + DEVICE_DT_INST_DEFINE(i, &gpio_keys_init, NULL, &gpio_keys_data_##i, \ |
| 231 | + &gpio_keys_config_##i, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ |
| 232 | + &gpio_keys_zephyr_api); |
| 233 | + |
| 234 | +DT_INST_FOREACH_STATUS_OKAY(GPIO_KEYS_INIT) |
0 commit comments