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