From 05070210099a3bb216fe5037bd352b3bbd0e1bfa Mon Sep 17 00:00:00 2001 From: Zhaoxiang Jin Date: Wed, 24 Apr 2024 19:24:00 +0800 Subject: [PATCH] drivers: sensor/nxp: Add NXP LPCMP driver Add NXP LPCMP driver Signed-off-by: Zhaoxiang Jin --- drivers/sensor/nxp/CMakeLists.txt | 1 + drivers/sensor/nxp/Kconfig | 1 + drivers/sensor/nxp/mcux_lpcmp/CMakeLists.txt | 5 + drivers/sensor/nxp/mcux_lpcmp/Kconfig | 18 + drivers/sensor/nxp/mcux_lpcmp/mcux_lpcmp.c | 451 +++++++++++++++++++ 5 files changed, 476 insertions(+) create mode 100644 drivers/sensor/nxp/mcux_lpcmp/CMakeLists.txt create mode 100644 drivers/sensor/nxp/mcux_lpcmp/Kconfig create mode 100644 drivers/sensor/nxp/mcux_lpcmp/mcux_lpcmp.c diff --git a/drivers/sensor/nxp/CMakeLists.txt b/drivers/sensor/nxp/CMakeLists.txt index 21e7825187f..05270c58dce 100644 --- a/drivers/sensor/nxp/CMakeLists.txt +++ b/drivers/sensor/nxp/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory_ifdef(CONFIG_FXAS21002 fxas21002) add_subdirectory_ifdef(CONFIG_FXOS8700 fxos8700) add_subdirectory_ifdef(CONFIG_MCUX_ACMP mcux_acmp) +add_subdirectory_ifdef(CONFIG_MCUX_LPCMP mcux_lpcmp) add_subdirectory_ifdef(CONFIG_NXP_TEMPMON nxp_tempmon) add_subdirectory_ifdef(CONFIG_QDEC_MCUX qdec_mcux) add_subdirectory_ifdef(CONFIG_QDEC_NXP_S32 qdec_nxp_s32) diff --git a/drivers/sensor/nxp/Kconfig b/drivers/sensor/nxp/Kconfig index bf1565580aa..3da1f9c6475 100644 --- a/drivers/sensor/nxp/Kconfig +++ b/drivers/sensor/nxp/Kconfig @@ -5,6 +5,7 @@ source "drivers/sensor/nxp/fxas21002/Kconfig" source "drivers/sensor/nxp/fxos8700/Kconfig" source "drivers/sensor/nxp/mcux_acmp/Kconfig" +source "drivers/sensor/nxp/mcux_lpcmp/Kconfig" source "drivers/sensor/nxp/nxp_kinetis_temp/Kconfig" source "drivers/sensor/nxp/nxp_tempmon/Kconfig" source "drivers/sensor/nxp/qdec_mcux/Kconfig" diff --git a/drivers/sensor/nxp/mcux_lpcmp/CMakeLists.txt b/drivers/sensor/nxp/mcux_lpcmp/CMakeLists.txt new file mode 100644 index 00000000000..0ad6fe11b1a --- /dev/null +++ b/drivers/sensor/nxp/mcux_lpcmp/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_MCUX_LPCMP mcux_lpcmp.c) diff --git a/drivers/sensor/nxp/mcux_lpcmp/Kconfig b/drivers/sensor/nxp/mcux_lpcmp/Kconfig new file mode 100644 index 00000000000..3e04b3c26c8 --- /dev/null +++ b/drivers/sensor/nxp/mcux_lpcmp/Kconfig @@ -0,0 +1,18 @@ +# NXP MCUX low power comparator (LPCMP) configuration options + +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config MCUX_LPCMP + bool "NXP LPCMP driver" + default y + depends on DT_HAS_NXP_LPCMP_ENABLED + select PINCTRL + help + Enable the MCUX LPCMP driver. + +config MCUX_LPCMP_TRIGGER + bool "Trigger support" + depends on MCUX_LPCMP + help + Enable trigger support for the NXP LPCMP. diff --git a/drivers/sensor/nxp/mcux_lpcmp/mcux_lpcmp.c b/drivers/sensor/nxp/mcux_lpcmp/mcux_lpcmp.c new file mode 100644 index 00000000000..97e55a7d3c6 --- /dev/null +++ b/drivers/sensor/nxp/mcux_lpcmp/mcux_lpcmp.c @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2020 Vestas Wind Systems A/S + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_lpcmp + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mcux_lpcmp, CONFIG_SENSOR_LOG_LEVEL); + +struct mcux_lpcmp_config { + LPCMP_Type *base; + const struct pinctrl_dev_config *pincfg; +#ifdef CONFIG_MCUX_LPCMP_TRIGGER + void (*irq_config_func)(const struct device *dev); +#endif /* CONFIG_MCUX_LPCMP_TRIGGER */ + bool output_enable: 1; + bool unfiltered: 1; + bool output_invert: 1; + lpcmp_hysteresis_mode_t hysteresis_level; + lpcmp_power_mode_t power_level; + lpcmp_functional_source_clock_t function_clock; +}; + +struct mcux_lpcmp_data { + lpcmp_config_t lpcmp_config; + lpcmp_dac_config_t dac_config; + lpcmp_filter_config_t filter_config; + lpcmp_window_control_config_t window_config; +#ifdef CONFIG_MCUX_LPCMP_TRIGGER + const struct device *dev; + const struct sensor_trigger *rising_trigger; + sensor_trigger_handler_t rising_handler; + const struct sensor_trigger *falling_trigger; + sensor_trigger_handler_t falling_handler; + struct k_work work; + volatile uint32_t status; +#endif /* CONFIG_MCUX_LPCMP_TRIGGER */ + bool cout; +}; + +static int mcux_lpcmp_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + const struct mcux_lpcmp_config *config = dev->config; + struct mcux_lpcmp_data *data = dev->data; + int32_t val1 = val->val1; + + __ASSERT_NO_MSG(val != NULL); + + if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) { + return -ENOTSUP; + } + + if (val->val2 != 0) { + return -EINVAL; + } + + switch ((int16_t)attr) { + /** Analog input mux related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_POSITIVE_MUX_INPUT: + LOG_DBG("positive mux = %d", val1); + if (val1 >= 0 && val1 < 8) { + config->base->CCR2 = ((config->base->CCR2 & (~LPCMP_CCR2_PSEL_MASK)) | + LPCMP_CCR2_PSEL(val1)); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_NEGATIVE_MUX_INPUT: + LOG_DBG("negative mux = %d", val1); + if (val1 >= 0 && val1 < 8) { + config->base->CCR2 = ((config->base->CCR2 & (~LPCMP_CCR2_MSEL_MASK)) | + LPCMP_CCR2_MSEL(val1)); + } else { + return -EINVAL; + } + break; + + /** DAC related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_DAC_ENABLE: + LOG_DBG("dac enable = %d", val1); + if (val1 == 1) { + config->base->DCR |= LPCMP_DCR_DAC_EN_MASK; + } else if (val1 == 0) { + config->base->DCR &= ~LPCMP_DCR_DAC_EN_MASK; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_HIGH_POWER_MODE_ENABLE: + LOG_DBG("dac power mode = %d", val1); + if ((val1 == 1) || (val1 == 0)) { + data->dac_config.enableLowPowerMode = (bool)val1; + LPCMP_SetDACConfig(config->base, &data->dac_config); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_REFERENCE_VOLTAGE_SOURCE: + LOG_DBG("dac vref = %d", val1); + if (val1 >= kLPCMP_VrefSourceVin1 && val1 <= kLPCMP_VrefSourceVin2) { + data->dac_config.referenceVoltageSource = val1; + LPCMP_SetDACConfig(config->base, &data->dac_config); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_OUTPUT_VOLTAGE: + LOG_DBG("dac value = %d", val1); + if (val1 >= 0 && val1 < 256) { + data->dac_config.DACValue = val1; + LPCMP_SetDACConfig(config->base, &data->dac_config); + } else { + return -EINVAL; + } + break; + + /** Sample and filter related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_SAMPLE_ENABLE: + LOG_DBG("Filter sample enable = %d", val1); + if ((val1 == 1) || (val1 == 0)) { + data->filter_config.enableSample = (bool)val1; + LPCMP_SetFilterConfig(config->base, &data->filter_config); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_FILTER_COUNT: + LOG_DBG("sample count = %d", val1); + data->filter_config.filterSampleCount = val1; + LPCMP_SetFilterConfig(config->base, &data->filter_config); + break; + case SENSOR_ATTR_MCUX_LPCMP_FILTER_PERIOD: + LOG_DBG("sample period = %d", val1); + data->filter_config.filterSamplePeriod = val1; + LPCMP_SetFilterConfig(config->base, &data->filter_config); + break; + + /** Window related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_ENABLE: + LOG_DBG("Window enable = %d", val1); + if ((val1 == 1) || (val1 == 0)) { + LPCMP_EnableWindowMode(config->base, (bool)val1); + } else { + return -EINVAL; + } + break; + + case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_SIGNAL_INVERT_ENABLE: + LOG_DBG("Invert window signal = %d", val1); + if ((val1 == 1) || (val1 == 0)) { + data->window_config.enableInvertWindowSignal = (bool)val1; + LPCMP_SetWindowControl(config->base, &data->window_config); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_COUTA_SIGNAL: + LOG_DBG("COUTA signal = %d", val1); + if ((val1 >= (int32_t)kLPCMP_COUTASignalNoSet) && + (val1 < (int32_t)kLPCMP_COUTASignalHigh)) { + data->window_config.COUTASignal = val1; + LPCMP_SetWindowControl(config->base, &data->window_config); + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_MCUX_LPCMP_COUT_EVENT_TO_CLOSE_WINDOW: + LOG_DBG("COUT event = %d", val1); + if ((val1 >= (int32_t)kLPCMP_CLoseWindowEventNoSet) && + (val1 < (int32_t)kLPCMP_CLoseWindowEventBothEdge)) { + data->window_config.closeWindowEvent = val1; + LPCMP_SetWindowControl(config->base, &data->window_config); + } else { + return -EINVAL; + } + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +static int mcux_lpcmp_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + const struct mcux_lpcmp_config *config = dev->config; + struct mcux_lpcmp_data *data = dev->data; + + __ASSERT_NO_MSG(val != NULL); + + if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) { + return -ENOTSUP; + } + + switch ((int16_t)attr) { + /** Analog mux related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_POSITIVE_MUX_INPUT: + val->val1 = (int32_t)((config->base->CCR2) & + LPCMP_CCR2_PSEL_MASK >> LPCMP_CCR2_PSEL_SHIFT); + break; + case SENSOR_ATTR_MCUX_LPCMP_NEGATIVE_MUX_INPUT: + val->val1 = (int32_t)((config->base->CCR2) & + LPCMP_CCR2_MSEL_MASK >> LPCMP_CCR2_MSEL_SHIFT); + break; + + /** DAC related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_DAC_ENABLE: + val->val1 = (int32_t)((config->base->DCR) & + LPCMP_DCR_DAC_EN_MASK >> LPCMP_DCR_DAC_EN_SHIFT); + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_HIGH_POWER_MODE_ENABLE: + val->val1 = (int32_t)(data->dac_config.enableLowPowerMode); + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_REFERENCE_VOLTAGE_SOURCE: + val->val1 = (int32_t)(data->dac_config.referenceVoltageSource); + break; + case SENSOR_ATTR_MCUX_LPCMP_DAC_OUTPUT_VOLTAGE: + val->val1 = (int32_t)(data->dac_config.DACValue); + break; + + /** Sample and filter related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_SAMPLE_ENABLE: + val->val1 = (int32_t)(data->filter_config.enableSample); + break; + case SENSOR_ATTR_MCUX_LPCMP_FILTER_COUNT: + val->val1 = (int32_t)(data->filter_config.filterSampleCount); + break; + case SENSOR_ATTR_MCUX_LPCMP_FILTER_PERIOD: + val->val1 = (int32_t)(data->filter_config.filterSamplePeriod); + break; + + /** Window related attributes */ + case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_ENABLE: + val->val1 = (int32_t)((config->base->CCR1) & + LPCMP_CCR1_WINDOW_EN_MASK >> LPCMP_CCR1_WINDOW_EN_SHIFT); + break; + case SENSOR_ATTR_MCUX_LPCMP_COUTA_WINDOW_SIGNAL_INVERT_ENABLE: + val->val1 = (int32_t)(data->window_config.enableInvertWindowSignal); + break; + case SENSOR_ATTR_MCUX_LPCMP_COUTA_SIGNAL: + val->val1 = (int32_t)(data->window_config.COUTASignal); + break; + case SENSOR_ATTR_MCUX_LPCMP_COUT_EVENT_TO_CLOSE_WINDOW: + val->val1 = (int32_t)(data->window_config.closeWindowEvent); + break; + + default: + return -ENOTSUP; + } + + val->val2 = 0; + + return 0; +} + +static int mcux_lpcmp_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + const struct mcux_lpcmp_config *config = dev->config; + struct mcux_lpcmp_data *data = dev->data; + + __ASSERT_NO_MSG(val != NULL); + + if (chan != SENSOR_CHAN_ALL && (int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) { + return -ENOTSUP; + } + + data->cout = ((LPCMP_GetStatusFlags(config->base) & + (uint32_t)kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag); + + return 0; +} + +static int mcux_lpcmp_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct mcux_lpcmp_data *data = dev->data; + + __ASSERT_NO_MSG(val != NULL); + + if ((int16_t)chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) { + return -ENOTSUP; + } + + val->val1 = data->cout ? 1 : 0; + val->val2 = 0; + + return 0; +} + +#ifdef CONFIG_MCUX_LPCMP_TRIGGER +static void mcux_lpcmp_isr(const struct device *dev) +{ + const struct mcux_lpcmp_config *config = dev->config; + struct mcux_lpcmp_data *data = dev->data; + + data->status = LPCMP_GetStatusFlags(config->base); + LPCMP_ClearStatusFlags(config->base, data->status); + + k_work_submit(&data->work); +} + +static int mcux_lpcmp_trigger_set(const struct device *dev, const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct mcux_lpcmp_data *data = dev->data; + + __ASSERT_NO_MSG(trig != NULL); + + if ((int16_t)trig->chan != SENSOR_CHAN_MCUX_LPCMP_OUTPUT) { + return -ENOTSUP; + } + + if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_RISING) { + data->rising_handler = handler; + data->rising_trigger = trig; + } else if ((int16_t)trig->type == SENSOR_TRIG_MCUX_LPCMP_OUTPUT_FALLING) { + data->falling_handler = handler; + data->falling_trigger = trig; + } else { + return -ENOTSUP; + } + + return 0; +} + +static void mcux_lpcmp_trigger_work_handler(struct k_work *item) +{ + struct mcux_lpcmp_data *data = CONTAINER_OF(item, struct mcux_lpcmp_data, work); + const struct sensor_trigger *trigger; + sensor_trigger_handler_t handler = NULL; + + if (((data->status & kLPCMP_OutputRisingEventFlag) == kLPCMP_OutputRisingEventFlag) && + ((data->status & kLPCMP_OutputAssertEventFlag) == kLPCMP_OutputAssertEventFlag)) { + trigger = data->rising_trigger; + handler = data->rising_handler; + } else if (((data->status & kLPCMP_OutputFallingEventFlag) == + kLPCMP_OutputFallingEventFlag) && + ((data->status & kLPCMP_OutputAssertEventFlag) != + kLPCMP_OutputAssertEventFlag)) { + trigger = data->falling_trigger; + handler = data->falling_handler; + } else { + return; + } + + if (handler) { + handler(data->dev, trigger); + } +} +#endif /* CONFIG_MCUX_LPCMP_TRIGGER */ + +static int mcux_lpcmp_init(const struct device *dev) +{ + const struct mcux_lpcmp_config *config = dev->config; + struct mcux_lpcmp_data *data = dev->data; + int err; + + err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (err) { + return err; + } + + /* LPCMP configuration */ + LPCMP_GetDefaultConfig(&data->lpcmp_config); + data->lpcmp_config.powerMode = config->power_level; + data->lpcmp_config.hysteresisMode = config->hysteresis_level; + data->lpcmp_config.enableOutputPin = config->output_enable; + data->lpcmp_config.enableInvertOutput = config->output_invert; + data->lpcmp_config.useUnfilteredOutput = config->unfiltered; +#if defined(FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL) && FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL + data->lpcmp_config.functionalSourceClock = config->function_clock; +#endif /* FSL_FEATURE_LPCMP_HAS_CCR1_FUNC_CLK_SEL */ + LPCMP_Init(config->base, &data->lpcmp_config); + +#ifdef CONFIG_MCUX_LPCMP_TRIGGER + data->dev = dev; + k_work_init(&data->work, mcux_lpcmp_trigger_work_handler); + config->irq_config_func(dev); + LPCMP_EnableInterrupts(config->base, kLPCMP_OutputRisingInterruptEnable | + kLPCMP_OutputFallingInterruptEnable); +#endif /* CONFIG_MCUX_LPCMP_TRIGGER */ + + LPCMP_Enable(config->base, true); + + return 0; +} + +static const struct sensor_driver_api mcux_lpcmp_driver_api = { + .attr_set = mcux_lpcmp_attr_set, + .attr_get = mcux_lpcmp_attr_get, +#ifdef CONFIG_MCUX_LPCMP_TRIGGER + .trigger_set = mcux_lpcmp_trigger_set, +#endif /* CONFIG_MCUX_LPCMP_TRIGGER */ + .sample_fetch = mcux_lpcmp_sample_fetch, + .channel_get = mcux_lpcmp_channel_get, +}; + +#define MCUX_LPCMP_DECLARE_CONFIG(n, config_func_init) \ + static const struct mcux_lpcmp_config mcux_lpcmp_config_##n = { \ + .base = (LPCMP_Type *)DT_INST_REG_ADDR(n), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .output_enable = DT_INST_PROP_OR(n, enable_output_pin, 0), \ + .unfiltered = DT_INST_PROP_OR(n, use_unfiltered_output, 0), \ + .output_invert = DT_INST_PROP_OR(n, output_invert, 0), \ + .hysteresis_level = DT_INST_PROP_OR(n, hysteresis_level, 0), \ + .power_level = DT_INST_ENUM_IDX(n, power_level), \ + .function_clock = DT_INST_ENUM_IDX(n, function_clock), \ + config_func_init} + +#ifdef CONFIG_MCUX_LPCMP_TRIGGER +#define MCUX_LPCMP_CONFIG_FUNC(n) \ + static void mcux_lpcmp_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), mcux_lpcmp_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(DT_INST_IRQN(n)); \ + } +#define MCUX_LPCMP_CONFIG_FUNC_INIT(n) .irq_config_func = mcux_lpcmp_config_func_##n +#define MCUX_LPCMP_INIT_CONFIG(n) MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT(n)) +#else /* CONFIG_MCUX_LPCMP_TRIGGER */ +#define MCUX_LPCMP_CONFIG_FUNC(n) +#define MCUX_LPCMP_CONFIG_FUNC_INIT +#define MCUX_LPCMP_INIT_CONFIG(n) MCUX_LPCMP_DECLARE_CONFIG(n, MCUX_LPCMP_CONFIG_FUNC_INIT) +#endif /* !CONFIG_MCUX_LPCMP_TRIGGER */ + +#define MCUX_LPCMP_INIT(n) \ + static struct mcux_lpcmp_data mcux_lpcmp_data_##n; \ + static const struct mcux_lpcmp_config mcux_lpcmp_config_##n; \ + PINCTRL_DT_INST_DEFINE(n); \ + SENSOR_DEVICE_DT_INST_DEFINE(n, &mcux_lpcmp_init, NULL, &mcux_lpcmp_data_##n, \ + &mcux_lpcmp_config_##n, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &mcux_lpcmp_driver_api); \ + MCUX_LPCMP_CONFIG_FUNC(n) \ + MCUX_LPCMP_INIT_CONFIG(n); + +DT_INST_FOREACH_STATUS_OKAY(MCUX_LPCMP_INIT)