1 /*
2 * Copyright (c) 2020 Vestas Wind Systems A/S
3 * Copyright (c) 2022 NXP
4 * Copyright (c) 2024 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <fsl_acmp.h>
10
11 #include <zephyr/kernel.h>
12 #include <zephyr/pm/device.h>
13 #include <zephyr/drivers/comparator.h>
14 #include <zephyr/drivers/comparator/mcux_acmp.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <zephyr/irq.h>
17
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(nxp_kinetis_acmp, CONFIG_COMPARATOR_LOG_LEVEL);
20
21 #define DT_DRV_COMPAT nxp_kinetis_acmp
22
23 /*
24 * DAC is a register defined in the MCUX HAL. We don't need it here and it conflicts
25 * with the COMP_MCUX_ACMP_PORT_INPUT_DAC definition so undef it here.
26 */
27 #ifdef DAC
28 #undef DAC
29 #endif
30
31 #if defined(FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT) && (FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT == 1U)
32 #define COMP_MCUX_ACMP_HAS_OFFSET 1
33 #else
34 #define COMP_MCUX_ACMP_HAS_OFFSET 0
35 #endif
36
37 #if defined(FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT) && (FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT == 1U)
38 #define COMP_MCUX_ACMP_HAS_HYSTERESIS 1
39 #else
40 #define COMP_MCUX_ACMP_HAS_HYSTERESIS 0
41 #endif
42
43 #if defined(FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT == 1U)
44 #define COMP_MCUX_ACMP_HAS_INPSEL 1
45 #else
46 #define COMP_MCUX_ACMP_HAS_INPSEL 0
47 #endif
48
49 #if defined(FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT == 1U)
50 #define COMP_MCUX_ACMP_HAS_INNSEL 1
51 #else
52 #define COMP_MCUX_ACMP_HAS_INNSEL 0
53 #endif
54
55 #if defined(FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT == 1U)
56 #define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 1
57 #else
58 #define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 0
59 #endif
60
61 #if defined(FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT == 1U)
62 #define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 1
63 #else
64 #define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 0
65 #endif
66
67 #if defined(FSL_FEATURE_ACMP_HAS_C3_REG) && (FSL_FEATURE_ACMP_HAS_C3_REG != 0U)
68 #define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 1
69 #else
70 #define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 0
71 #endif
72
73 #if !(defined(FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE) && (FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE == 1U))
74 #define COMP_MCUX_ACMP_HAS_WINDOW_MODE 1
75 #else
76 #define COMP_MCUX_ACMP_HAS_WINDOW_MODE 0
77 #endif
78
79 #define MCUX_ACMP_ENUM(name, value) \
80 _CONCAT_4(COMP_MCUX_ACMP_, name, _, value)
81
82 #define MCUX_ACMP_DT_INST_ENUM(inst, name, prop) \
83 MCUX_ACMP_ENUM(name, DT_INST_STRING_TOKEN(inst, prop))
84
85 #define MCUX_ACMP_DT_INST_ENUM_OR(inst, name, prop, or) \
86 COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prop), \
87 (MCUX_ACMP_DT_INST_ENUM(inst, name, prop)), \
88 (MCUX_ACMP_ENUM(name, or)))
89
90 #define MCUX_ACMP_DT_INST_OFFSET_MODE(inst) \
91 MCUX_ACMP_DT_INST_ENUM_OR(inst, OFFSET_MODE, offset_mode, LEVEL0)
92
93 #define MCUX_ACMP_DT_INST_HYST_MODE(inst) \
94 MCUX_ACMP_DT_INST_ENUM_OR(inst, HYSTERESIS_MODE, hysteresis_mode, LEVEL0)
95
96 #define MCUX_ACMP_DT_INST_EN_HS_MODE(inst) \
97 DT_INST_PROP(inst, enable_high_speed_mode)
98
99 #define MCUX_ACMP_DT_INST_INV_OUT(inst) \
100 DT_INST_PROP(inst, invert_output)
101
102 #define MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst) \
103 DT_INST_PROP(inst, use_unfiltered_output)
104
105 #define MCUX_ACMP_DT_INST_EN_PIN_OUT(inst) \
106 DT_INST_PROP(inst, enable_pin_out)
107
108 #define MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst) \
109 { \
110 .offset_mode = MCUX_ACMP_DT_INST_OFFSET_MODE(inst), \
111 .hysteresis_mode = MCUX_ACMP_DT_INST_HYST_MODE(inst), \
112 .enable_high_speed_mode = MCUX_ACMP_DT_INST_EN_HS_MODE(inst), \
113 .invert_output = MCUX_ACMP_DT_INST_INV_OUT(inst), \
114 .use_unfiltered_output = MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst), \
115 .enable_pin_output = MCUX_ACMP_DT_INST_EN_PIN_OUT(inst), \
116 }
117
118 #define MCUX_ACMP_DT_INST_P_MUX_IN(inst) \
119 MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, positive_mux_input)
120
121 #define MCUX_ACMP_DT_INST_N_MUX_IN(inst) \
122 MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, negative_mux_input)
123
124 #define MCUX_ACMP_DT_INST_P_PORT_IN(inst) \
125 MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, positive_port_input, MUX)
126
127 #define MCUX_ACMP_DT_INST_N_PORT_IN(inst) \
128 MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, negative_port_input, MUX)
129
130 #define MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst) \
131 { \
132 .positive_mux_input = MCUX_ACMP_DT_INST_P_MUX_IN(inst), \
133 .negative_mux_input = MCUX_ACMP_DT_INST_N_MUX_IN(inst), \
134 .positive_port_input = MCUX_ACMP_DT_INST_P_PORT_IN(inst), \
135 .negative_port_input = MCUX_ACMP_DT_INST_N_PORT_IN(inst), \
136 }
137
138 #define MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst) \
139 DT_INST_PROP(inst, filter_enable_sample)
140
141 #define MCUX_ACMP_DT_INST_FILTER_COUNT(inst) \
142 DT_INST_PROP_OR(inst, filter_count, 0)
143
144 #define MCUX_ACMP_DT_INST_FILTER_PERIOD(inst) \
145 DT_INST_PROP_OR(inst, filter_period, 0)
146
147 #define MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst) \
148 { \
149 .enable_sample = MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst), \
150 .filter_count = MCUX_ACMP_DT_INST_FILTER_COUNT(inst), \
151 .filter_period = MCUX_ACMP_DT_INST_FILTER_PERIOD(inst), \
152 }
153
154 #define MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst) \
155 MCUX_ACMP_DT_INST_ENUM_OR(inst, DAC_VREF_SOURCE, dac_vref_source, VIN1)
156
157 #define MCUX_ACMP_DT_INST_DAC_VALUE(inst) \
158 DT_INST_PROP_OR(inst, dac_value, 0)
159
160 #define MCUX_ACMP_DT_INST_DAC_EN(inst) \
161 DT_INST_PROP(inst, dac_enable)
162
163 #define MCUX_ACMP_DT_INST_DAC_EN_HS(inst) \
164 DT_INST_PROP(inst, dac_enable_high_speed)
165
166 #define MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst) \
167 { \
168 .vref_source = MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst), \
169 .value = MCUX_ACMP_DT_INST_DAC_VALUE(inst), \
170 .enable_output = MCUX_ACMP_DT_INST_DAC_EN(inst), \
171 .enable_high_speed_mode = MCUX_ACMP_DT_INST_DAC_EN_HS(inst), \
172 }
173
174 #define MCUX_ACMP_DT_INST_DM_EN_P_CH(inst) \
175 DT_INST_PROP(inst, discrete_mode_enable_positive_channel)
176
177 #define MCUX_ACMP_DT_INST_DM_EN_N_CH(inst) \
178 DT_INST_PROP(inst, discrete_mode_enable_negative_channel)
179
180 #define MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst) \
181 DT_INST_PROP(inst, discrete_mode_enable_resistor_divider)
182
183 #define MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst) \
184 MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_CLOCK, discrete_mode_clock_source, SLOW)
185
186 #define MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst) \
187 MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_SAMPLE_TIME, discrete_mode_sample_time, T1)
188
189 #define MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst) \
190 MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase1_time, ALT0)
191
192 #define MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst) \
193 MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase2_time, ALT0)
194
195 #define MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst) \
196 { \
197 .enable_positive_channel = MCUX_ACMP_DT_INST_DM_EN_P_CH(inst), \
198 .enable_negative_channel = MCUX_ACMP_DT_INST_DM_EN_N_CH(inst), \
199 .enable_resistor_divider = MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst), \
200 .clock_source = MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst), \
201 .sample_time = MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst), \
202 .phase1_time = MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst), \
203 .phase2_time = MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst), \
204 }
205
206 #define MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst) \
207 DT_INST_PROP(inst, enable_window_mode)
208
209 struct mcux_acmp_config {
210 CMP_Type *base;
211 const struct pinctrl_dev_config *pincfg;
212 void (*irq_init)(void);
213 const struct comp_mcux_acmp_mode_config mode_config;
214 const struct comp_mcux_acmp_input_config input_config;
215 const struct comp_mcux_acmp_filter_config filter_config;
216 const struct comp_mcux_acmp_dac_config dac_config;
217 #if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
218 const struct comp_mcux_acmp_dm_config dm_config;
219 #endif
220 #if COMP_MCUX_ACMP_HAS_WINDOW_MODE
221 bool enable_window_mode;
222 #endif
223 };
224
225 #if MCUX_ACMP_HAS_OFFSET
226 BUILD_ASSERT((int)kACMP_OffsetLevel0 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL0);
227 BUILD_ASSERT((int)kACMP_OffsetLevel1 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL1);
228 #endif
229
230 #if COMP_MCUX_ACMP_HAS_HYSTERESIS
231 BUILD_ASSERT((int)kACMP_HysteresisLevel0 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL0);
232 BUILD_ASSERT((int)kACMP_HysteresisLevel1 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL1);
233 BUILD_ASSERT((int)kACMP_HysteresisLevel2 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL2);
234 BUILD_ASSERT((int)kACMP_HysteresisLevel3 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL3);
235 #endif
236
237 BUILD_ASSERT((int)kACMP_VrefSourceVin1 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN1);
238 BUILD_ASSERT((int)kACMP_VrefSourceVin2 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN2);
239
240 #if MCUX_ACMP_HAS_INPSEL || MCUX_ACMP_HAS_INNSEL
241 BUILD_ASSERT((int)kACMP_PortInputFromDAC == (int)COMP_MCUX_ACMP_PORT_INPUT_DAC);
242 BUILD_ASSERT((int)kACMP_PortInputFromMux == (int)COMP_MCUX_ACMP_PORT_INPUT_MUX);
243 #endif
244
245 #if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
246 BUILD_ASSERT((int)kACMP_DiscreteClockSlow == (int)COMP_MCUX_ACMP_DM_CLOCK_SLOW);
247 BUILD_ASSERT((int)kACMP_DiscreteClockFast == (int)COMP_MCUX_ACMP_DM_CLOCK_FAST);
248
249 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs1T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T1);
250 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs2T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T2);
251 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs4T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T4);
252 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs8T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T8);
253 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs16T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T16);
254 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs32T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T32);
255 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs64T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T64);
256 BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs256T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T256);
257
258 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt0 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT0);
259 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt1 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT1);
260 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt2 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT2);
261 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt3 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT3);
262 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt4 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT4);
263 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt5 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT5);
264 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt6 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT6);
265 BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt7 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT7);
266 #endif
267
268 struct mcux_acmp_data {
269 uint32_t interrupt_mask;
270 comparator_callback_t callback;
271 void *user_data;
272 };
273
274 #if CONFIG_PM_DEVICE
mcux_acmp_is_resumed(const struct device * dev)275 static bool mcux_acmp_is_resumed(const struct device *dev)
276 {
277 enum pm_device_state state;
278
279 (void)pm_device_state_get(dev, &state);
280 return state == PM_DEVICE_STATE_ACTIVE;
281 }
282 #else
mcux_acmp_is_resumed(const struct device * dev)283 static bool mcux_acmp_is_resumed(const struct device *dev)
284 {
285 ARG_UNUSED(dev);
286 return true;
287 }
288 #endif
289
mcux_acmp_get_output(const struct device * dev)290 static int mcux_acmp_get_output(const struct device *dev)
291 {
292 const struct mcux_acmp_config *config = dev->config;
293 uint32_t status;
294
295 status = ACMP_GetStatusFlags(config->base);
296 return (status & kACMP_OutputAssertEventFlag) ? 1 : 0;
297 }
298
mcux_acmp_set_trigger(const struct device * dev,enum comparator_trigger trigger)299 static int mcux_acmp_set_trigger(const struct device *dev,
300 enum comparator_trigger trigger)
301 {
302 const struct mcux_acmp_config *config = dev->config;
303 struct mcux_acmp_data *data = dev->data;
304
305 ACMP_DisableInterrupts(config->base, UINT32_MAX);
306
307 switch (trigger) {
308 case COMPARATOR_TRIGGER_NONE:
309 data->interrupt_mask = 0;
310 break;
311
312 case COMPARATOR_TRIGGER_RISING_EDGE:
313 data->interrupt_mask = kACMP_OutputRisingInterruptEnable;
314 break;
315
316 case COMPARATOR_TRIGGER_FALLING_EDGE:
317 data->interrupt_mask = kACMP_OutputFallingInterruptEnable;
318 break;
319
320 case COMPARATOR_TRIGGER_BOTH_EDGES:
321 data->interrupt_mask = kACMP_OutputFallingInterruptEnable |
322 kACMP_OutputRisingInterruptEnable;
323 break;
324 }
325
326 if (data->interrupt_mask && data->callback != NULL) {
327 ACMP_EnableInterrupts(config->base, data->interrupt_mask);
328 }
329
330 return 0;
331 }
332
mcux_acmp_set_trigger_callback(const struct device * dev,comparator_callback_t callback,void * user_data)333 static int mcux_acmp_set_trigger_callback(const struct device *dev,
334 comparator_callback_t callback,
335 void *user_data)
336 {
337 const struct mcux_acmp_config *config = dev->config;
338 struct mcux_acmp_data *data = dev->data;
339
340 ACMP_DisableInterrupts(config->base, UINT32_MAX);
341
342 data->callback = callback;
343 data->user_data = user_data;
344
345 if (data->callback == NULL) {
346 return 0;
347 }
348
349 if (data->interrupt_mask) {
350 ACMP_EnableInterrupts(config->base, data->interrupt_mask);
351 }
352
353 return 0;
354 }
355
mcux_acmp_trigger_is_pending(const struct device * dev)356 static int mcux_acmp_trigger_is_pending(const struct device *dev)
357 {
358 const struct mcux_acmp_config *config = dev->config;
359 struct mcux_acmp_data *data = dev->data;
360 uint32_t status_flags;
361
362 status_flags = ACMP_GetStatusFlags(config->base);
363 ACMP_ClearStatusFlags(config->base, UINT32_MAX);
364
365 if ((data->interrupt_mask & kACMP_OutputRisingInterruptEnable) &&
366 (status_flags & kACMP_OutputRisingEventFlag)) {
367 return 1;
368 }
369
370 if ((data->interrupt_mask & kACMP_OutputFallingInterruptEnable) &&
371 (status_flags & kACMP_OutputFallingEventFlag)) {
372 return 1;
373 }
374
375 return 0;
376 }
377
378 static DEVICE_API(comparator, mcux_acmp_comp_api) = {
379 .get_output = mcux_acmp_get_output,
380 .set_trigger = mcux_acmp_set_trigger,
381 .set_trigger_callback = mcux_acmp_set_trigger_callback,
382 .trigger_is_pending = mcux_acmp_trigger_is_pending,
383 };
384
comp_mcux_acmp_init_mode_config(const struct device * dev,const struct comp_mcux_acmp_mode_config * config)385 static void comp_mcux_acmp_init_mode_config(const struct device *dev,
386 const struct comp_mcux_acmp_mode_config *config)
387 {
388 const struct mcux_acmp_config *dev_config = dev->config;
389 acmp_config_t acmp_config;
390
391 #if COMP_MCUX_ACMP_HAS_OFFSET
392 acmp_config.offsetMode = (acmp_offset_mode_t)config->offset_mode;
393 #endif
394
395 #if COMP_MCUX_ACMP_HAS_HYSTERESIS
396 acmp_config.hysteresisMode = (acmp_hysteresis_mode_t)config->hysteresis_mode;
397 #endif
398
399 acmp_config.enableHighSpeed = config->enable_high_speed_mode;
400 acmp_config.enableInvertOutput = config->invert_output;
401 acmp_config.useUnfilteredOutput = config->use_unfiltered_output;
402 acmp_config.enablePinOut = config->enable_pin_output;
403
404 ACMP_Init(dev_config->base, &acmp_config);
405 }
406
comp_mcux_acmp_set_mode_config(const struct device * dev,const struct comp_mcux_acmp_mode_config * config)407 int comp_mcux_acmp_set_mode_config(const struct device *dev,
408 const struct comp_mcux_acmp_mode_config *config)
409 {
410 const struct mcux_acmp_config *dev_config = dev->config;
411
412 comp_mcux_acmp_init_mode_config(dev, config);
413
414 if (mcux_acmp_is_resumed(dev)) {
415 ACMP_Enable(dev_config->base, true);
416 }
417
418 return 0;
419 }
420
comp_mcux_acmp_set_input_config(const struct device * dev,const struct comp_mcux_acmp_input_config * config)421 int comp_mcux_acmp_set_input_config(const struct device *dev,
422 const struct comp_mcux_acmp_input_config *config)
423 {
424 const struct mcux_acmp_config *dev_config = dev->config;
425 acmp_channel_config_t acmp_channel_config;
426
427 #if COMP_MCUX_ACMP_HAS_INPSEL
428 acmp_channel_config.positivePortInput = (acmp_port_input_t)config->positive_port_input;
429 #endif
430
431 acmp_channel_config.plusMuxInput = (uint32_t)config->positive_mux_input;
432
433 #if COMP_MCUX_ACMP_HAS_INNSEL
434 acmp_channel_config.negativePortInput = (acmp_port_input_t)config->negative_port_input;
435 #endif
436
437 acmp_channel_config.minusMuxInput = (uint32_t)config->negative_mux_input;
438
439 ACMP_SetChannelConfig(dev_config->base, &acmp_channel_config);
440 return 0;
441 }
442
comp_mcux_acmp_set_filter_config(const struct device * dev,const struct comp_mcux_acmp_filter_config * config)443 int comp_mcux_acmp_set_filter_config(const struct device *dev,
444 const struct comp_mcux_acmp_filter_config *config)
445 {
446 const struct mcux_acmp_config *dev_config = dev->config;
447 acmp_filter_config_t acmp_filter_config;
448
449 if (config->enable_sample && config->filter_count == 0) {
450 return -EINVAL;
451 }
452
453 if (config->filter_count > 7) {
454 return -EINVAL;
455 }
456
457 acmp_filter_config.enableSample = config->enable_sample;
458 acmp_filter_config.filterCount = config->filter_count;
459 acmp_filter_config.filterPeriod = config->filter_period;
460
461 ACMP_SetFilterConfig(dev_config->base, &acmp_filter_config);
462 return 0;
463 }
464
comp_mcux_acmp_set_dac_config(const struct device * dev,const struct comp_mcux_acmp_dac_config * config)465 int comp_mcux_acmp_set_dac_config(const struct device *dev,
466 const struct comp_mcux_acmp_dac_config *config)
467 {
468 const struct mcux_acmp_config *dev_config = dev->config;
469 acmp_dac_config_t acmp_dac_config;
470
471 acmp_dac_config.referenceVoltageSource =
472 (acmp_reference_voltage_source_t)config->vref_source;
473
474 acmp_dac_config.DACValue = config->value;
475
476 #if COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE
477 acmp_dac_config.enableOutput = config->enable_output;
478 #endif
479
480 #if COMP_MCUX_ACMP_HAS_DAC_WORK_MODE
481 acmp_dac_config.workMode = config->enable_high_speed_mode
482 ? kACMP_DACWorkHighSpeedMode
483 : kACMP_DACWorkLowSpeedMode;
484 #endif
485
486 ACMP_SetDACConfig(dev_config->base, &acmp_dac_config);
487 return 0;
488 }
489
490 #if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
comp_mcux_acmp_set_dm_config(const struct device * dev,const struct comp_mcux_acmp_dm_config * config)491 int comp_mcux_acmp_set_dm_config(const struct device *dev,
492 const struct comp_mcux_acmp_dm_config *config)
493 {
494 const struct mcux_acmp_config *dev_config = dev->config;
495 acmp_discrete_mode_config_t acmp_dm_config;
496
497 acmp_dm_config.enablePositiveChannelDiscreteMode = config->enable_positive_channel;
498 acmp_dm_config.enableNegativeChannelDiscreteMode = config->enable_negative_channel;
499 acmp_dm_config.enableResistorDivider = config->enable_resistor_divider;
500 acmp_dm_config.clockSource = (acmp_discrete_clock_source_t)config->clock_source;
501 acmp_dm_config.sampleTime = (acmp_discrete_sample_time_t)config->sample_time;
502 acmp_dm_config.phase1Time = (acmp_discrete_phase_time_t)config->phase1_time;
503 acmp_dm_config.phase2Time = (acmp_discrete_phase_time_t)config->phase2_time;
504
505 ACMP_SetDiscreteModeConfig(dev_config->base, &acmp_dm_config);
506 return 0;
507 }
508 #endif
509
510 #if COMP_MCUX_ACMP_HAS_WINDOW_MODE
comp_mcux_acmp_set_window_mode(const struct device * dev,bool enable)511 int comp_mcux_acmp_set_window_mode(const struct device *dev, bool enable)
512 {
513 const struct mcux_acmp_config *config = dev->config;
514
515 ACMP_EnableWindowMode(config->base, enable);
516 return 0;
517 }
518 #endif
519
mcux_acmp_pm_callback(const struct device * dev,enum pm_device_action action)520 static int mcux_acmp_pm_callback(const struct device *dev, enum pm_device_action action)
521 {
522 const struct mcux_acmp_config *config = dev->config;
523
524 if (action == PM_DEVICE_ACTION_RESUME) {
525 ACMP_Enable(config->base, true);
526 }
527
528 #if CONFIG_PM_DEVICE
529 if (action == PM_DEVICE_ACTION_SUSPEND) {
530 ACMP_Enable(config->base, false);
531 }
532 #endif
533
534 return 0;
535 }
536
mcux_acmp_irq_handler(const struct device * dev)537 static void mcux_acmp_irq_handler(const struct device *dev)
538 {
539 const struct mcux_acmp_config *config = dev->config;
540 struct mcux_acmp_data *data = dev->data;
541
542 ACMP_ClearStatusFlags(config->base, UINT32_MAX);
543
544 if (data->callback == NULL) {
545 return;
546 }
547
548 data->callback(dev, data->user_data);
549 }
550
mcux_acmp_init(const struct device * dev)551 static int mcux_acmp_init(const struct device *dev)
552 {
553 const struct mcux_acmp_config *config = dev->config;
554 int ret;
555
556 ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
557 if (ret) {
558 LOG_ERR("failed to set %s", "pincfg");
559 return ret;
560 }
561
562 comp_mcux_acmp_init_mode_config(dev, &config->mode_config);
563
564 ret = comp_mcux_acmp_set_input_config(dev, &config->input_config);
565 if (ret) {
566 LOG_ERR("failed to set %s", "input config");
567 return ret;
568 }
569
570 ret = comp_mcux_acmp_set_filter_config(dev, &config->filter_config);
571 if (ret) {
572 LOG_ERR("failed to set %s", "filter config");
573 return ret;
574 }
575
576 ret = comp_mcux_acmp_set_dac_config(dev, &config->dac_config);
577 if (ret) {
578 LOG_ERR("failed to set %s", "dac config");
579 return ret;
580 }
581
582 #if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
583 ret = comp_mcux_acmp_set_dm_config(dev, &config->dm_config);
584 if (ret) {
585 LOG_ERR("failed to set %s", "discrete mode config");
586 return ret;
587 }
588 #endif
589
590 #if COMP_MCUX_ACMP_HAS_WINDOW_MODE
591 ret = comp_mcux_acmp_set_window_mode(dev, config->enable_window_mode);
592 if (ret) {
593 LOG_ERR("failed to set %s", "window mode");
594 return ret;
595 }
596 #endif
597
598 ACMP_DisableInterrupts(config->base, UINT32_MAX);
599 config->irq_init();
600
601 return pm_device_driver_init(dev, mcux_acmp_pm_callback);
602 }
603
604 #define MCUX_ACMP_IRQ_HANDLER_SYM(inst) \
605 _CONCAT(mcux_acmp_irq_init, inst)
606
607 #define MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \
608 static void MCUX_ACMP_IRQ_HANDLER_SYM(inst)(void) \
609 { \
610 IRQ_CONNECT(DT_INST_IRQN(inst), \
611 DT_INST_IRQ(inst, priority), \
612 mcux_acmp_irq_handler, \
613 DEVICE_DT_INST_GET(inst), \
614 0); \
615 \
616 irq_enable(DT_INST_IRQN(inst)); \
617 }
618
619 #define MCUX_ACMP_DEVICE(inst) \
620 PINCTRL_DT_INST_DEFINE(inst); \
621 \
622 static struct mcux_acmp_data _CONCAT(data, inst); \
623 \
624 MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \
625 \
626 static const struct mcux_acmp_config _CONCAT(config, inst) = { \
627 .base = (CMP_Type *)DT_INST_REG_ADDR(inst), \
628 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
629 .irq_init = MCUX_ACMP_IRQ_HANDLER_SYM(inst), \
630 .mode_config = MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst), \
631 .input_config = MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst), \
632 .filter_config = MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst), \
633 .dac_config = MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst), \
634 IF_ENABLED(COMP_MCUX_ACMP_HAS_DISCRETE_MODE, \
635 (.dm_config = MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst),)) \
636 IF_ENABLED(COMP_MCUX_ACMP_HAS_WINDOW_MODE, \
637 (.enable_window_mode = MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst),)) \
638 }; \
639 \
640 PM_DEVICE_DT_INST_DEFINE(inst, mcux_acmp_pm_callback); \
641 \
642 DEVICE_DT_INST_DEFINE(inst, \
643 mcux_acmp_init, \
644 PM_DEVICE_DT_INST_GET(inst), \
645 &_CONCAT(data, inst), \
646 &_CONCAT(config, inst), \
647 POST_KERNEL, \
648 CONFIG_COMPARATOR_INIT_PRIORITY, \
649 &mcux_acmp_comp_api);
650
651 DT_INST_FOREACH_STATUS_OKAY(MCUX_ACMP_DEVICE)
652