drivers: comparator: add mcux acmp device driver

Add mcux SDK based kinetis acmp device driver implementing the
comparator device driver API.

Signed-off-by: Bjarki Arge Andreasen <bjarki.andreasen@nordicsemi.no>
This commit is contained in:
Bjarki Arge Andreasen 2024-08-19 11:53:09 +02:00 committed by Anas Nashif
parent 3b08a08c7f
commit d37f844104
6 changed files with 831 additions and 1 deletions

View file

@ -6,5 +6,6 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/comparator.h)
zephyr_library() zephyr_library()
zephyr_library_sources_ifdef(CONFIG_USERSPACE comparator_handlers.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE comparator_handlers.c)
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_MCUX_ACMP comparator_mcux_acmp.c)
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c) zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_COMP comparator_nrf_comp.c)
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c) zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NRF_LPCOMP comparator_nrf_lpcomp.c)

View file

@ -18,6 +18,7 @@ config COMPARATOR_INIT_PRIORITY
help help
Comparator device driver initialization priority. Comparator device driver initialization priority.
rsource "Kconfig.mcux_acmp"
rsource "Kconfig.nrf_comp" rsource "Kconfig.nrf_comp"
rsource "Kconfig.nrf_lpcomp" rsource "Kconfig.nrf_lpcomp"

View file

@ -0,0 +1,9 @@
# Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0
config COMPARATOR_MCUX_ACMP
bool "NXP MCUX ACMP comparator driver"
default y
depends on DT_HAS_NXP_KINETIS_ACMP_ENABLED
select PINCTRL
select MCUX_ACMP

View file

