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 	__ASSERT_NO_MSG(val != NULL);
277 
278 	if (chan != SENSOR_CHAN_ALL && (int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
279 		return -ENOTSUP;
280 	}
281 
282 	data->cout = ((LPCMP_GetStatusFlags(config->base) &
283 		       (uint32_t)kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag);
284 
285 	return 0;
286 }
287 
mcux_lpcmp_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)288 static int mcux_lpcmp_channel_get(const struct device *dev, enum sensor_channel chan,
289 				  struct sensor_value *val)
290 {
291 	struct mcux_lpcmp_data *data = dev->data;
292 
293 	__ASSERT_NO_MSG(val != NULL);
294 
295 	if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
296 		return -ENOTSUP;
297 	}
298 
299 	val->val1 = data->cout ? 1 : 0;
300 	val->val2 = 0;
301 
302 	return 0;
303 }
304 
305 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
mcux_lpcmp_isr(const struct device * dev)306 static void mcux_lpcmp_isr(const struct device *dev)
307 {
308 	const struct mcux_lpcmp_config *config = dev->config;
309 	struct mcux_lpcmp_data *data = dev->data;
310 
311 	data->status = LPCMP_GetStatusFlags(config->base);
312 	LPCMP_ClearStatusFlags(config->base, data->status);
313 
314 	k_work_submit(&data->work);
315 }
316 
mcux_lpcmp_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)317 static int mcux_lpcmp_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
318 				  sensor_trigger_handler_t handler)
319 {
320 	struct mcux_lpcmp_data *data = dev->data;
321 
322 	__ASSERT_NO_MSG(trig != NULL);
323 
324 	if ((int16_t)trig->chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) {
325 		return -ENOTSUP;
326 	}
327 
328 	if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_RISING) {
329 		data->rising_handler = handler;
330 		data->rising_trigger = trig;
331 	} else if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_FALLING) {
332 		data->falling_handler = handler;
333 		data->falling_trigger = trig;
334 	} else {
335 		return -ENOTSUP;
336 	}
337 
338 	return 0;
339 }
340 
mcux_lpcmp_trigger_work_handler(struct k_work * item)341 static void mcux_lpcmp_trigger_work_handler(struct k_work *item)
342 {
343 	struct mcux_lpcmp_data *data = CONTAINER_OF(item, struct mcux_lpcmp_data, work);
344 	const struct sensor_trigger *trigger;
345 	sensor_trigger_handler_t handler = NULL;
346 
347 	if (((data->status & kLPCMP_OutputRisingEventFlag) == kLPCMP_OutputRisingEventFlag) &&
348 	    ((data->status & kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag)) {
349 		trigger = data->rising_trigger;
350 		handler = data->rising_handler;
351 	} else if (((data->status & kLPCMP_OutputFallingEventFlag) ==
352 		    kLPCMP_OutputFallingEventFlag) &&
353 		   ((data->status & kLPCMP_OutputAssertEventFlag) !=
354 		    kLPCMP_OutputAssertEventFlag)) {
355 		trigger = data->falling_trigger;
356 		handler = data->falling_handler;
357 	} else {
358 		return;
359 	}
360 
361 	if (handler) {
362 		handler(data->dev, trigger);
363 	}
364 }
365 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
366 
mcux_lpcmp_init(const struct device * dev)367 static int mcux_lpcmp_init(const struct device *dev)
368 {
369 	const struct mcux_lpcmp_config *config = dev->config;
370 	struct mcux_lpcmp_data *data = dev->data;
371 	int err;
372 
373 	err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
374 	if (err) {
375 		return err;
376 	}
377 
378 	/* LPCMP configuration */
379 	LPCMP_GetDefaultConfig(&data->lpcmp_config);
380 	data->lpcmp_config.powerMode = config->power_level;
381 	data->lpcmp_config.hysteresisMode = config->hysteresis_level;
382 	data->lpcmp_config.enableOutputPin = config->output_enable;
383 	data->lpcmp_config.enableInvertOutput = config->output_invert;
384 	data->lpcmp_config.useUnfilteredOutput = config->unfiltered;
385 #if defined(FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL) && FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL
386 	data->lpcmp_config.functionalSourceClock = config->function_clock;
387 #endif /* FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL */
388 	LPCMP_Init(config->base, &data->lpcmp_config);
389 
390 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
391 	data->dev = dev;
392 	k_work_init(&data->work, mcux_lpcmp_trigger_work_handler);
393 	config->irq_config_func(dev);
394 	LPCMP_EnableInterrupts(config->base, kLPCMP_OutputRisingInterruptEnable |
395 						     kLPCMP_OutputFallingInterruptEnable);
396 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
397 
398 	LPCMP_Enable(config->base, true);
399 
400 	return 0;
401 }
402 
403 static const struct sensor_driver_api mcux_lpcmp_driver_api = {
404 	.attr_set = mcux_lpcmp_attr_set,
405 	.attr_get = mcux_lpcmp_attr_get,
406 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
407 	.trigger_set = mcux_lpcmp_trigger_set,
408 #endif /* CONFIG_MCUX_LPCMP_TRIGGER */
409 	.sample_fetch = mcux_lpcmp_sample_fetch,
410 	.channel_get = mcux_lpcmp_channel_get,
411 };
412 
413 #define MCUX_LPCMP_DECLARE_CONFIG(n, config_func_init)                                             \
414 	static const struct mcux_lpcmp_config mcux_lpcmp_config_##n = {                            \
415 		.base = (LPCMP_Type *)DT_INST_REG_ADDR(n),                                         \
416 		.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),                                       \
417 		.output_enable = DT_INST_PROP_OR(n, enable_output_pin, 0),                         \
418 		.unfiltered = DT_INST_PROP_OR(n, use_unfiltered_output, 0),                        \
419 		.output_invert = DT_INST_PROP_OR(n, output_invert, 0),                             \
420 		.hysteresis_level = DT_INST_PROP_OR(n, hysteresis_level, 0),                       \
421 		.power_level = DT_INST_ENUM_IDX(n, power_level),                                   \
422 		.function_clock = DT_INST_ENUM_IDX(n, function_clock),                             \
423 		config_func_init}
424 
425 #ifdef CONFIG_MCUX_LPCMP_TRIGGER
426 #define MCUX_LPCMP_CONFIG_FUNC(n)                                                                  \
427 	static void mcux_lpcmp_config_func_##n(const struct device *dev)                           \
428 	{                                                                                          \
429 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_lpcmp_isr,             \
430 			    DEVICE_DT_INST_GET(n), 0);                                             \
431 		irq_enable(DT_INST_IRQN(n));                                                       \
432 	}
433 #define MCUX_LPCMP_CONFIG_FUNC_INIT(n) .irq_config_func = mcux_lpcmp_config_func_##n
434 #define MCUX_LPCMP_INIT_CONFIG(n)      MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT(n))
435 #else /* CONFIG_MCUX_LPCMP_TRIGGER */
436 #define MCUX_LPCMP_CONFIG_FUNC(n)
437 #define MCUX_LPCMP_CONFIG_FUNC_INIT
438 #define MCUX_LPCMP_INIT_CONFIG(n) MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT)
439 #endif /* !CONFIG_MCUX_LPCMP_TRIGGER */
440 
441 #define MCUX_LPCMP_INIT(n)                                                                         \
442 	static struct mcux_lpcmp_data mcux_lpcmp_data_##n;                                         \
443 	static const struct mcux_lpcmp_config mcux_lpcmp_config_##n;                               \
444 	PINCTRL_DT_INST_DEFINE(n);                                                                 \
445 	SENSOR_DEVICE_DT_INST_DEFINE(n, &mcux_lpcmp_init, NULL, &mcux_lpcmp_data_##n,              \
446 				     &mcux_lpcmp_config_##n, POST_KERNEL,                          \
447 				     CONFIG_SENSOR_INIT_PRIORITY, &mcux_lpcmp_driver_api);         \
448 	MCUX_LPCMP_CONFIG_FUNC(n)                                                                  \
449 	MCUX_LPCMP_INIT_CONFIG(n);
450 
451 DT_INST_FOREACH_STATUS_OKAY(MCUX_LPCMP_INIT)
452