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