1 /*
2  * Copyright (c) 2018, Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/sensor.h>
8 #include <zephyr/pm/device.h>
9 #include <zephyr/drivers/pinctrl.h>
10 #include <soc.h>
11 
12 #include <nrfx_qdec.h>
13 #include <hal/nrf_gpio.h>
14 
15 #include <zephyr/logging/log.h>
16 #include <zephyr/irq.h>
17 LOG_MODULE_REGISTER(qdec_nrfx, CONFIG_SENSOR_LOG_LEVEL);
18 
19 #define DT_DRV_COMPAT nordic_nrf_qdec
20 
21 #define FULL_ANGLE 360
22 
23 /* limit range to avoid overflow when converting steps to degrees */
24 #define ACC_MAX (INT_MAX / FULL_ANGLE)
25 #define ACC_MIN (INT_MIN / FULL_ANGLE)
26 
27 
28 struct qdec_nrfx_data {
29 	int32_t fetched_acc;
30 	int32_t acc;
31 	bool overflow;
32 	sensor_trigger_handler_t data_ready_handler;
33 	const struct sensor_trigger *data_ready_trigger;
34 };
35 
36 struct qdec_nrfx_config {
37 	nrfx_qdec_t qdec;
38 	nrfx_qdec_config_t config;
39 	void (*irq_connect)(void);
40 	const struct pinctrl_dev_config *pcfg;
41 	uint32_t enable_pin;
42 	int32_t steps;
43 };
44 
accumulate(struct qdec_nrfx_data * data,int32_t acc)45 static void accumulate(struct qdec_nrfx_data *data, int32_t acc)
46 {
47 	unsigned int key = irq_lock();
48 
49 	bool overflow = ((acc > 0) && (ACC_MAX - acc < data->acc)) ||
50 			((acc < 0) && (ACC_MIN - acc > data->acc));
51 
52 	if (!overflow) {
53 		data->acc += acc;
54 	} else {
55 		data->overflow = true;
56 	}
57 
58 	irq_unlock(key);
59 }
60 
qdec_nrfx_sample_fetch(const struct device * dev,enum sensor_channel chan)61 static int qdec_nrfx_sample_fetch(const struct device *dev,
62 				  enum sensor_channel chan)
63 {
64 	const struct qdec_nrfx_config *config = dev->config;
65 	struct qdec_nrfx_data *data = dev->data;
66 	int32_t acc;
67 	uint32_t accdbl;
68 
69 	if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
70 		return -ENOTSUP;
71 	}
72 
73 	nrfx_qdec_accumulators_read(&config->qdec, &acc, &accdbl);
74 
75 	accumulate(data, acc);
76 
77 	unsigned int key = irq_lock();
78 
79 	data->fetched_acc = data->acc;
80 	data->acc = 0;
81 
82 	irq_unlock(key);
83 
84 	if (data->overflow) {
85 		data->overflow = false;
86 		return -EOVERFLOW;
87 	}
88 
89 	return 0;
90 }
91 
qdec_nrfx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)92 static int qdec_nrfx_channel_get(const struct device *dev,
93 				 enum sensor_channel chan,
94 				 struct sensor_value *val)
95 {
96 	struct qdec_nrfx_data *data = dev->data;
97 	const struct qdec_nrfx_config *config = dev->config;
98 	unsigned int key;
99 	int32_t acc;
100 
101 	if (chan != SENSOR_CHAN_ROTATION) {
102 		return -ENOTSUP;
103 	}
104 
105 	key = irq_lock();
106 	acc = data->fetched_acc;
107 	irq_unlock(key);
108 
109 	val->val1 = (acc * FULL_ANGLE) / config->steps;
110 	val->val2 = (acc * FULL_ANGLE) - (val->val1 * config->steps);
111 	if (val->val2 != 0) {
112 		val->val2 *= 1000000;
113 		val->val2 /= config->steps;
114 	}
115 
116 	return 0;
117 }
118 
qdec_nrfx_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)119 static int qdec_nrfx_trigger_set(const struct device *dev,
120 				 const struct sensor_trigger *trig,
121 				 sensor_trigger_handler_t handler)
122 {
123 	struct qdec_nrfx_data *data = dev->data;
124 	unsigned int key;
125 
126 	if (trig->type != SENSOR_TRIG_DATA_READY) {
127 		return -ENOTSUP;
128 	}
129 
130 	if ((trig->chan != SENSOR_CHAN_ALL) &&
131 	    (trig->chan != SENSOR_CHAN_ROTATION)) {
132 		return -ENOTSUP;
133 	}
134 
135 	key = irq_lock();
136 	data->data_ready_handler = handler;
137 	data->data_ready_trigger = trig;
138 	irq_unlock(key);
139 
140 	return 0;
141 }
142 
qdec_nrfx_event_handler(nrfx_qdec_event_t event,void * p_context)143 static void qdec_nrfx_event_handler(nrfx_qdec_event_t event, void *p_context)
144 {
145 	const struct device *dev = p_context;
146 	struct qdec_nrfx_data *dev_data = dev->data;
147 
148 	sensor_trigger_handler_t handler;
149 	const struct sensor_trigger *trig;
150 	unsigned int key;
151 
152 	switch (event.type) {
153 	case NRF_QDEC_EVENT_REPORTRDY:
154 		accumulate(dev_data, event.data.report.acc);
155 
156 		key = irq_lock();
157 		handler = dev_data->data_ready_handler;
158 		trig = dev_data->data_ready_trigger;
159 		irq_unlock(key);
160 
161 		if (handler) {
162 			handler(dev, trig);
163 		}
164 		break;
165 
166 	case NRF_QDEC_EVENT_ACCOF:
167 		dev_data->overflow = true;
168 		break;
169 
170 	default:
171 		LOG_ERR("unhandled event (0x%x)", event.type);
172 		break;
173 	}
174 }
175 
qdec_nrfx_gpio_ctrl(const struct device * dev,bool enable)176 static void qdec_nrfx_gpio_ctrl(const struct device *dev, bool enable)
177 {
178 	const struct qdec_nrfx_config *config = dev->config;
179 
180 	if (config->enable_pin != NRF_QDEC_PIN_NOT_CONNECTED) {
181 
182 		uint32_t val = (enable)?(0):(1);
183 
184 		nrf_gpio_pin_write(config->enable_pin, val);
185 		nrf_gpio_cfg_output(config->enable_pin);
186 	}
187 }
188 
189 static DEVICE_API(sensor, qdec_nrfx_driver_api) = {
190 	.sample_fetch = qdec_nrfx_sample_fetch,
191 	.channel_get  = qdec_nrfx_channel_get,
192 	.trigger_set  = qdec_nrfx_trigger_set,
193 };
194 
qdec_pm_suspend(const struct device * dev)195 static void qdec_pm_suspend(const struct device *dev)
196 {
197 	const struct qdec_nrfx_config *config = dev->config;
198 
199 	nrfx_qdec_disable(&config->qdec);
200 	qdec_nrfx_gpio_ctrl(dev, false);
201 
202 	(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
203 }
204 
qdec_pm_resume(const struct device * dev)205 static void qdec_pm_resume(const struct device *dev)
206 {
207 	const struct qdec_nrfx_config *config = dev->config;
208 
209 	(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
210 	qdec_nrfx_gpio_ctrl(dev, true);
211 	nrfx_qdec_enable(&config->qdec);
212 }
213 
qdec_nrfx_pm_action(const struct device * dev,enum pm_device_action action)214 static int qdec_nrfx_pm_action(const struct device *dev, enum pm_device_action action)
215 {
216 	switch (action) {
217 	case PM_DEVICE_ACTION_RESUME:
218 		qdec_pm_resume(dev);
219 		break;
220 
221 	case PM_DEVICE_ACTION_SUSPEND:
222 		if (IS_ENABLED(CONFIG_PM_DEVICE)) {
223 			qdec_pm_suspend(dev);
224 		}
225 		break;
226 	default:
227 		return -ENOTSUP;
228 		break;
229 	}
230 
231 	return 0;
232 }
233 
qdec_nrfx_init(const struct device * dev)234 static int qdec_nrfx_init(const struct device *dev)
235 {
236 	const struct qdec_nrfx_config *config = dev->config;
237 	nrfx_err_t nerr;
238 
239 	config->irq_connect();
240 
241 	nerr = nrfx_qdec_init(&config->qdec, &config->config, qdec_nrfx_event_handler, (void *)dev);
242 	if (nerr != NRFX_SUCCESS) {
243 		return (nerr == NRFX_ERROR_INVALID_STATE) ? -EBUSY : -EFAULT;
244 	}
245 
246 	/* End up in suspend state. */
247 	qdec_nrfx_gpio_ctrl(dev, false);
248 	if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
249 		(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
250 	}
251 
252 	return pm_device_driver_init(dev, qdec_nrfx_pm_action);
253 }
254 
255 #define QDEC(idx)			DT_NODELABEL(qdec##idx)
256 #define QDEC_PROP(idx, prop)		DT_PROP(QDEC(idx), prop)
257 
258 #define SENSOR_NRFX_QDEC_DEVICE(idx)							     \
259 	NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(QDEC(idx));					     \
260 	BUILD_ASSERT(QDEC_PROP(idx, steps) > 0,						     \
261 		     "Wrong QDEC"#idx" steps setting in dts. Only positive number valid");   \
262 	BUILD_ASSERT(QDEC_PROP(idx, steps) <= 2048,					     \
263 		     "Wrong QDEC"#idx" steps setting in dts. Overflow possible");	     \
264 	static void irq_connect##idx(void)						     \
265 	{										     \
266 		IRQ_CONNECT(DT_IRQN(QDEC(idx)), DT_IRQ(QDEC(idx), priority),		     \
267 			    nrfx_isr, nrfx_qdec_##idx##_irq_handler, 0);		     \
268 	}										     \
269 	static struct qdec_nrfx_data qdec_##idx##_data;					     \
270 	PINCTRL_DT_DEFINE(QDEC(idx));							     \
271 	static struct qdec_nrfx_config qdec_##idx##_config = {				     \
272 		.qdec = NRFX_QDEC_INSTANCE(idx),					     \
273 		.config = {								     \
274 			.reportper = NRF_QDEC_REPORTPER_40,				     \
275 			.sampleper = NRF_QDEC_SAMPLEPER_2048US,				     \
276 			.skip_gpio_cfg = true,						     \
277 			.skip_psel_cfg = true,						     \
278 			.ledpre  = QDEC_PROP(idx, led_pre),				     \
279 			.ledpol  = NRF_QDEC_LEPOL_ACTIVE_HIGH,				     \
280 			.reportper_inten = true,					     \
281 		},									     \
282 		.irq_connect = irq_connect##idx,					     \
283 		.pcfg = PINCTRL_DT_DEV_CONFIG_GET(QDEC(idx)),				     \
284 		.enable_pin = DT_PROP_OR(QDEC(idx), enable_pin, NRF_QDEC_PIN_NOT_CONNECTED), \
285 		.steps = QDEC_PROP(idx, steps),						     \
286 	};										     \
287 	PM_DEVICE_DT_DEFINE(QDEC(idx), qdec_nrfx_pm_action, PM_DEVICE_ISR_SAFE);	     \
288 	SENSOR_DEVICE_DT_DEFINE(QDEC(idx),						     \
289 				qdec_nrfx_init,						     \
290 				PM_DEVICE_DT_GET(QDEC(idx)),				     \
291 				&qdec_##idx##_data,					     \
292 				&qdec_##idx##_config,					     \
293 				POST_KERNEL,						     \
294 				CONFIG_SENSOR_INIT_PRIORITY,				     \
295 				&qdec_nrfx_driver_api)
296 
297 #ifdef CONFIG_HAS_HW_NRF_QDEC0
298 SENSOR_NRFX_QDEC_DEVICE(0);
299 #endif
300 
301 #ifdef CONFIG_HAS_HW_NRF_QDEC1
302 SENSOR_NRFX_QDEC_DEVICE(1);
303 #endif
304 
305 #ifdef CONFIG_HAS_HW_NRF_QDEC20
306 SENSOR_NRFX_QDEC_DEVICE(20);
307 #endif
308 
309 #ifdef CONFIG_HAS_HW_NRF_QDEC21
310 SENSOR_NRFX_QDEC_DEVICE(21);
311 #endif
312 
313 #ifdef CONFIG_HAS_HW_NRF_QDEC130
314 SENSOR_NRFX_QDEC_DEVICE(130);
315 #endif
316 
317 #ifdef CONFIG_HAS_HW_NRF_QDEC131
318 SENSOR_NRFX_QDEC_DEVICE(131);
319 #endif
320