drivers: sensor/nxp: Add NXP LPCMP driver

Add NXP LPCMP driver

Signed-off-by: Zhaoxiang Jin <Zhaoxiang.Jin_1@nxp.com>
This commit is contained in:
Zhaoxiang Jin 2024-04-24 19:24:00 +08:00 committed by David Leach
commit 0507021009
5 changed files with 476 additions and 0 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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)

View file

@ -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.

View file

@ -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 <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include <fsl_lpcmp.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/sensor/mcux_lpcmp.h>
#include <fsl_lpcmp.h>
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)