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