1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <nrfx_lpcomp.h>
8 
9 #include <zephyr/drivers/comparator/nrf_lpcomp.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/pm/device.h>
12 
13 #include <string.h>
14 
15 #define DT_DRV_COMPAT nordic_nrf_lpcomp
16 
17 #define SHIM_NRF_LPCOMP_DT_INST_REFSEL(inst) \
18 	_CONCAT(COMP_NRF_LPCOMP_REFSEL_, DT_INST_STRING_TOKEN(inst, refsel))
19 
20 #define SHIM_NRF_LPCOMP_DT_INST_REFSEL_IS_AREF(inst) \
21 	DT_INST_ENUM_HAS_VALUE(inst, refsel, AREF)
22 
23 #define SHIM_NRF_LPCOMP_DT_INST_EXTREFSEL(inst) \
24 	_CONCAT(COMP_NRF_LPCOMP_EXTREFSEL_, DT_INST_STRING_TOKEN(inst, extrefsel))
25 
26 #define SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(inst) \
27 	DT_INST_PROP(inst, enable_hyst)
28 
29 #define SHIM_NRF_LPCOMP_DT_INST_PSEL(inst) \
30 	_CONCAT(COMP_NRF_LPCOMP_PSEL_, DT_INST_STRING_TOKEN(inst, psel))
31 
32 struct shim_nrf_lpcomp_data {
33 	nrfx_lpcomp_config_t config;
34 	uint32_t event_mask;
35 	bool started;
36 	atomic_t triggered;
37 	comparator_callback_t callback;
38 	void *user_data;
39 };
40 
41 #if (NRF_LPCOMP_HAS_AIN_AS_PIN)
42 static const uint32_t shim_nrf_lpcomp_ain_map[] = {
43 #if defined(CONFIG_SOC_NRF54H20) || defined(CONFIG_SOC_NRF9280)
44 	NRF_PIN_PORT_TO_PIN_NUMBER(0U, 1),
45 	NRF_PIN_PORT_TO_PIN_NUMBER(1U, 1),
46 	NRF_PIN_PORT_TO_PIN_NUMBER(2U, 1),
47 	NRF_PIN_PORT_TO_PIN_NUMBER(3U, 1),
48 	NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1),
49 	NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1),
50 	NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1),
51 	NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1),
52 #elif defined(CONFIG_SOC_NRF54L05) || defined(CONFIG_SOC_NRF54L10) ||  defined(CONFIG_SOC_NRF54L15)
53 	NRF_PIN_PORT_TO_PIN_NUMBER(4U, 1),
54 	NRF_PIN_PORT_TO_PIN_NUMBER(5U, 1),
55 	NRF_PIN_PORT_TO_PIN_NUMBER(6U, 1),
56 	NRF_PIN_PORT_TO_PIN_NUMBER(7U, 1),
57 	NRF_PIN_PORT_TO_PIN_NUMBER(11U, 1),
58 	NRF_PIN_PORT_TO_PIN_NUMBER(12U, 1),
59 	NRF_PIN_PORT_TO_PIN_NUMBER(13U, 1),
60 	NRF_PIN_PORT_TO_PIN_NUMBER(14U, 1),
61 #endif
62 };
63 #endif
64 
65 #if (NRF_LPCOMP_HAS_AIN_AS_PIN)
66 BUILD_ASSERT(COMP_NRF_LPCOMP_PSEL_AIN0 == 0);
67 BUILD_ASSERT(COMP_NRF_LPCOMP_PSEL_AIN7 == 7);
68 BUILD_ASSERT(COMP_NRF_LPCOMP_EXTREFSEL_AIN0 == 0);
69 BUILD_ASSERT(COMP_NRF_LPCOMP_EXTREFSEL_AIN1 == 1);
70 #endif
71 
72 #if (LPCOMP_REFSEL_RESOLUTION == 8)
73 BUILD_ASSERT((SHIM_NRF_LPCOMP_DT_INST_REFSEL(0) < COMP_NRF_LPCOMP_REFSEL_VDD_1_16) ||
74 	     (SHIM_NRF_LPCOMP_DT_INST_REFSEL(0) > COMP_NRF_LPCOMP_REFSEL_VDD_15_16));
75 #endif
76 
77 #if SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(0)
78 BUILD_ASSERT(NRF_LPCOMP_HAS_HYST);
79 #endif
80 
81 static struct shim_nrf_lpcomp_data shim_nrf_lpcomp_data0;
82 
83 static const struct comp_nrf_lpcomp_config shim_nrf_lpcomp_config0 = {
84 	.psel = SHIM_NRF_LPCOMP_DT_INST_PSEL(0),
85 #if SHIM_NRF_LPCOMP_DT_INST_REFSEL_IS_AREF(0)
86 	.extrefsel = SHIM_NRF_LPCOMP_DT_INST_EXTREFSEL(0),
87 #endif
88 	.refsel = SHIM_NRF_LPCOMP_DT_INST_REFSEL(0),
89 	.enable_hyst = SHIM_NRF_LPCOMP_DT_INST_ENABLE_HYST(0),
90 };
91 
92 #if CONFIG_PM_DEVICE
shim_nrf_lpcomp_is_resumed(void)93 static bool shim_nrf_lpcomp_is_resumed(void)
94 {
95 	enum pm_device_state state;
96 
97 	(void)pm_device_state_get(DEVICE_DT_INST_GET(0), &state);
98 	return state == PM_DEVICE_STATE_ACTIVE;
99 }
100 #else
shim_nrf_lpcomp_is_resumed(void)101 static bool shim_nrf_lpcomp_is_resumed(void)
102 {
103 	return true;
104 }
105 #endif
106 
shim_nrf_lpcomp_start(void)107 static void shim_nrf_lpcomp_start(void)
108 {
109 	if (shim_nrf_lpcomp_data0.started) {
110 		return;
111 	}
112 
113 	nrfx_lpcomp_start(shim_nrf_lpcomp_data0.event_mask, 0);
114 	shim_nrf_lpcomp_data0.started = true;
115 }
116 
shim_nrf_lpcomp_stop(void)117 static void shim_nrf_lpcomp_stop(void)
118 {
119 	if (!shim_nrf_lpcomp_data0.started) {
120 		return;
121 	}
122 
123 	nrfx_lpcomp_stop();
124 	shim_nrf_lpcomp_data0.started = false;
125 }
126 
shim_nrf_lpcomp_pm_callback(const struct device * dev,enum pm_device_action action)127 static int shim_nrf_lpcomp_pm_callback(const struct device *dev, enum pm_device_action action)
128 {
129 	ARG_UNUSED(dev);
130 
131 	ARG_UNUSED(dev);
132 
133 	switch (action) {
134 	case PM_DEVICE_ACTION_RESUME:
135 		shim_nrf_lpcomp_start();
136 		break;
137 
138 #if CONFIG_PM_DEVICE
139 	case PM_DEVICE_ACTION_SUSPEND:
140 		shim_nrf_lpcomp_stop();
141 		break;
142 #endif
143 
144 	default:
145 		return -ENOTSUP;
146 	}
147 
148 	return 0;
149 }
150 
151 #if (NRF_LPCOMP_HAS_AIN_AS_PIN)
shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim,nrf_lpcomp_input_t * nrf)152 static int shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim,
153 				       nrf_lpcomp_input_t *nrf)
154 {
155 	if (shim >= ARRAY_SIZE(shim_nrf_lpcomp_ain_map)) {
156 		return -EINVAL;
157 	}
158 
159 	*nrf = shim_nrf_lpcomp_ain_map[(uint32_t)shim];
160 	return 0;
161 }
162 #else
shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim,nrf_lpcomp_input_t * nrf)163 static int shim_nrf_lpcomp_psel_to_nrf(enum comp_nrf_lpcomp_psel shim,
164 				       nrf_lpcomp_input_t *nrf)
165 {
166 	switch (shim) {
167 	case COMP_NRF_LPCOMP_PSEL_AIN0:
168 		*nrf = NRF_LPCOMP_INPUT_0;
169 		break;
170 
171 	case COMP_NRF_LPCOMP_PSEL_AIN1:
172 		*nrf = NRF_LPCOMP_INPUT_1;
173 		break;
174 
175 	case COMP_NRF_LPCOMP_PSEL_AIN2:
176 		*nrf = NRF_LPCOMP_INPUT_2;
177 		break;
178 
179 	case COMP_NRF_LPCOMP_PSEL_AIN3:
180 		*nrf = NRF_LPCOMP_INPUT_3;
181 		break;
182 
183 	case COMP_NRF_LPCOMP_PSEL_AIN4:
184 		*nrf = NRF_LPCOMP_INPUT_4;
185 		break;
186 
187 	case COMP_NRF_LPCOMP_PSEL_AIN5:
188 		*nrf = NRF_LPCOMP_INPUT_5;
189 		break;
190 
191 	case COMP_NRF_LPCOMP_PSEL_AIN6:
192 		*nrf = NRF_LPCOMP_INPUT_6;
193 		break;
194 
195 	case COMP_NRF_LPCOMP_PSEL_AIN7:
196 		*nrf = NRF_LPCOMP_INPUT_7;
197 		break;
198 
199 	default:
200 		return -EINVAL;
201 	}
202 
203 	return 0;
204 }
205 #endif
206 
207 #if (NRF_LPCOMP_HAS_AIN_AS_PIN)
shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim,nrf_lpcomp_ext_ref_t * nrf)208 static int shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim,
209 					    nrf_lpcomp_ext_ref_t *nrf)
210 {
211 	if (shim >= ARRAY_SIZE(shim_nrf_lpcomp_ain_map)) {
212 		return -EINVAL;
213 	}
214 
215 	*nrf = shim_nrf_lpcomp_ain_map[shim];
216 	return 0;
217 }
218 #else
shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim,nrf_lpcomp_ext_ref_t * nrf)219 static int shim_nrf_lpcomp_extrefsel_to_nrf(enum comp_nrf_lpcomp_extrefsel shim,
220 					    nrf_lpcomp_ext_ref_t *nrf)
221 {
222 	switch (shim) {
223 	case COMP_NRF_LPCOMP_EXTREFSEL_AIN0:
224 		*nrf = NRF_LPCOMP_EXT_REF_REF0;
225 		break;
226 
227 	case COMP_NRF_LPCOMP_EXTREFSEL_AIN1:
228 		*nrf = NRF_LPCOMP_EXT_REF_REF1;
229 		break;
230 
231 	default:
232 		return -EINVAL;
233 	}
234 
235 	return 0;
236 }
237 #endif
238 
shim_nrf_lpcomp_refsel_to_nrf(enum comp_nrf_lpcomp_refsel shim,nrf_lpcomp_ref_t * nrf)239 static int shim_nrf_lpcomp_refsel_to_nrf(enum comp_nrf_lpcomp_refsel shim,
240 					 nrf_lpcomp_ref_t *nrf)
241 {
242 	switch (shim) {
243 	case COMP_NRF_LPCOMP_REFSEL_VDD_1_8:
244 		*nrf = NRF_LPCOMP_REF_SUPPLY_1_8;
245 		break;
246 
247 	case COMP_NRF_LPCOMP_REFSEL_VDD_2_8:
248 		*nrf = NRF_LPCOMP_REF_SUPPLY_2_8;
249 		break;
250 
251 	case COMP_NRF_LPCOMP_REFSEL_VDD_3_8:
252 		*nrf = NRF_LPCOMP_REF_SUPPLY_3_8;
253 		break;
254 
255 	case COMP_NRF_LPCOMP_REFSEL_VDD_4_8:
256 		*nrf = NRF_LPCOMP_REF_SUPPLY_4_8;
257 		break;
258 
259 	case COMP_NRF_LPCOMP_REFSEL_VDD_5_8:
260 		*nrf = NRF_LPCOMP_REF_SUPPLY_5_8;
261 		break;
262 
263 	case COMP_NRF_LPCOMP_REFSEL_VDD_6_8:
264 		*nrf = NRF_LPCOMP_REF_SUPPLY_6_8;
265 		break;
266 
267 	case COMP_NRF_LPCOMP_REFSEL_VDD_7_8:
268 		*nrf = NRF_LPCOMP_REF_SUPPLY_7_8;
269 		break;
270 
271 #if (LPCOMP_REFSEL_RESOLUTION == 16)
272 	case COMP_NRF_LPCOMP_REFSEL_VDD_1_16:
273 		*nrf = NRF_LPCOMP_REF_SUPPLY_1_16;
274 		break;
275 
276 	case COMP_NRF_LPCOMP_REFSEL_VDD_3_16:
277 		*nrf = NRF_LPCOMP_REF_SUPPLY_3_16;
278 		break;
279 
280 	case COMP_NRF_LPCOMP_REFSEL_VDD_5_16:
281 		*nrf = NRF_LPCOMP_REF_SUPPLY_5_16;
282 		break;
283 
284 	case COMP_NRF_LPCOMP_REFSEL_VDD_7_16:
285 		*nrf = NRF_LPCOMP_REF_SUPPLY_7_16;
286 		break;
287 
288 	case COMP_NRF_LPCOMP_REFSEL_VDD_9_16:
289 		*nrf = NRF_LPCOMP_REF_SUPPLY_9_16;
290 		break;
291 
292 	case COMP_NRF_LPCOMP_REFSEL_VDD_11_16:
293 		*nrf = NRF_LPCOMP_REF_SUPPLY_11_16;
294 		break;
295 
296 	case COMP_NRF_LPCOMP_REFSEL_VDD_13_16:
297 		*nrf = NRF_LPCOMP_REF_SUPPLY_13_16;
298 		break;
299 
300 	case COMP_NRF_LPCOMP_REFSEL_VDD_15_16:
301 		*nrf = NRF_LPCOMP_REF_SUPPLY_15_16;
302 		break;
303 #endif
304 
305 	case COMP_NRF_LPCOMP_REFSEL_AREF:
306 		*nrf = NRF_LPCOMP_REF_EXT_REF;
307 		break;
308 
309 	default:
310 		return -EINVAL;
311 	}
312 
313 	return 0;
314 }
315 
shim_nrf_lpcomp_config_to_nrf(const struct comp_nrf_lpcomp_config * shim,nrfx_lpcomp_config_t * nrf)316 static int shim_nrf_lpcomp_config_to_nrf(const struct comp_nrf_lpcomp_config *shim,
317 					 nrfx_lpcomp_config_t *nrf)
318 {
319 	if (shim_nrf_lpcomp_refsel_to_nrf(shim->refsel, &nrf->reference)) {
320 		return -EINVAL;
321 	}
322 
323 	if (shim_nrf_lpcomp_extrefsel_to_nrf(shim->extrefsel, &nrf->ext_ref)) {
324 		return -EINVAL;
325 	}
326 
327 #if NRF_LPCOMP_HAS_HYST
328 	if (shim->enable_hyst) {
329 		nrf->hyst = NRF_LPCOMP_HYST_ENABLED;
330 	} else {
331 		nrf->hyst = NRF_LPCOMP_HYST_NOHYST;
332 	}
333 #else
334 	if (shim->enable_hyst) {
335 		return -EINVAL;
336 	}
337 #endif
338 
339 	if (shim_nrf_lpcomp_psel_to_nrf(shim->psel, &nrf->input)) {
340 		return -EINVAL;
341 	}
342 
343 	return 0;
344 }
345 
shim_nrf_lpcomp_reconfigure(void)346 static void shim_nrf_lpcomp_reconfigure(void)
347 {
348 	(void)nrfx_lpcomp_reconfigure(&shim_nrf_lpcomp_data0.config);
349 }
350 
shim_nrf_lpcomp_get_output(const struct device * dev)351 static int shim_nrf_lpcomp_get_output(const struct device *dev)
352 {
353 	ARG_UNUSED(dev);
354 
355 	return nrfx_lpcomp_sample();
356 }
357 
shim_nrf_lpcomp_set_trigger(const struct device * dev,enum comparator_trigger trigger)358 static int shim_nrf_lpcomp_set_trigger(const struct device *dev,
359 				       enum comparator_trigger trigger)
360 {
361 	shim_nrf_lpcomp_stop();
362 
363 	switch (trigger) {
364 	case COMPARATOR_TRIGGER_NONE:
365 		shim_nrf_lpcomp_data0.event_mask = 0;
366 		shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_CROSS;
367 		break;
368 
369 	case COMPARATOR_TRIGGER_RISING_EDGE:
370 		shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_UP_MASK;
371 		shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_UP;
372 		break;
373 
374 	case COMPARATOR_TRIGGER_FALLING_EDGE:
375 		shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_DOWN_MASK;
376 		shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_DOWN;
377 		break;
378 
379 	case COMPARATOR_TRIGGER_BOTH_EDGES:
380 		shim_nrf_lpcomp_data0.event_mask = NRF_LPCOMP_INT_CROSS_MASK;
381 		shim_nrf_lpcomp_data0.config.detection = NRF_LPCOMP_DETECT_CROSS;
382 		break;
383 	}
384 
385 	shim_nrf_lpcomp_reconfigure();
386 
387 	if (shim_nrf_lpcomp_is_resumed()) {
388 		shim_nrf_lpcomp_start();
389 	}
390 
391 	return 0;
392 }
393 
shim_nrf_lpcomp_set_trigger_callback(const struct device * dev,comparator_callback_t callback,void * user_data)394 static int shim_nrf_lpcomp_set_trigger_callback(const struct device *dev,
395 						comparator_callback_t callback,
396 						void *user_data)
397 {
398 	shim_nrf_lpcomp_stop();
399 
400 	shim_nrf_lpcomp_data0.callback = callback;
401 	shim_nrf_lpcomp_data0.user_data = user_data;
402 
403 	if (callback != NULL && atomic_test_and_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0)) {
404 		callback(dev, user_data);
405 	}
406 
407 	if (shim_nrf_lpcomp_is_resumed()) {
408 		shim_nrf_lpcomp_start();
409 	}
410 
411 	return 0;
412 }
413 
shim_nrf_lpcomp_trigger_is_pending(const struct device * dev)414 static int shim_nrf_lpcomp_trigger_is_pending(const struct device *dev)
415 {
416 	ARG_UNUSED(dev);
417 
418 	return atomic_test_and_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0);
419 }
420 
421 static DEVICE_API(comparator, shim_nrf_lpcomp_api) = {
422 	.get_output = shim_nrf_lpcomp_get_output,
423 	.set_trigger = shim_nrf_lpcomp_set_trigger,
424 	.set_trigger_callback = shim_nrf_lpcomp_set_trigger_callback,
425 	.trigger_is_pending = shim_nrf_lpcomp_trigger_is_pending,
426 };
427 
comp_nrf_lpcomp_configure(const struct device * dev,const struct comp_nrf_lpcomp_config * config)428 int comp_nrf_lpcomp_configure(const struct device *dev,
429 			      const struct comp_nrf_lpcomp_config *config)
430 {
431 	nrfx_lpcomp_config_t nrf = {};
432 
433 	if (shim_nrf_lpcomp_config_to_nrf(config, &nrf)) {
434 		return -EINVAL;
435 	}
436 
437 	memcpy(&shim_nrf_lpcomp_data0.config, &nrf, sizeof(shim_nrf_lpcomp_data0.config));
438 
439 	shim_nrf_lpcomp_stop();
440 	shim_nrf_lpcomp_reconfigure();
441 	if (shim_nrf_lpcomp_is_resumed()) {
442 		shim_nrf_lpcomp_start();
443 	}
444 
445 	return 0;
446 }
447 
shim_nrf_lpcomp_event_handler(nrf_lpcomp_event_t event)448 static void shim_nrf_lpcomp_event_handler(nrf_lpcomp_event_t event)
449 {
450 	ARG_UNUSED(event);
451 
452 	if (shim_nrf_lpcomp_data0.callback == NULL) {
453 		atomic_set_bit(&shim_nrf_lpcomp_data0.triggered, 0);
454 		return;
455 	}
456 
457 	shim_nrf_lpcomp_data0.callback(DEVICE_DT_INST_GET(0), shim_nrf_lpcomp_data0.user_data);
458 	atomic_clear_bit(&shim_nrf_lpcomp_data0.triggered, 0);
459 }
460 
shim_nrf_lpcomp_init(const struct device * dev)461 static int shim_nrf_lpcomp_init(const struct device *dev)
462 {
463 	IRQ_CONNECT(DT_INST_IRQN(0),
464 		    DT_INST_IRQ(0, priority),
465 		    nrfx_isr,
466 		    nrfx_lpcomp_irq_handler,
467 		    0);
468 
469 	irq_enable(DT_INST_IRQN(0));
470 
471 	(void)shim_nrf_lpcomp_config_to_nrf(&shim_nrf_lpcomp_config0,
472 					    &shim_nrf_lpcomp_data0.config);
473 
474 	if (nrfx_lpcomp_init(&shim_nrf_lpcomp_data0.config,
475 			     shim_nrf_lpcomp_event_handler) != NRFX_SUCCESS) {
476 		return -ENODEV;
477 	}
478 
479 	return pm_device_driver_init(dev, shim_nrf_lpcomp_pm_callback);
480 }
481 
482 PM_DEVICE_DT_INST_DEFINE(0, shim_nrf_lpcomp_pm_callback);
483 
484 DEVICE_DT_INST_DEFINE(0,
485 		      shim_nrf_lpcomp_init,
486 		      PM_DEVICE_DT_INST_GET(0),
487 		      NULL,
488 		      NULL,
489 		      POST_KERNEL,
490 		      CONFIG_COMPARATOR_INIT_PRIORITY,
491 		      &shim_nrf_lpcomp_api);
492