Skip to content

Commit 211e4d2

Browse files
asemjonovscarlescufi
authored andcommitted
gpio: Add driver support for software based gpio debounce
Software based GPIO debounce driver implementation. Signed-off-by: Al Semjonovs <[email protected]>
1 parent 643d877 commit 211e4d2

File tree

5 files changed

+350
-0
lines changed

5 files changed

+350
-0
lines changed

drivers/gpio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,4 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NPM6001 gpio_npm6001.c)
7070
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s.c)
7171
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s_port.c)
7272
zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c)
73+
zephyr_library_sources_ifdef(CONFIG_GPIO_KEYS_ZEPHYR gpio_keys_zephyr.c)

drivers/gpio/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,6 @@ source "drivers/gpio/Kconfig.rt1718s"
162162

163163
source "drivers/gpio/Kconfig.numicro"
164164

165+
source "drivers/gpio/Kconfig.zephyr"
166+
165167
endif # GPIO

drivers/gpio/Kconfig.zephyr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2022 Google LLC
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config GPIO_KEYS_ZEPHYR
5+
bool "Zephyr GPIO Keys"
6+
default y
7+
depends on DT_HAS_ZEPHYR_GPIO_KEYS_ENABLED
8+
help
9+
Enable support for Zephyr GPIO Keys.

drivers/gpio/gpio_keys_zephyr.c

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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)

include/zephyr/drivers/gpio_keys.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2022 Google LLC
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef ZEPHYR_INCLUDE_DRIVERS_GPIO_KEYS_H_
8+
#define ZEPHYR_INCLUDE_DRIVERS_GPIO_KEYS_H_
9+
10+
#include <stdint.h>
11+
#include <zephyr/drivers/gpio.h>
12+
13+
#ifdef __cplusplus
14+
extern "C" {
15+
#endif
16+
17+
struct gpio_keys_callback {
18+
struct gpio_callback gpio_cb;
19+
uint32_t zephyr_code;
20+
int8_t pin_state;
21+
};
22+
23+
typedef void (*gpio_keys_callback_handler_t)(const struct device *port,
24+
struct gpio_keys_callback *cb, gpio_port_pins_t pins);
25+
/**
26+
* @brief GPIO Keys Driver APIs
27+
* @defgroup gpio_keys_interface GPIO KeysDriver APIs
28+
* @ingroup io_interfaces
29+
* @{
30+
*/
31+
32+
__subsystem struct gpio_keys_api {
33+
int (*enable_interrupt)(const struct device *dev, gpio_keys_callback_handler_t cb);
34+
int (*disable_interrupt)(const struct device *dev);
35+
int (*get_pin)(const struct device *dev, uint32_t idx);
36+
};
37+
38+
/**
39+
* @brief Enable interrupt
40+
*
41+
* @param dev Pointer to device structure for the driver instance.
42+
* @param cb Function callback to be invoked after GPIO key has been debounced
43+
*
44+
* @return 0 If successful
45+
*/
46+
__syscall int gpio_keys_enable_interrupt(const struct device *dev, gpio_keys_callback_handler_t cb);
47+
48+
static inline int z_impl_gpio_keys_enable_interrupt(const struct device *dev,
49+
gpio_keys_callback_handler_t cb)
50+
{
51+
struct gpio_keys_api *api;
52+
53+
api = (struct gpio_keys_api *)dev->api;
54+
return api->enable_interrupt(dev, cb);
55+
}
56+
57+
/**
58+
* @brief Disable interrupt
59+
*
60+
* @param dev Pointer to device structure for the driver instance.
61+
*
62+
* @return 0 If successful
63+
*/
64+
__syscall int gpio_keys_disable_interrupt(const struct device *dev);
65+
66+
static inline int z_impl_gpio_keys_disable_interrupt(const struct device *dev)
67+
{
68+
struct gpio_keys_api *api;
69+
70+
api = (struct gpio_keys_api *)dev->api;
71+
return api->disable_interrupt(dev);
72+
}
73+
74+
/**
75+
* @brief Get the logical level of GPIO Key
76+
*
77+
* @param dev Pointer to device structure for the driver instance.
78+
* @param idx GPIO Key index in device tree
79+
*
80+
* @retval 0 If successful.
81+
* @retval -EIO I/O error when accessing an external GPIO chip.
82+
* @retval -EWOULDBLOCK if operation would block.
83+
*/
84+
__syscall int gpio_keys_get_pin(const struct device *dev, uint32_t idx);
85+
86+
static inline int z_impl_gpio_keys_get_pin(const struct device *dev, uint32_t idx)
87+
{
88+
struct gpio_keys_api *api;
89+
90+
api = (struct gpio_keys_api *)dev->api;
91+
return api->get_pin(dev, idx);
92+
}
93+
94+
/**
95+
* @}
96+
*/
97+
98+
#ifdef __cplusplus
99+
}
100+
#endif
101+
102+
#include <syscalls/gpio_keys.h>
103+
104+
#endif /* ZEPHYR_INCLUDE_DRIVERS_GPIO_KEYS_H_ */

0 commit comments

Comments
 (0)