1 /*
2  * Copyright (c) 2020 Vestas Wind Systems A/S
3  * Copyright 2024 NXP
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT nxp_lpcmp
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/kernel.h>
14 #include <fsl_lpcmp.h>
15 #include <zephyr/drivers/pinctrl.h>
16 #include <zephyr/irq.h>
17 #include <zephyr/drivers/sensor/mcux_lpcmp.h>
18 #include <fsl_lpcmp.h>
19 
20 LOG_MODULE_REGISTER(mcux_lpcmp, CONFIG_SENSOR_LOG_LEVEL);
21 
22 struct mcux_lpcmp_config {
23 	LPCMP_Type *base;
24 	const struct pinctrl_dev_config *pincfg;
25 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
26 	void (*irq_config_func)(const struct device *dev);
27 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
28 	bool output_enable: 1;
29 	bool unfiltered: 1;
30 	bool output_invert: 1;
31 	lpcmp_hysteresis_mode_t hysteresis_level;
32 	lpcmp_power_mode_t power_level;
33 	lpcmp_functional_source_clock_t function_clock;
34 };
35 
36 struct mcux_lpcmp_data {
37 	lpcmp_config_t lpcmp_config;
38 	lpcmp_dac_config_t dac_config;
39 	lpcmp_filter_config_t filter_config;
40 	lpcmp_window_control_config_t window_config;
41 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
42 	const struct device *dev;
43 	const struct sensor_trigger *rising_trigger;
44 	sensor_trigger_handler_t rising_handler;
45 	const struct sensor_trigger *falling_trigger;
46 	sensor_trigger_handler_t falling_handler;
47 	struct k_work work;
48 	volatile uint32_t status;
49 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
50 	bool cout;
51 };
52 
mcux_lpcmp_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)53 static int mcux_lpcmp_attr_set(const struct device *dev, enum sensor_channel chan,
54 			       enum sensor_attribute attr, const struct sensor_value *val)
55 {
56 	const struct mcux_lpcmp_config *config = dev->config;
57 	struct mcux_lpcmp_data *data = dev->data;
58 	int32_t val1 = val->val1;
59 
60 	__ASSERT_NO_MSG(val != NULL);
61 
62 	if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
63 		return -ENOTSUP;
64 	}
65 
66 	if (val->val2 != 0) {
67 		return -EINVAL;
68 	}
69 
70 	switch ((int16_t)attr) {
71 	/** Analog input mux related attributes */
72 	case SENSOR_ATTR_MCUX_LPCMP_POSITIVE_MUX_INPUT:
73 		LOG_DBG("positive mux = %d", val1);
74 		if (val1 >= 0 && val1 < 8) {
75 			config->base->CCR2 = ((config->base->CCR2 & (~LPCMP_CCR2_PSEL_MASK)) |
76 					      LPCMP_CCR2_PSEL(val1));
77 		} else {
78 			return -EINVAL;
79 		}
80 		break;
81 	case SENSOR_ATTR_MCUX_LPCMP_NEGATIVE_MUX_INPUT:
82 		LOG_DBG("negative mux = %d", val1);
83 		if (val1 >= 0 && val1 < 8) {
84 			config->base->CCR2 = ((config->base->CCR2 & (~LPCMP_CCR2_MSEL_MASK)) |
85 					      LPCMP_CCR2_MSEL(val1));
86 		} else {
87 			return -EINVAL;
88 		}
89 		break;
90 
91 	/** DAC related attributes */
92 	case SENSOR_ATTR_MCUX_LPCMP_DAC_ENABLE:
93 		LOG_DBG("dac enable = %d", val1);
94 		if (val1 == 1) {
95 			config->base->DCR |= LPCMP_DCR_DAC_EN_MASK;
96 		} else if (val1 == 0) {
97 			config->base->DCR &= ~LPCMP_DCR_DAC_EN_MASK;
98 		} else {
99 			return -EINVAL;
100 		}
101 		break;
102 	case SENSOR_ATTR_MCUX_LPCMP_DAC_HIGH_POWER_MODE_ENABLE:
103 		LOG_DBG("dac power mode = %d", val1);
104 		if ((val1 == 1) || (val1 == 0)) {
105 			data->dac_config.enableLowPowerMode = (bool)val1;
106 			LPCMP_SetDACConfig(config->base, &data->dac_config);
107 		} else {
108 			return -EINVAL;
109 		}
110 		break;
111 	case SENSOR_ATTR_MCUX_LPCMP_DAC_REFERENCE_VOLTAGE_SOURCE:
112 		LOG_DBG("dac vref = %d", val1);
113 		if (val1 >= kLPCMP_VrefSourceVin1 && val1 <= kLPCMP_VrefSourceVin2) {
114 			data->dac_config.referenceVoltageSource = val1;
115 			LPCMP_SetDACConfig(config->base, &data->dac_config);
116 		} else {
117 			return -EINVAL;
118 		}
119 		break;
120 	case SENSOR_ATTR_MCUX_LPCMP_DAC_OUTPUT_VOLTAGE:
121 		LOG_DBG("dac value = %d", val1);
122 		if (val1 >= 0 && val1 < 256) {
123 			data->dac_config.DACValue = val1;
124 			LPCMP_SetDACConfig(config->base, &data->dac_config);
125 		} else {
126 			return -EINVAL;
127 		}
128 		break;
129 
130 	/** Sample and filter related attributes */
131 	case SENSOR_ATTR_MCUX_LPCMP_SAMPLE_ENABLE:
132 		LOG_DBG("Filter sample enable = %d", val1);
133 		if ((val1 == 1) || (val1 == 0)) {
134 			data->filter_config.enableSample = (bool)val1;
135 			LPCMP_SetFilterConfig(config->base, &data->filter_config);
136 		} else {
137 			return -EINVAL;
138 		}
139 		break;
140 	case SENSOR_ATTR_MCUX_LPCMP_FILTER_COUNT:
141 		LOG_DBG("sample count = %d", val1);
142 		data->filter_config.filterSampleCount = val1;
143 		LPCMP_SetFilterConfig(config->base, &data->filter_config);
144 		break;
145 	case SENSOR_ATTR_MCUX_LPCMP_FILTER_PERIOD:
146 		LOG_DBG("sample period = %d", val1);
147 		data->filter_config.filterSamplePeriod = val1;
148 		LPCMP_SetFilterConfig(config->base, &data->filter_config);
149 		break;
150 
151 	/** Window related attributes  */
152 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_ENABLE:
153 		LOG_DBG("Window enable = %d", val1);
154 		if ((val1 == 1) || (val1 == 0)) {
155 			LPCMP_EnableWindowMode(config->base, (bool)val1);
156 		} else {
157 			return -EINVAL;
158 		}
159 		break;
160 
161 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_SIGNAL_INVERT_ENABLE:
162 		LOG_DBG("Invert window signal = %d", val1);
163 		if ((val1 == 1) || (val1 == 0)) {
164 			data->window_config.enableInvertWindowSignal = (bool)val1;
165 			LPCMP_SetWindowControl(config->base, &data->window_config);
166 		} else {
167 			return -EINVAL;
168 		}
169 		break;
170 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_SIGNAL:
171 		LOG_DBG("COUTA signal = %d", val1);
172 		if ((val1 >= (int32_t)kLPCMP_COUTASignalNoSet) &&
173 		    (val1 < (int32_t)kLPCMP_COUTASignalHigh)) {
174 			data->window_config.COUTASignal = val1;
175 			LPCMP_SetWindowControl(config->base, &data->window_config);
176 		} else {
177 			return -EINVAL;
178 		}
179 		break;
180 	case SENSOR_ATTR_MCUX_LPCMP_COUT_EVENT_TO_CLOSE_WINDOW:
181 		LOG_DBG("COUT event = %d", val1);
182 		if ((val1 >= (int32_t)kLPCMP_CLoseWindowEventNoSet) &&
183 		    (val1 < (int32_t)kLPCMP_CLoseWindowEventBothEdge)) {
184 			data->window_config.closeWindowEvent = val1;
185 			LPCMP_SetWindowControl(config->base, &data->window_config);
186 		} else {
187 			return -EINVAL;
188 		}
189 		break;
190 
191 	default:
192 		return -ENOTSUP;
193 	}
194 
195 	return 0;
196 }
197 
mcux_lpcmp_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)198 static int mcux_lpcmp_attr_get(const struct device *dev, enum sensor_channel chan,
199 			       enum sensor_attribute attr, struct sensor_value *val)
200 {
201 	const struct mcux_lpcmp_config *config = dev->config;
202 	struct mcux_lpcmp_data *data = dev->data;
203 
204 	__ASSERT_NO_MSG(val != NULL);
205 
206 	if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
207 		return -ENOTSUP;
208 	}
209 
210 	switch ((int16_t)attr) {
211 	/** Analog mux related attributes */
212 	case SENSOR_ATTR_MCUX_LPCMP_POSITIVE_MUX_INPUT:
213 		val->val1 = (int32_t)((config->base->CCR2) &
214 				      LPCMP_CCR2_PSEL_MASK >> LPCMP_CCR2_PSEL_SHIFT);
215 		break;
216 	case SENSOR_ATTR_MCUX_LPCMP_NEGATIVE_MUX_INPUT:
217 		val->val1 = (int32_t)((config->base->CCR2) &
218 				      LPCMP_CCR2_MSEL_MASK >> LPCMP_CCR2_MSEL_SHIFT);
219 		break;
220 
221 	/** DAC related attributes */
222 	case SENSOR_ATTR_MCUX_LPCMP_DAC_ENABLE:
223 		val->val1 = (int32_t)((config->base->DCR) &
224 				      LPCMP_DCR_DAC_EN_MASK >> LPCMP_DCR_DAC_EN_SHIFT);
225 		break;
226 	case SENSOR_ATTR_MCUX_LPCMP_DAC_HIGH_POWER_MODE_ENABLE:
227 		val->val1 = (int32_t)(data->dac_config.enableLowPowerMode);
228 		break;
229 	case SENSOR_ATTR_MCUX_LPCMP_DAC_REFERENCE_VOLTAGE_SOURCE:
230 		val->val1 = (int32_t)(data->dac_config.referenceVoltageSource);
231 		break;
232 	case SENSOR_ATTR_MCUX_LPCMP_DAC_OUTPUT_VOLTAGE:
233 		val->val1 = (int32_t)(data->dac_config.DACValue);
234 		break;
235 
236 	/** Sample and filter related attributes */
237 	case SENSOR_ATTR_MCUX_LPCMP_SAMPLE_ENABLE:
238 		val->val1 = (int32_t)(data->filter_config.enableSample);
239 		break;
240 	case SENSOR_ATTR_MCUX_LPCMP_FILTER_COUNT:
241 		val->val1 = (int32_t)(data->filter_config.filterSampleCount);
242 		break;
243 	case SENSOR_ATTR_MCUX_LPCMP_FILTER_PERIOD:
244 		val->val1 = (int32_t)(data->filter_config.filterSamplePeriod);
245 		break;
246 
247 	/** Window related attributes  */
248 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_ENABLE:
249 		val->val1 = (int32_t)((config->base->CCR1) &
250 				      LPCMP_CCR1_WINDOW_EN_MASK >> LPCMP_CCR1_WINDOW_EN_SHIFT);
251 		break;
252 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_SIGNAL_INVERT_ENABLE:
253 		val->val1 = (int32_t)(data->window_config.enableInvertWindowSignal);
254 		break;
255 	case SENSOR_ATTR_MCUX_LPCMP_COUTA_SIGNAL:
256 		val->val1 = (int32_t)(data->window_config.COUTASignal);
257 		break;
258 	case SENSOR_ATTR_MCUX_LPCMP_COUT_EVENT_TO_CLOSE_WINDOW:
259 		val->val1 = (int32_t)(data->window_config.closeWindowEvent);
260 		break;
261 
262 	default:
263 		return -ENOTSUP;
264 	}
265 
266 	val->val2 = 0;
267 
268 	return 0;
269 }
270 
mcux_lpcmp_sample_fetch(const struct device * dev,enum sensor_channel chan)271 static int mcux_lpcmp_sample_fetch(const struct device *dev, enum sensor_channel chan)
272 {
273 	const struct mcux_lpcmp_config *config = dev->config;
274 	struct mcux_lpcmp_data *data = dev->data;
275 
276 	if (chan != SENSOR_CHAN_ALL && (int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
277 		return -ENOTSUP;
278 	}
279 
280 	data->cout = ((LPCMP_GetStatusFlags(config->base) &
281 		       (uint32_t)kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag);
282 
283 	return 0;
284 }
285 
mcux_lpcmp_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)286 static int mcux_lpcmp_channel_get(const struct device *dev, enum sensor_channel chan,
287 				  struct sensor_value *val)
288 {
289 	struct mcux_lpcmp_data *data = dev->data;
290 
291 	__ASSERT_NO_MSG(val != NULL);
292 
293 	if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
294 		return -ENOTSUP;
295 	}
296 
297 	val->val1 = data->cout ? 1 : 0;
298 	val->val2 = 0;
299 
300 	return 0;
301 }
302 
303 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
mcux_lpcmp_isr(const struct device * dev)304 static void mcux_lpcmp_isr(const struct device *dev)
305 {
306 	const struct mcux_lpcmp_config *config = dev->config;
307 	struct mcux_lpcmp_data *data = dev->data;
308 
309 	data->status = LPCMP_GetStatusFlags(config->base);
310 	LPCMP_ClearStatusFlags(config->base, data->status);
311 
312 	k_work_submit(&data->work);
313 }
314 
mcux_lpcmp_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)315 static int mcux_lpcmp_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
316 				  sensor_trigger_handler_t handler)
317 {
318 	struct mcux_lpcmp_data *data = dev->data;
319 
320 	__ASSERT_NO_MSG(trig != NULL);
321 
322 	if ((int16_t)trig->chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
323 		return -ENOTSUP;
324 	}
325 
326 	if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_RISING) {
327 		data->rising_handler = handler;
328 		data->rising_trigger = trig;
329 	} else if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_FALLING) {
330 		data->falling_handler = handler;
331 		data->falling_trigger = trig;
332 	} else {
333 		return -ENOTSUP;
334 	}
335 
336 	return 0;
337 }
338 
mcux_lpcmp_trigger_work_handler(struct k_work * item)339 static void mcux_lpcmp_trigger_work_handler(struct k_work *item)
340 {
341 	struct mcux_lpcmp_data *data = CONTAINER_OF(item, struct mcux_lpcmp_data, work);
342 	const struct sensor_trigger *trigger;
343 	sensor_trigger_handler_t handler = NULL;
344 
345 	if (((data->status & kLPCMP_OutputRisingEventFlag) == kLPCMP_OutputRisingEventFlag) &&
346 	    ((data->status & kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag)) {
347 		trigger = data->rising_trigger;
348 		handler = data->rising_handler;
349 	} else if (((data->status & kLPCMP_OutputFallingEventFlag) ==
350 		    kLPCMP_OutputFallingEventFlag) &&
351 		   ((data->status & kLPCMP_OutputAssertEventFlag) !=
352 		    kLPCMP_OutputAssertEventFlag)) {
353 		trigger = data->falling_trigger;
354 		handler = data->falling_handler;
355 	} else {
356 		return;
357 	}
358 
359 	if (handler) {
360 		handler(data->dev, trigger);
361 	}
362 }
363 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
364 
mcux_lpcmp_init(const struct device * dev)365 static int mcux_lpcmp_init(const struct device *dev)
366 {
367 	const struct mcux_lpcmp_config *config = dev->config;
368 	struct mcux_lpcmp_data *data = dev->data;
369 	int err;
370 
371 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
372 	if (err) {
373 		return err;
374 	}
375 
376 	/* LPCMP configuration */
377 	LPCMP_GetDefaultConfig(&data->lpcmp_config);
378 	data->lpcmp_config.powerMode = config->power_level;
379 	data->lpcmp_config.hysteresisMode = config->hysteresis_level;
380 	data->lpcmp_config.enableOutputPin = config->output_enable;
381 	data->lpcmp_config.enableInvertOutput = config->output_invert;
382 	data->lpcmp_config.useUnfilteredOutput = config->unfiltered;
383 #if defined(FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL) && FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL
384 	data->lpcmp_config.functionalSourceClock = config->function_clock;
385 #endif /* FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL */
386 	LPCMP_Init(config->base, &data->lpcmp_config);
387 
388 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
389 	data->dev = dev;
390 	k_work_init(&data->work, mcux_lpcmp_trigger_work_handler);
391 	config->irq_config_func(dev);
392 	LPCMP_EnableInterrupts(config->base, kLPCMP_OutputRisingInterruptEnable |
393 						     kLPCMP_OutputFallingInterruptEnable);
394 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
395 
396 	LPCMP_Enable(config->base, true);
397 
398 	return 0;
399 }
400 
401 static DEVICE_API(sensor, mcux_lpcmp_driver_api) = {
402 	.attr_set = mcux_lpcmp_attr_set,
403 	.attr_get = mcux_lpcmp_attr_get,
404 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
405 	.trigger_set = mcux_lpcmp_trigger_set,
406 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
407 	.sample_fetch = mcux_lpcmp_sample_fetch,
408 	.channel_get = mcux_lpcmp_channel_get,
409 };
410 
411 #define MCUX_LPCMP_DECLARE_CONFIG(n, config_func_init)                                             \
412 	static const struct mcux_lpcmp_config mcux_lpcmp_config_##n = {                            \
413 		.base = (LPCMP_Type *)DT_INST_REG_ADDR(n),                                         \
414 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                       \
415 		.output_enable = DT_INST_PROP_OR(n, enable_output_pin, 0),                         \
416 		.unfiltered = DT_INST_PROP_OR(n, use_unfiltered_output, 0),                        \
417 		.output_invert = DT_INST_PROP_OR(n, output_invert, 0),                             \
418 		.hysteresis_level = DT_INST_PROP_OR(n, hysteresis_level, 0),                       \
419 		.power_level = DT_INST_ENUM_IDX(n, power_level),                                   \
420 		.function_clock = DT_INST_ENUM_IDX(n, function_clock),                             \
421 		config_func_init}
422 
423 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
424 #define MCUX_LPCMP_CONFIG_FUNC(n)                                                                  \
425 	static void mcux_lpcmp_config_func_##n(const struct device *dev)                           \
426 	{                                                                                          \
427 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_lpcmp_isr,             \
428 			    DEVICE_DT_INST_GET(n), 0);                                             \
429 		irq_enable(DT_INST_IRQN(n));                                                       \
430 	}
431 #define MCUX_LPCMP_CONFIG_FUNC_INIT(n) .irq_config_func = mcux_lpcmp_config_func_##n
432 #define MCUX_LPCMP_INIT_CONFIG(n)      MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT(n))
433 #else /* CONFIG_MCUX_LPCMP_TRIGGER */
434 #define MCUX_LPCMP_CONFIG_FUNC(n)
435 #define MCUX_LPCMP_CONFIG_FUNC_INIT
436 #define MCUX_LPCMP_INIT_CONFIG(n) MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT)
437 #endif /* !CONFIG_MCUX_LPCMP_TRIGGER */
438 
439 #define MCUX_LPCMP_INIT(n)                                                                         \
440 	static struct mcux_lpcmp_data mcux_lpcmp_data_##n;                                         \
441 	static const struct mcux_lpcmp_config mcux_lpcmp_config_##n;                               \
442 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
443 	SENSOR_DEVICE_DT_INST_DEFINE(n, &mcux_lpcmp_init, NULL, &mcux_lpcmp_data_##n,              \
444 				     &mcux_lpcmp_config_##n, POST_KERNEL,                          \
445 				     CONFIG_SENSOR_INIT_PRIORITY, &mcux_lpcmp_driver_api);         \
446 	MCUX_LPCMP_CONFIG_FUNC(n)                                                                  \
447 	MCUX_LPCMP_INIT_CONFIG(n);
448 
449 DT_INST_FOREACH_STATUS_OKAY(MCUX_LPCMP_INIT)
450