1 /*
2 * Copyright 2023 Google LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT gpio_qdec
8
9 #include <stdint.h>
10 #include <stdlib.h>
11
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/input/input.h>
15 #include <zephyr/kernel.h>
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(input_gpio_qdec, CONFIG_INPUT_LOG_LEVEL);
19
20 #define GPIO_QDEC_GPIO_NUM 2
21
22 struct gpio_qdec_config {
23 struct gpio_dt_spec gpio[GPIO_QDEC_GPIO_NUM];
24 uint32_t sample_time_us;
25 uint32_t idle_timeout_ms;
26 uint16_t axis;
27 uint8_t steps_per_period;
28 };
29
30 struct gpio_qdec_data {
31 const struct device *dev;
32 struct k_timer sample_timer;
33 uint8_t prev_step;
34 int32_t acc;
35 struct k_work event_work;
36 struct k_work_delayable idle_work;
37 struct gpio_callback gpio_cb;
38 };
39
40 /* Positive transitions */
41 #define QDEC_LL_LH 0x01
42 #define QDEC_LH_HH 0x13
43 #define QDEC_HH_HL 0x32
44 #define QDEC_HL_LL 0x20
45
46 /* Negative transitions */
47 #define QDEC_LL_HL 0x02
48 #define QDEC_LH_LL 0x10
49 #define QDEC_HH_LH 0x31
50 #define QDEC_HL_HH 0x23
51
gpio_qdec_get_step(const struct device * dev)52 static uint8_t gpio_qdec_get_step(const struct device *dev)
53 {
54 const struct gpio_qdec_config *cfg = dev->config;
55 uint8_t step = 0x00;
56
57 if (gpio_pin_get_dt(&cfg->gpio[0])) {
58 step |= 0x01;
59 }
60 if (gpio_pin_get_dt(&cfg->gpio[1])) {
61 step |= 0x02;
62 }
63
64 return step;
65 }
66
gpio_qdec_sample_timer_timeout(struct k_timer * timer)67 static void gpio_qdec_sample_timer_timeout(struct k_timer *timer)
68 {
69 const struct device *dev = k_timer_user_data_get(timer);
70 const struct gpio_qdec_config *cfg = dev->config;
71 struct gpio_qdec_data *data = dev->data;
72 int8_t delta = 0;
73 unsigned int key;
74 uint8_t step;
75
76 step = gpio_qdec_get_step(dev);
77
78 if (data->prev_step == step) {
79 return;
80 }
81
82 switch ((data->prev_step << 4U) | step) {
83 case QDEC_LL_LH:
84 case QDEC_LH_HH:
85 case QDEC_HH_HL:
86 case QDEC_HL_LL:
87 delta = 1;
88 break;
89 case QDEC_LL_HL:
90 case QDEC_LH_LL:
91 case QDEC_HH_LH:
92 case QDEC_HL_HH:
93 delta = -1;
94 break;
95 default:
96 LOG_WRN("%s: lost steps", dev->name);
97 }
98
99 data->prev_step = step;
100
101 key = irq_lock();
102 data->acc += delta;
103 irq_unlock(key);
104
105 if (abs(data->acc) >= cfg->steps_per_period) {
106 k_work_submit(&data->event_work);
107 }
108
109 k_work_reschedule(&data->idle_work, K_MSEC(cfg->idle_timeout_ms));
110 }
111
gpio_qdec_event_worker(struct k_work * work)112 static void gpio_qdec_event_worker(struct k_work *work)
113 {
114 struct gpio_qdec_data *data = CONTAINER_OF(
115 work, struct gpio_qdec_data, event_work);
116 const struct device *dev = data->dev;
117 const struct gpio_qdec_config *cfg = dev->config;
118 unsigned int key;
119 int32_t acc;
120
121 key = irq_lock();
122 acc = data->acc / cfg->steps_per_period;
123 data->acc -= acc * cfg->steps_per_period;
124 irq_unlock(key);
125
126 if (acc != 0) {
127 input_report_rel(data->dev, cfg->axis, acc, true, K_FOREVER);
128 }
129 }
130
gpio_qdec_irq_setup(const struct device * dev,bool enable)131 static void gpio_qdec_irq_setup(const struct device *dev, bool enable)
132 {
133 const struct gpio_qdec_config *cfg = dev->config;
134 unsigned int flags = enable ? GPIO_INT_EDGE_BOTH : GPIO_INT_DISABLE;
135 int ret;
136
137 for (int i = 0; i < GPIO_QDEC_GPIO_NUM; i++) {
138 const struct gpio_dt_spec *gpio = &cfg->gpio[i];
139
140 ret = gpio_pin_interrupt_configure_dt(gpio, flags);
141 if (ret != 0) {
142 LOG_ERR("Pin %d interrupt configuration failed: %d", i, ret);
143 return;
144 }
145 }
146 }
147
gpio_qdec_idle_worker(struct k_work * work)148 static void gpio_qdec_idle_worker(struct k_work *work)
149 {
150 struct k_work_delayable *dwork = k_work_delayable_from_work(work);
151 struct gpio_qdec_data *data = CONTAINER_OF(
152 dwork, struct gpio_qdec_data, idle_work);
153 const struct device *dev = data->dev;
154
155 k_timer_stop(&data->sample_timer);
156
157 gpio_qdec_irq_setup(dev, true);
158
159 LOG_DBG("polling stop");
160 }
161
gpio_qdec_cb(const struct device * gpio_dev,struct gpio_callback * cb,uint32_t pins)162 static void gpio_qdec_cb(const struct device *gpio_dev, struct gpio_callback *cb,
163 uint32_t pins)
164 {
165 struct gpio_qdec_data *data = CONTAINER_OF(
166 cb, struct gpio_qdec_data, gpio_cb);
167 const struct device *dev = data->dev;
168 const struct gpio_qdec_config *cfg = dev->config;
169
170 gpio_qdec_irq_setup(dev, false);
171
172 k_timer_start(&data->sample_timer, K_NO_WAIT,
173 K_USEC(cfg->sample_time_us));
174
175 LOG_DBG("polling start");
176 }
177
gpio_qdec_init(const struct device * dev)178 static int gpio_qdec_init(const struct device *dev)
179 {
180 const struct gpio_qdec_config *cfg = dev->config;
181 struct gpio_qdec_data *data = dev->data;
182 int ret;
183
184 data->dev = dev;
185
186 k_work_init(&data->event_work, gpio_qdec_event_worker);
187 k_work_init_delayable(&data->idle_work, gpio_qdec_idle_worker);
188
189 k_timer_init(&data->sample_timer, gpio_qdec_sample_timer_timeout, NULL);
190 k_timer_user_data_set(&data->sample_timer, (void *)dev);
191
192 gpio_init_callback(&data->gpio_cb, gpio_qdec_cb,
193 BIT(cfg->gpio[0].pin) | BIT(cfg->gpio[1].pin));
194 for (int i = 0; i < GPIO_QDEC_GPIO_NUM; i++) {
195 const struct gpio_dt_spec *gpio = &cfg->gpio[i];
196
197 if (!gpio_is_ready_dt(gpio)) {
198 LOG_ERR("%s is not ready", gpio->port->name);
199 return -ENODEV;
200 }
201
202 ret = gpio_pin_configure_dt(gpio, GPIO_INPUT);
203 if (ret != 0) {
204 LOG_ERR("Pin %d configuration failed: %d", i, ret);
205 return ret;
206 }
207
208 ret = gpio_add_callback_dt(gpio, &data->gpio_cb);
209 if (ret < 0) {
210 LOG_ERR("Could not set gpio callback");
211 return ret;
212 }
213 }
214
215 data->prev_step = gpio_qdec_get_step(dev);
216
217 gpio_qdec_irq_setup(dev, true);
218
219 LOG_DBG("Device %s initialized", dev->name);
220
221 return 0;
222 }
223
224 #define QDEC_GPIO_INIT(n) \
225 BUILD_ASSERT(DT_INST_PROP_LEN(n, gpios) == GPIO_QDEC_GPIO_NUM, \
226 "input_gpio_qdec: gpios must have exactly two entries"); \
227 \
228 static const struct gpio_qdec_config gpio_qdec_cfg_##n = { \
229 .gpio = {GPIO_DT_SPEC_INST_GET_BY_IDX(n, gpios, 0), \
230 GPIO_DT_SPEC_INST_GET_BY_IDX(n, gpios, 1)}, \
231 .sample_time_us = DT_INST_PROP(n, sample_time_us), \
232 .idle_timeout_ms = DT_INST_PROP(n, idle_timeout_ms), \
233 .steps_per_period = DT_INST_PROP(n, steps_per_period), \
234 .axis = DT_INST_PROP(n, zephyr_axis), \
235 }; \
236 \
237 static struct gpio_qdec_data gpio_qdec_data_##n; \
238 \
239 DEVICE_DT_INST_DEFINE(n, gpio_qdec_init, NULL, \
240 &gpio_qdec_data_##n, \
241 &gpio_qdec_cfg_##n, \
242 POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
243 NULL);
244
245 DT_INST_FOREACH_STATUS_OKAY(QDEC_GPIO_INIT)
246