@ -0,0 +1,651 @@
/*
* Copyright (c) 2020 Vestas Wind Systems A/S
* Copyright (c) 2022 NXP
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <fsl_acmp.h>
#include <zephyr/kernel.h>
#include <zephyr/pm/device.h>
#include <zephyr/drivers/comparator.h>
#include <zephyr/drivers/comparator/mcux_acmp.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(nxp_kinetis_acmp, CONFIG_COMPARATOR_LOG_LEVEL);
#define DT_DRV_COMPAT nxp_kinetis_acmp
/*
* DAC is a register defined in the MCUX HAL. We don't need it here and it conflicts
* with the COMP_MCUX_ACMP_PORT_INPUT_DAC definition so undef it here.
*/
#ifdef DAC
#undef DAC
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT) && (FSL_FEATURE_ACMP_HAS_C0_OFFSET_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_OFFSET 1
#else
#define COMP_MCUX_ACMP_HAS_OFFSET 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT) && (FSL_FEATURE_ACMP_HAS_C0_HYSTCTR_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_HYSTERESIS 1
#else
#define COMP_MCUX_ACMP_HAS_HYSTERESIS 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INPSEL_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_INPSEL 1
#else
#define COMP_MCUX_ACMP_HAS_INPSEL 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT) && (FSL_FEATURE_ACMP_HAS_C1_INNSEL_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_INNSEL 1
#else
#define COMP_MCUX_ACMP_HAS_INNSEL 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DACOE_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 1
#else
#define COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT) && (FSL_FEATURE_ACMP_HAS_C1_DMODE_BIT == 1U)
#define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 1
#else
#define COMP_MCUX_ACMP_HAS_DAC_WORK_MODE 0
#endif
#if defined(FSL_FEATURE_ACMP_HAS_C3_REG) && (FSL_FEATURE_ACMP_HAS_C3_REG != 0U)
#define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 1
#else
#define COMP_MCUX_ACMP_HAS_DISCRETE_MODE 0
#endif
#if !(defined(FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE) && (FSL_FEATURE_ACMP_HAS_NO_WINDOW_MODE == 1U))
#define COMP_MCUX_ACMP_HAS_WINDOW_MODE 1
#else
#define COMP_MCUX_ACMP_HAS_WINDOW_MODE 0
#endif
#define MCUX_ACMP_ENUM(name, value) \
_CONCAT_4(COMP_MCUX_ACMP_, name, _, value)
#define MCUX_ACMP_DT_INST_ENUM(inst, name, prop) \
MCUX_ACMP_ENUM(name, DT_INST_STRING_TOKEN(inst, prop))
#define MCUX_ACMP_DT_INST_ENUM_OR(inst, name, prop, or) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, prop), \
(MCUX_ACMP_DT_INST_ENUM(inst, name, prop)), \
(MCUX_ACMP_ENUM(name, or)))
#define MCUX_ACMP_DT_INST_OFFSET_MODE(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, OFFSET_MODE, offset_mode, LEVEL0)
#define MCUX_ACMP_DT_INST_HYST_MODE(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, HYSTERESIS_MODE, hysteresis_mode, LEVEL0)
#define MCUX_ACMP_DT_INST_EN_HS_MODE(inst) \
DT_INST_PROP(inst, enable_high_speed_mode)
#define MCUX_ACMP_DT_INST_INV_OUT(inst) \
DT_INST_PROP(inst, invert_output)
#define MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst) \
DT_INST_PROP(inst, use_unfiltered_output)
#define MCUX_ACMP_DT_INST_EN_PIN_OUT(inst) \
DT_INST_PROP(inst, enable_pin_out)
#define MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst) \
{ \
.offset_mode = MCUX_ACMP_DT_INST_OFFSET_MODE(inst), \
.hysteresis_mode = MCUX_ACMP_DT_INST_HYST_MODE(inst), \
.enable_high_speed_mode = MCUX_ACMP_DT_INST_EN_HS_MODE(inst), \
.invert_output = MCUX_ACMP_DT_INST_INV_OUT(inst), \
.use_unfiltered_output = MCUX_ACMP_DT_INST_USE_UNFILTERED_OUT(inst), \
.enable_pin_output = MCUX_ACMP_DT_INST_EN_PIN_OUT(inst), \
}
#define MCUX_ACMP_DT_INST_P_MUX_IN(inst) \
MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, positive_mux_input)
#define MCUX_ACMP_DT_INST_N_MUX_IN(inst) \
MCUX_ACMP_DT_INST_ENUM(inst, MUX_INPUT, negative_mux_input)
#define MCUX_ACMP_DT_INST_P_PORT_IN(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, positive_port_input, MUX)
#define MCUX_ACMP_DT_INST_N_PORT_IN(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, PORT_INPUT, negative_port_input, MUX)
#define MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst) \
{ \
.positive_mux_input = MCUX_ACMP_DT_INST_P_MUX_IN(inst), \
.negative_mux_input = MCUX_ACMP_DT_INST_N_MUX_IN(inst), \
.positive_port_input = MCUX_ACMP_DT_INST_P_PORT_IN(inst), \
.negative_port_input = MCUX_ACMP_DT_INST_N_PORT_IN(inst), \
}
#define MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst) \
DT_INST_PROP(inst, filter_enable_sample)
#define MCUX_ACMP_DT_INST_FILTER_COUNT(inst) \
DT_INST_PROP_OR(inst, filter_count, 0)
#define MCUX_ACMP_DT_INST_FILTER_PERIOD(inst) \
DT_INST_PROP_OR(inst, filter_period, 0)
#define MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst) \
{ \
.enable_sample = MCUX_ACMP_DT_INST_FILTER_EN_SAMPLE(inst), \
.filter_count = MCUX_ACMP_DT_INST_FILTER_COUNT(inst), \
.filter_period = MCUX_ACMP_DT_INST_FILTER_PERIOD(inst), \
}
#define MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, DAC_VREF_SOURCE, dac_vref_source, VIN1)
#define MCUX_ACMP_DT_INST_DAC_VALUE(inst) \
DT_INST_PROP_OR(inst, dac_value, 0)
#define MCUX_ACMP_DT_INST_DAC_EN(inst) \
DT_INST_PROP(inst, dac_enable)
#define MCUX_ACMP_DT_INST_DAC_EN_HS(inst) \
DT_INST_PROP(inst, dac_enable_high_speed)
#define MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst) \
{ \
.vref_source = MCUX_ACMP_DT_INST_DAC_VREF_SOURCE(inst), \
.value = MCUX_ACMP_DT_INST_DAC_VALUE(inst), \
.enable_output = MCUX_ACMP_DT_INST_DAC_EN(inst), \
.enable_high_speed_mode = MCUX_ACMP_DT_INST_DAC_EN_HS(inst), \
}
#define MCUX_ACMP_DT_INST_DM_EN_P_CH(inst) \
DT_INST_PROP(inst, discrete_mode_enable_positive_channel)
#define MCUX_ACMP_DT_INST_DM_EN_N_CH(inst) \
DT_INST_PROP(inst, discrete_mode_enable_negative_channel)
#define MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst) \
DT_INST_PROP(inst, discrete_mode_enable_resistor_divider)
#define MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_CLOCK, discrete_mode_clock_source, SLOW)
#define MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_SAMPLE_TIME, discrete_mode_sample_time, T1)
#define MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase1_time, ALT0)
#define MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst) \
MCUX_ACMP_DT_INST_ENUM_OR(inst, DM_PHASE_TIME, discrete_mode_phase2_time, ALT0)
#define MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst) \
{ \
.enable_positive_channel = MCUX_ACMP_DT_INST_DM_EN_P_CH(inst), \
.enable_negative_channel = MCUX_ACMP_DT_INST_DM_EN_N_CH(inst), \
.enable_resistor_divider = MCUX_ACMP_DT_INST_DM_EN_RES_DIV(inst), \
.clock_source = MCUX_ACMP_DT_INST_DM_CLOCK_SOURCE(inst), \
.sample_time = MCUX_ACMP_DT_INST_DM_SAMPLE_TIME(inst), \
.phase1_time = MCUX_ACMP_DT_INST_DM_PHASE1_TIME(inst), \
.phase2_time = MCUX_ACMP_DT_INST_DM_PHASE2_TIME(inst), \
}
#define MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst) \
DT_INST_PROP(inst, enable_window_mode)
struct mcux_acmp_config {
CMP_Type *base;
const struct pinctrl_dev_config *pincfg;
void (*irq_init)(void);
const struct comp_mcux_acmp_mode_config mode_config;
const struct comp_mcux_acmp_input_config input_config;
const struct comp_mcux_acmp_filter_config filter_config;
const struct comp_mcux_acmp_dac_config dac_config;
#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
const struct comp_mcux_acmp_dm_config dm_config;
#endif
#if COMP_MCUX_ACMP_HAS_WINDOW_MODE
bool enable_window_mode;
#endif
};
#if MCUX_ACMP_HAS_OFFSET
BUILD_ASSERT((int)kACMP_OffsetLevel0 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL0);
BUILD_ASSERT((int)kACMP_OffsetLevel1 == (int)COMP_MCUX_ACMP_OFFSET_MODE_LEVEL1);
#endif
#if COMP_MCUX_ACMP_HAS_HYSTERESIS
BUILD_ASSERT((int)kACMP_HysteresisLevel0 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL0);
BUILD_ASSERT((int)kACMP_HysteresisLevel1 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL1);
BUILD_ASSERT((int)kACMP_HysteresisLevel2 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL2);
BUILD_ASSERT((int)kACMP_HysteresisLevel3 == (int)COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL3);
#endif
BUILD_ASSERT((int)kACMP_VrefSourceVin1 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN1);
BUILD_ASSERT((int)kACMP_VrefSourceVin2 == (int)COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN2);
#if MCUX_ACMP_HAS_INPSEL || MCUX_ACMP_HAS_INNSEL
BUILD_ASSERT((int)kACMP_PortInputFromDAC == (int)COMP_MCUX_ACMP_PORT_INPUT_DAC);
BUILD_ASSERT((int)kACMP_PortInputFromMux == (int)COMP_MCUX_ACMP_PORT_INPUT_MUX);
#endif
#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
BUILD_ASSERT((int)kACMP_DiscreteClockSlow == (int)COMP_MCUX_ACMP_DM_CLOCK_SLOW);
BUILD_ASSERT((int)kACMP_DiscreteClockFast == (int)COMP_MCUX_ACMP_DM_CLOCK_FAST);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs1T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T1);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs2T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T2);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs4T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T4);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs8T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T8);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs16T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T16);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs32T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T32);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs64T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T64);
BUILD_ASSERT((int)kACMP_DiscreteSampleTimeAs256T == (int)COMP_MCUX_ACMP_DM_SAMPLE_TIME_T256);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt0 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT0);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt1 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT1);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt2 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT2);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt3 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT3);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt4 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT4);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt5 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT5);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt6 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT6);
BUILD_ASSERT((int)kACMP_DiscretePhaseTimeAlt7 == (int)COMP_MCUX_ACMP_DM_PHASE_TIME_ALT7);
#endif
struct mcux_acmp_data {
uint32_t interrupt_mask;
comparator_callback_t callback;
void *user_data;
};
#if CONFIG_PM_DEVICE
static bool mcux_acmp_is_resumed(const struct device *dev)
{
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
return state == PM_DEVICE_STATE_ACTIVE;
}
#else
static bool mcux_acmp_is_resumed(const struct device *dev)
{
ARG_UNUSED(dev);
return true;
}
#endif
static int mcux_acmp_get_output(const struct device *dev)
{
const struct mcux_acmp_config *config = dev->config;
uint32_t status;
status = ACMP_GetStatusFlags(config->base);
return (status & kACMP_OutputAssertEventFlag) ? 1 : 0;
}
static int mcux_acmp_set_trigger(const struct device *dev,
enum comparator_trigger trigger)
{
const struct mcux_acmp_config *config = dev->config;
struct mcux_acmp_data *data = dev->data;
ACMP_DisableInterrupts(config->base, UINT32_MAX);
switch (trigger) {
case COMPARATOR_TRIGGER_NONE:
data->interrupt_mask = 0;
break;
case COMPARATOR_TRIGGER_RISING_EDGE:
data->interrupt_mask = kACMP_OutputRisingInterruptEnable;
break;
case COMPARATOR_TRIGGER_FALLING_EDGE:
data->interrupt_mask = kACMP_OutputFallingInterruptEnable;
break;
case COMPARATOR_TRIGGER_BOTH_EDGES:
data->interrupt_mask = kACMP_OutputFallingInterruptEnable |
kACMP_OutputRisingInterruptEnable;
break;
}
if (data->interrupt_mask && data->callback != NULL) {
ACMP_EnableInterrupts(config->base, data->interrupt_mask);
}
return 0;
}
static int mcux_acmp_set_trigger_callback(const struct device *dev,
comparator_callback_t callback,
void *user_data)
{
const struct mcux_acmp_config *config = dev->config;
struct mcux_acmp_data *data = dev->data;
ACMP_DisableInterrupts(config->base, UINT32_MAX);
data->callback = callback;
data->user_data = user_data;
if (data->callback == NULL) {
return 0;
}
if (data->interrupt_mask) {
ACMP_EnableInterrupts(config->base, data->interrupt_mask);
}
return 0;
}
static int mcux_acmp_trigger_is_pending(const struct device *dev)
{
const struct mcux_acmp_config *config = dev->config;
struct mcux_acmp_data *data = dev->data;
uint32_t status_flags;
status_flags = ACMP_GetStatusFlags(config->base);
ACMP_ClearStatusFlags(config->base, UINT32_MAX);
if ((data->interrupt_mask & kACMP_OutputRisingInterruptEnable) &&
(status_flags & kACMP_OutputRisingEventFlag)) {
return 1;
}
if ((data->interrupt_mask & kACMP_OutputFallingInterruptEnable) &&
(status_flags & kACMP_OutputFallingEventFlag)) {
return 1;
}
return 0;
}
static const struct comparator_driver_api mcux_acmp_comp_api = {
.get_output = mcux_acmp_get_output,
.set_trigger = mcux_acmp_set_trigger,
.set_trigger_callback = mcux_acmp_set_trigger_callback,
.trigger_is_pending = mcux_acmp_trigger_is_pending,
};
static void comp_mcux_acmp_init_mode_config(const struct device *dev,
const struct comp_mcux_acmp_mode_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
acmp_config_t acmp_config;
#if COMP_MCUX_ACMP_HAS_OFFSET
acmp_config.offsetMode = (acmp_offset_mode_t)config->offset_mode;
#endif
#if COMP_MCUX_ACMP_HAS_HYSTERESIS
acmp_config.hysteresisMode = (acmp_hysteresis_mode_t)config->hysteresis_mode;
#endif
acmp_config.enableHighSpeed = config->enable_high_speed_mode;
acmp_config.enableInvertOutput = config->invert_output;
acmp_config.useUnfilteredOutput = config->use_unfiltered_output;
acmp_config.enablePinOut = config->enable_pin_output;
ACMP_Init(dev_config->base, &acmp_config);
}
int comp_mcux_acmp_set_mode_config(const struct device *dev,
const struct comp_mcux_acmp_mode_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
comp_mcux_acmp_init_mode_config(dev, config);
if (mcux_acmp_is_resumed(dev)) {
ACMP_Enable(dev_config->base, true);
}
return 0;
}
int comp_mcux_acmp_set_input_config(const struct device *dev,
const struct comp_mcux_acmp_input_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
acmp_channel_config_t acmp_channel_config;
#if COMP_MCUX_ACMP_HAS_INPSEL
acmp_channel_config.positivePortInput = (acmp_port_input_t)config->positive_port_input;
#endif
acmp_channel_config.plusMuxInput = (uint32_t)config->positive_mux_input;
#if COMP_MCUX_ACMP_HAS_INNSEL
acmp_channel_config.negativePortInput = (acmp_port_input_t)config->negative_port_input;
#endif
acmp_channel_config.minusMuxInput = (uint32_t)config->negative_mux_input;
ACMP_SetChannelConfig(dev_config->base, &acmp_channel_config);
return 0;
}
int comp_mcux_acmp_set_filter_config(const struct device *dev,
const struct comp_mcux_acmp_filter_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
acmp_filter_config_t acmp_filter_config;
if (config->enable_sample && config->filter_count == 0) {
return -EINVAL;
}
if (config->filter_count > 7) {
return -EINVAL;
}
acmp_filter_config.enableSample = config->enable_sample;
acmp_filter_config.filterCount = config->filter_count;
acmp_filter_config.filterPeriod = config->filter_period;
ACMP_SetFilterConfig(dev_config->base, &acmp_filter_config);
return 0;
}
int comp_mcux_acmp_set_dac_config(const struct device *dev,
const struct comp_mcux_acmp_dac_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
acmp_dac_config_t acmp_dac_config;
acmp_dac_config.referenceVoltageSource =
(acmp_reference_voltage_source_t)config->vref_source;
acmp_dac_config.DACValue = config->value;
#if COMP_MCUX_ACMP_HAS_DAC_OUT_ENABLE
acmp_dac_config.enableOutput = config->enable_output;
#endif
#if COMP_MCUX_ACMP_HAS_DAC_WORK_MODE
acmp_dac_config.workMode = config->enable_high_speed_mode
? kACMP_DACWorkHighSpeedMode
: kACMP_DACWorkLowSpeedMode;
#endif
ACMP_SetDACConfig(dev_config->base, &acmp_dac_config);
return 0;
}
#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
int comp_mcux_acmp_set_dm_config(const struct device *dev,
const struct comp_mcux_acmp_dm_config *config)
{
const struct mcux_acmp_config *dev_config = dev->config;
acmp_discrete_mode_config_t acmp_dm_config;
acmp_dm_config.enablePositiveChannelDiscreteMode = config->enable_positive_channel;
acmp_dm_config.enableNegativeChannelDiscreteMode = config->enable_negative_channel;
acmp_dm_config.enableResistorDivider = config->enable_resistor_divider;
acmp_dm_config.clockSource = (acmp_discrete_clock_source_t)config->clock_source;
acmp_dm_config.sampleTime = (acmp_discrete_sample_time_t)config->sample_time;
acmp_dm_config.phase1Time = (acmp_discrete_phase_time_t)config->phase1_time;
acmp_dm_config.phase2Time = (acmp_discrete_phase_time_t)config->phase2_time;
ACMP_SetDiscreteModeConfig(dev_config->base, &acmp_dm_config);
return 0;
}
#endif
#if COMP_MCUX_ACMP_HAS_WINDOW_MODE
int comp_mcux_acmp_set_window_mode(const struct device *dev, bool enable)
{
const struct mcux_acmp_config *config = dev->config;
ACMP_EnableWindowMode(config->base, enable);
return 0;
}
#endif
static int mcux_acmp_pm_callback(const struct device *dev, enum pm_device_action action)
{
const struct mcux_acmp_config *config = dev->config;
if (action == PM_DEVICE_ACTION_RESUME) {
ACMP_Enable(config->base, true);
}
#if CONFIG_PM_DEVICE
if (action == PM_DEVICE_ACTION_SUSPEND) {
ACMP_Enable(config->base, false);
}
#endif
return 0;
}
static void mcux_acmp_irq_handler(const struct device *dev)
{
const struct mcux_acmp_config *config = dev->config;
struct mcux_acmp_data *data = dev->data;
ACMP_ClearStatusFlags(config->base, UINT32_MAX);
if (data->callback == NULL) {
return;
}
data->callback(dev, data->user_data);
}
static int mcux_acmp_init(const struct device *dev)
{
const struct mcux_acmp_config *config = dev->config;
int ret;
ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (ret) {
LOG_ERR("failed to set %s", "pincfg");
return ret;
}
comp_mcux_acmp_init_mode_config(dev, &config->mode_config);
ret = comp_mcux_acmp_set_input_config(dev, &config->input_config);
if (ret) {
LOG_ERR("failed to set %s", "input config");
return ret;
}
ret = comp_mcux_acmp_set_filter_config(dev, &config->filter_config);
if (ret) {
LOG_ERR("failed to set %s", "filter config");
return ret;
}
ret = comp_mcux_acmp_set_dac_config(dev, &config->dac_config);
if (ret) {
LOG_ERR("failed to set %s", "dac config");
return ret;
}
#if COMP_MCUX_ACMP_HAS_DISCRETE_MODE
ret = comp_mcux_acmp_set_dm_config(dev, &config->dm_config);
if (ret) {
LOG_ERR("failed to set %s", "discrete mode config");
return ret;
}
#endif
#if COMP_MCUX_ACMP_HAS_WINDOW_MODE
ret = comp_mcux_acmp_set_window_mode(dev, config->enable_window_mode);
if (ret) {
LOG_ERR("failed to set %s", "window mode");
return ret;
}
#endif
ACMP_DisableInterrupts(config->base, UINT32_MAX);
config->irq_init();
return pm_device_driver_init(dev, mcux_acmp_pm_callback);
}
#define MCUX_ACMP_IRQ_HANDLER_SYM(inst) \
_CONCAT(mcux_acmp_irq_init, inst)
#define MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \
static void MCUX_ACMP_IRQ_HANDLER_SYM(inst)(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), \
DT_INST_IRQ(inst, priority), \
mcux_acmp_irq_handler, \
DEVICE_DT_INST_GET(inst), \
0); \
\
irq_enable(DT_INST_IRQN(inst)); \
}
#define MCUX_ACMP_DEVICE(inst) \
PINCTRL_DT_INST_DEFINE(inst); \
\
static struct mcux_acmp_data _CONCAT(data, inst); \
\
MCUX_ACMP_IRQ_HANDLER_DEFINE(inst) \
\
static const struct mcux_acmp_config _CONCAT(config, inst) = { \
.base = (CMP_Type *)DT_INST_REG_ADDR(inst), \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
.irq_init = MCUX_ACMP_IRQ_HANDLER_SYM(inst), \
.mode_config = MCUX_ACMP_DT_INST_MODE_CONFIG_INIT(inst), \
.input_config = MCUX_ACMP_DT_INST_INPUT_CONFIG_INIT(inst), \
.filter_config = MCUX_ACMP_DT_INST_FILTER_CONFIG_INIT(inst), \
.dac_config = MCUX_ACMP_DT_INST_DAC_CONFIG_INIT(inst), \
IF_ENABLED(COMP_MCUX_ACMP_HAS_DISCRETE_MODE, \
(.dm_config = MCUX_ACMP_DT_INST_DM_CONFIG_INIT(inst),)) \
IF_ENABLED(COMP_MCUX_ACMP_HAS_WINDOW_MODE, \
(.enable_window_mode = MCUX_ACMP_DT_INST_EN_WINDOW_MODE(inst),)) \
}; \
\
PM_DEVICE_DT_INST_DEFINE(inst, mcux_acmp_pm_callback); \
\
DEVICE_DT_INST_DEFINE(inst, \
mcux_acmp_init, \
PM_DEVICE_DT_INST_GET(inst), \
&_CONCAT(data, inst), \
&_CONCAT(config, inst), \
POST_KERNEL, \
CONFIG_COMPARATOR_INIT_PRIORITY, \
&mcux_acmp_comp_api);
DT_INST_FOREACH_STATUS_OKAY(MCUX_ACMP_DEVICE)

View file

@ -2,7 +2,39 @@
# Copyright (c) 2024 Nordic Semiconductor ASA # Copyright (c) 2024 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
description: NXP Kinetis ACMP (Analog CoMParator) description: |
NXP Kinetis ACMP (Analog CoMParator)
The following example displays the minimum node layout:
acmp0: acmp@deadbeef {
compatible = "nxp,kinetis-acmp";
reg = <0xdeadbeef 0x1000>;
interrupts = <0 0>;
clocks = <&scg KINETIS_SCG_BUS_CLK>;
status = "disabled";
};
Enabling the comparator node requires setting the minimum default
configuration of the comparator. This includes selecting the
positive and negative inputs, and routing them using pinctrl:
&pinctrl {
acmp0_default: acmp0_default {
group0 {
...
};
};
};
&acmp0 {
status = "okay";
pinctrl-0 = <&acmp0_default>;
pinctrl-names = "default";
positive-mux-input = "IN0";
negative-mux-input = "IN1";
};
compatible: "nxp,kinetis-acmp" compatible: "nxp,kinetis-acmp"

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_
#define ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_
#include <zephyr/drivers/comparator.h>
#ifdef __cplusplus
extern "C" {
#endif
enum comp_mcux_acmp_offset_mode {
COMP_MCUX_ACMP_OFFSET_MODE_LEVEL0 = 0,
COMP_MCUX_ACMP_OFFSET_MODE_LEVEL1,
};
enum comp_mcux_acmp_hysteresis_mode {
COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL0 = 0,
COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL1,
COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL2,
COMP_MCUX_ACMP_HYSTERESIS_MODE_LEVEL3,
};
struct comp_mcux_acmp_mode_config {
enum comp_mcux_acmp_offset_mode offset_mode;
enum comp_mcux_acmp_hysteresis_mode hysteresis_mode;
bool enable_high_speed_mode;
bool invert_output;
bool use_unfiltered_output;
bool enable_pin_output;
};
enum comp_mcux_acmp_mux_input {
COMP_MCUX_ACMP_MUX_INPUT_IN0 = 0,
COMP_MCUX_ACMP_MUX_INPUT_IN1,
COMP_MCUX_ACMP_MUX_INPUT_IN2,
COMP_MCUX_ACMP_MUX_INPUT_IN3,
COMP_MCUX_ACMP_MUX_INPUT_IN4,
COMP_MCUX_ACMP_MUX_INPUT_IN5,
COMP_MCUX_ACMP_MUX_INPUT_IN6,
COMP_MCUX_ACMP_MUX_INPUT_IN7,
};
enum comp_mcux_acmp_port_input {
COMP_MCUX_ACMP_PORT_INPUT_DAC = 0,
COMP_MCUX_ACMP_PORT_INPUT_MUX,
};
struct comp_mcux_acmp_input_config {
enum comp_mcux_acmp_mux_input positive_mux_input;
enum comp_mcux_acmp_mux_input negative_mux_input;
enum comp_mcux_acmp_port_input positive_port_input;
enum comp_mcux_acmp_port_input negative_port_input;
};
struct comp_mcux_acmp_filter_config {
bool enable_sample;
uint8_t filter_count;
uint8_t filter_period;
};
enum comp_mcux_acmp_dac_vref_source {
COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN1 = 0,
COMP_MCUX_ACMP_DAC_VREF_SOURCE_VIN2,
};
struct comp_mcux_acmp_dac_config {
enum comp_mcux_acmp_dac_vref_source vref_source;
uint8_t value;
bool enable_output;
bool enable_high_speed_mode;
};
enum comp_mcux_acmp_dm_clock {
COMP_MCUX_ACMP_DM_CLOCK_SLOW = 0,
COMP_MCUX_ACMP_DM_CLOCK_FAST,
};
enum comp_mcux_acmp_dm_sample_time {
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T1 = 0,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T2,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T4,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T8,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T16,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T32,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T64,
COMP_MCUX_ACMP_DM_SAMPLE_TIME_T256,
};
enum comp_mcux_acmp_dm_phase_time {
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT0 = 0,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT1,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT2,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT3,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT4,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT5,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT6,
COMP_MCUX_ACMP_DM_PHASE_TIME_ALT7,
};
struct comp_mcux_acmp_dm_config {
bool enable_positive_channel;
bool enable_negative_channel;
bool enable_resistor_divider;
enum comp_mcux_acmp_dm_clock clock_source;
enum comp_mcux_acmp_dm_sample_time sample_time;
enum comp_mcux_acmp_dm_phase_time phase1_time;
enum comp_mcux_acmp_dm_phase_time phase2_time;
};
int comp_mcux_acmp_set_mode_config(const struct device *dev,
const struct comp_mcux_acmp_mode_config *config);
int comp_mcux_acmp_set_input_config(const struct device *dev,
const struct comp_mcux_acmp_input_config *config);
int comp_mcux_acmp_set_filter_config(const struct device *dev,
const struct comp_mcux_acmp_filter_config *config);
int comp_mcux_acmp_set_dac_config(const struct device *dev,
const struct comp_mcux_acmp_dac_config *config);
int comp_mcux_acmp_set_dm_config(const struct device *dev,
const struct comp_mcux_acmp_dm_config *config);
int comp_mcux_acmp_set_window_mode(const struct device *dev, bool enable);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_COMP_MCUX_ACMP_H_ */