drivers: comparator: add NXP ACOMP driver support
Add NXP ACOMP comparator driver support. Signed-off-by: Zhaoxiang Jin <Zhaoxiang.Jin_1@nxp.com>
This commit is contained in:
committed by
Anas Nashif
parent
c65ffc7737
commit
bd65e115fa
@@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_COMPARATOR_MCHP_AC_G1 comparator_mchp_ac_g1.
|
||||
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_LPCOMP comparator_nrf_lpcomp.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NXP_ACOMP comparator_nxp_acomp.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NXP_CMP comparator_nxp_cmp.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_NXP_HSCMP comparator_nxp_hscmp.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_RENESAS_RA comparator_renesas_ra.c)
|
||||
|
||||
@@ -25,6 +25,7 @@ rsource "Kconfig.mchp"
|
||||
rsource "Kconfig.mcux_acmp"
|
||||
rsource "Kconfig.nrf_comp"
|
||||
rsource "Kconfig.nrf_lpcomp"
|
||||
rsource "Kconfig.nxp_acomp"
|
||||
rsource "Kconfig.nxp_cmp"
|
||||
rsource "Kconfig.nxp_hscmp"
|
||||
rsource "Kconfig.renesas_ra"
|
||||
|
||||
7
drivers/comparator/Kconfig.nxp_acomp
Normal file
7
drivers/comparator/Kconfig.nxp_acomp
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright 2025 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config COMPARATOR_NXP_ACOMP
|
||||
bool "NXP analog comparator driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_ACOMP_ENABLED
|
||||
284
drivers/comparator/comparator_nxp_acomp.c
Normal file
284
drivers/comparator/comparator_nxp_acomp.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/drivers/comparator.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
|
||||
LOG_MODULE_REGISTER(nxp_acomp, CONFIG_COMPARATOR_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT nxp_acomp
|
||||
|
||||
#define ACOMP_NEG_INPUT_VIO_0P25 12U
|
||||
|
||||
struct nxp_acomp_config {
|
||||
ACOMP_Type *base;
|
||||
bool async_output;
|
||||
bool invert_output;
|
||||
bool enable_pin_out;
|
||||
bool inactive_value_high;
|
||||
uint8_t positive_input;
|
||||
uint8_t negative_input;
|
||||
uint8_t positive_hysteresis;
|
||||
uint8_t negative_hysteresis;
|
||||
uint8_t warmup_time_us;
|
||||
uint8_t response_mode;
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
};
|
||||
|
||||
struct nxp_acomp_data {
|
||||
comparator_callback_t callback;
|
||||
void *user_data;
|
||||
uint32_t interrupt_mask;
|
||||
};
|
||||
|
||||
static int nxp_acomp_get_output(const struct device *dev)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
uint32_t status = config->base->STATUS0;
|
||||
|
||||
return ((bool)(status & ACOMP_STATUS0_OUT_MASK)) ? 1U : 0U;
|
||||
}
|
||||
|
||||
static int nxp_acomp_set_trigger(const struct device *dev, enum comparator_trigger trigger)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
struct nxp_acomp_data *data = dev->data;
|
||||
uint32_t ctrl = config->base->CTRL0;
|
||||
|
||||
ctrl &= ~(ACOMP_CTRL0_INT_ACT_HI_MASK | ACOMP_CTRL0_EDGE_LEVL_SEL_MASK);
|
||||
|
||||
switch (trigger) {
|
||||
case COMPARATOR_TRIGGER_NONE:
|
||||
data->interrupt_mask = 0U;
|
||||
break;
|
||||
case COMPARATOR_TRIGGER_RISING_EDGE:
|
||||
ctrl |= (ACOMP_CTRL0_EDGE_LEVL_SEL(1U) | ACOMP_CTRL0_INT_ACT_HI(1U));
|
||||
data->interrupt_mask = ACOMP_IMR0_OUTA_INT_MASK_MASK | ACOMP_IMR0_OUT_INT_MASK_MASK;
|
||||
break;
|
||||
case COMPARATOR_TRIGGER_FALLING_EDGE:
|
||||
ctrl |= (ACOMP_CTRL0_EDGE_LEVL_SEL(1U) | ACOMP_CTRL0_INT_ACT_HI(0U));
|
||||
data->interrupt_mask = ACOMP_IMR0_OUTA_INT_MASK_MASK | ACOMP_IMR0_OUT_INT_MASK_MASK;
|
||||
break;
|
||||
case COMPARATOR_TRIGGER_BOTH_EDGES:
|
||||
LOG_ERR("Trigger type not support.");
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
LOG_ERR("Trigger type invalid.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->base->CTRL0 = ctrl;
|
||||
|
||||
/* Clear latched status flags before enabling interrupts. */
|
||||
config->base->ICR0 = ACOMP_ICR0_OUT_INT_CLR_MASK | ACOMP_ICR0_OUTA_INT_CLR_MASK;
|
||||
|
||||
if ((data->interrupt_mask != 0U) && (data->callback != NULL)) {
|
||||
config->base->IMR0 &= ~data->interrupt_mask;
|
||||
} else {
|
||||
config->base->IMR0 |= data->interrupt_mask;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nxp_acomp_trigger_is_pending(const struct device *dev)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
struct nxp_acomp_data *data = dev->data;
|
||||
|
||||
/* Read raw status first. We'll clear the hardware flag when we
|
||||
* consume the pending event to avoid leaving sticky RAW bits set
|
||||
* and producing repeated reports.
|
||||
*/
|
||||
bool interrupts_enabled = (data->interrupt_mask & (ACOMP_IMR0_OUTA_INT_MASK_MASK |
|
||||
ACOMP_IMR0_OUT_INT_MASK_MASK)) != 0U;
|
||||
bool interrupt_flags = ((config->base->IRSR0 & (ACOMP_IRSR0_OUTA_INT_RAW_MASK |
|
||||
ACOMP_IRSR0_OUT_INT_RAW_MASK))) != 0U;
|
||||
int pending = (interrupts_enabled && interrupt_flags) ? 1 : 0;
|
||||
|
||||
/* Always clear raw flags so we don't re-report the same edge. */
|
||||
config->base->ICR0 = ACOMP_ICR0_OUTA_INT_CLR_MASK | ACOMP_ICR0_OUT_INT_CLR_MASK;
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static int nxp_acomp_set_trigger_callback(const struct device *dev,
|
||||
comparator_callback_t callback, void *user_data)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
struct nxp_acomp_data *data = dev->data;
|
||||
|
||||
config->base->CTRL0 &= ~ACOMP_CTRL0_EN_MASK;
|
||||
|
||||
data->callback = callback;
|
||||
data->user_data = user_data;
|
||||
|
||||
/* Clear any pending flags when (re)arming the callback. */
|
||||
config->base->ICR0 = ACOMP_ICR0_OUTA_INT_CLR_MASK | ACOMP_ICR0_OUT_INT_CLR_MASK;
|
||||
|
||||
if ((data->callback != NULL) && (data->interrupt_mask != 0U)) {
|
||||
config->base->IMR0 &= ~data->interrupt_mask;
|
||||
} else {
|
||||
config->base->IMR0 |= data->interrupt_mask;
|
||||
}
|
||||
|
||||
config->base->CTRL0 |= ACOMP_CTRL0_EN_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nxp_acomp_irq_handler(const struct device *dev)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
struct nxp_acomp_data *data = dev->data;
|
||||
uint32_t status = config->base->IRSR0;
|
||||
uint32_t raw_mask = ACOMP_IRSR0_OUTA_INT_RAW_MASK | ACOMP_IRSR0_OUT_INT_RAW_MASK;
|
||||
|
||||
/* Clear interrupt status flags */
|
||||
config->base->ICR0 = ACOMP_ICR0_OUTA_INT_CLR_MASK | ACOMP_ICR0_OUT_INT_CLR_MASK;
|
||||
|
||||
if ((status & raw_mask) == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->callback == NULL) {
|
||||
LOG_WRN("No callback can be executed.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* RAW bits stay asserted; mask both sources after one hit to avoid ISR storms. */
|
||||
config->base->IMR0 |= ACOMP_IMR0_OUTA_INT_MASK_MASK | ACOMP_IMR0_OUT_INT_MASK_MASK;
|
||||
|
||||
data->callback(dev, data->user_data);
|
||||
}
|
||||
|
||||
#if CONFIG_PM_DEVICE
|
||||
static int nxp_acomp_pm_callback(const struct device *dev,
|
||||
enum pm_device_action action)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
config->base->CTRL0 |= ACOMP_CTRL0_EN_MASK;
|
||||
break;
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
config->base->CTRL0 &= ~ACOMP_CTRL0_EN_MASK;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int nxp_acomp_init(const struct device *dev)
|
||||
{
|
||||
const struct nxp_acomp_config *config = dev->config;
|
||||
uint32_t ctrl = config->base->CTRL0;
|
||||
uint32_t route = config->base->ROUTE0;
|
||||
|
||||
/* Do software first. */
|
||||
config->base->RST0 |= ACOMP_RST0_SOFT_RST_MASK;
|
||||
config->base->RST0 &= ~ACOMP_RST0_SOFT_RST_MASK;
|
||||
|
||||
ctrl &= ~(ACOMP_CTRL0_WARMTIME_MASK | ACOMP_CTRL0_BIAS_PROG_MASK |
|
||||
ACOMP_CTRL0_INACT_VAL_MASK | ACOMP_CTRL0_GPIOINV_MASK |
|
||||
ACOMP_CTRL0_HYST_SELP_MASK | ACOMP_CTRL0_HYST_SELN_MASK |
|
||||
ACOMP_CTRL0_POS_SEL_MASK | ACOMP_CTRL0_NEG_SEL_MASK |
|
||||
ACOMP_CTRL0_LEVEL_SEL_MASK | ACOMP_CTRL0_MUXEN_MASK);
|
||||
|
||||
ctrl |= ACOMP_CTRL0_WARMTIME(config->warmup_time_us) |
|
||||
ACOMP_CTRL0_BIAS_PROG(config->response_mode) |
|
||||
ACOMP_CTRL0_INACT_VAL(config->inactive_value_high) |
|
||||
ACOMP_CTRL0_GPIOINV(config->invert_output) |
|
||||
ACOMP_CTRL0_HYST_SELP(config->positive_hysteresis) |
|
||||
ACOMP_CTRL0_HYST_SELN(config->negative_hysteresis) |
|
||||
ACOMP_CTRL0_POS_SEL(config->positive_input) |
|
||||
ACOMP_CTRL0_NEG_SEL(config->negative_input) |
|
||||
ACOMP_CTRL0_MUXEN(1U);
|
||||
|
||||
/* VIO-based negative inputs use LEVEL_SEL[1:0] to pick 0.25/0.5/0.75/1.0. */
|
||||
if (config->negative_input >= ACOMP_NEG_INPUT_VIO_0P25) {
|
||||
uint32_t level = (config->negative_input - ACOMP_NEG_INPUT_VIO_0P25) & 0x3U;
|
||||
|
||||
ctrl |= ACOMP_CTRL0_LEVEL_SEL(level);
|
||||
}
|
||||
config->base->CTRL0 = ctrl;
|
||||
|
||||
route &= ~(ACOMP_ROUTE0_OUTSEL_MASK | ACOMP_ROUTE0_PE_MASK);
|
||||
route |= ACOMP_ROUTE0_OUTSEL(config->async_output) |
|
||||
ACOMP_ROUTE0_PE(config->enable_pin_out);
|
||||
config->base->ROUTE0 = route;
|
||||
|
||||
/* Disable interrupt, clear status. */
|
||||
config->base->IMR0 |= ACOMP_IMR0_OUT_INT_MASK_MASK | ACOMP_IMR0_OUTA_INT_MASK_MASK;
|
||||
config->base->ICR0 = ACOMP_ICR0_OUT_INT_CLR_MASK | ACOMP_ICR0_OUTA_INT_CLR_MASK;
|
||||
|
||||
config->base->CTRL0 |= ACOMP_CTRL0_EN_MASK;
|
||||
|
||||
config->irq_config_func(dev);
|
||||
|
||||
#if CONFIG_PM_DEVICE
|
||||
return pm_device_driver_init(dev, nxp_acomp_pm_callback);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static DEVICE_API(comparator, nxp_acomp_api) = {
|
||||
.get_output = nxp_acomp_get_output,
|
||||
.set_trigger = nxp_acomp_set_trigger,
|
||||
.set_trigger_callback = nxp_acomp_set_trigger_callback,
|
||||
.trigger_is_pending = nxp_acomp_trigger_is_pending,
|
||||
};
|
||||
|
||||
#if CONFIG_PM_DEVICE
|
||||
#define ACOMP_PM_DEVICE_DEFINE PM_DEVICE_DT_INST_DEFINE(inst, nxp_acomp_pm_callback);
|
||||
#define ACOMP_PM_DEVICE_GET PM_DEVICE_DT_INST_GET(inst)
|
||||
#else
|
||||
#define ACOMP_PM_DEVICE_DEFINE
|
||||
#define ACOMP_PM_DEVICE_GET NULL
|
||||
#endif
|
||||
|
||||
#define NXP_ACOMP_INIT(inst) \
|
||||
ACOMP_PM_DEVICE_DEFINE \
|
||||
\
|
||||
static void nxp_acomp_irq_config_##inst(const struct device *dev) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
|
||||
nxp_acomp_irq_handler, DEVICE_DT_INST_GET(inst), 0); \
|
||||
irq_enable(DT_INST_IRQN(inst)); \
|
||||
} \
|
||||
\
|
||||
static struct nxp_acomp_data nxp_acomp_data_##inst; \
|
||||
\
|
||||
static const struct nxp_acomp_config nxp_acomp_config_##inst = { \
|
||||
.base = (ACOMP_Type *)DT_INST_REG_ADDR(inst), \
|
||||
.positive_input = DT_ENUM_IDX(DT_DRV_INST(inst), positive_input), \
|
||||
.negative_input = DT_ENUM_IDX(DT_DRV_INST(inst), negative_input), \
|
||||
.positive_hysteresis = DT_INST_PROP_OR(inst, positive_hysteresis_mv / 10, 0), \
|
||||
.negative_hysteresis = DT_INST_PROP_OR(inst, negative_hysteresis_mv / 10, 0), \
|
||||
.warmup_time_us = DT_INST_PROP_OR(inst, warmup_time_us, 0), \
|
||||
.response_mode = DT_ENUM_IDX_OR(DT_DRV_INST(inst), response_mode, 0), \
|
||||
.inactive_value_high = DT_INST_PROP_OR(inst, inactive_value_high, 0), \
|
||||
.invert_output = DT_INST_PROP_OR(inst, invert_output, 0), \
|
||||
.enable_pin_out = DT_INST_PROP_OR(inst, enable_pin_out, 0), \
|
||||
.async_output = DT_INST_PROP_OR(inst, async_output, 0), \
|
||||
.irq_config_func = nxp_acomp_irq_config_##inst, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, nxp_acomp_init, NULL, \
|
||||
&nxp_acomp_data_##inst, &nxp_acomp_config_##inst, POST_KERNEL, \
|
||||
CONFIG_COMPARATOR_INIT_PRIORITY, &nxp_acomp_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(NXP_ACOMP_INIT)
|
||||
102
dts/bindings/comparator/nxp,acomp.yaml
Normal file
102
dts/bindings/comparator/nxp,acomp.yaml
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright 2025 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
NXP Analog Comparator (ACOMP)
|
||||
|
||||
compatible: "nxp,acomp"
|
||||
|
||||
include:
|
||||
- base.yaml
|
||||
- pinctrl-device.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
positive-input:
|
||||
type: string
|
||||
enum:
|
||||
- ch0
|
||||
- ch1
|
||||
- ch2
|
||||
- ch3
|
||||
- ch4
|
||||
- ch5
|
||||
- ch6
|
||||
- ch7
|
||||
- daca
|
||||
- dacb
|
||||
required: true
|
||||
description: |
|
||||
Positive input selection.
|
||||
|
||||
negative-input:
|
||||
type: string
|
||||
enum:
|
||||
- ch0
|
||||
- ch1
|
||||
- ch2
|
||||
- ch3
|
||||
- ch4
|
||||
- ch5
|
||||
- ch6
|
||||
- ch7
|
||||
- daca
|
||||
- dacb
|
||||
- vref1p2
|
||||
- vssa
|
||||
- vio-0p25
|
||||
- vio-0p50
|
||||
- vio-0p75
|
||||
- vio-1p00
|
||||
required: true
|
||||
description: |
|
||||
Negative input selection.
|
||||
|
||||
positive-hysteresis-mv:
|
||||
type: int
|
||||
enum: [0, 10, 20, 30, 40, 50, 60, 70]
|
||||
description: |
|
||||
Positive input hysteresis in millivolts.
|
||||
|
||||
negative-hysteresis-mv:
|
||||
type: int
|
||||
enum: [0, 10, 20, 30, 40, 50, 60, 70]
|
||||
description: |
|
||||
Negative input hysteresis in millivolts.
|
||||
|
||||
warmup-time-us:
|
||||
type: int
|
||||
enum: [1, 2, 4, 8]
|
||||
description: |
|
||||
Warm-up time before the comparator becomes active.
|
||||
|
||||
response-mode:
|
||||
type: string
|
||||
enum: ["fast", "medium", "slow"]
|
||||
description: |
|
||||
Bias/response speed selection.
|
||||
|
||||
inactive-value-high:
|
||||
type: boolean
|
||||
description: |
|
||||
Set comparator output high when disabled.
|
||||
|
||||
enable-pin-out:
|
||||
type: boolean
|
||||
description: |
|
||||
Enable routing comparator output to its GPIO pin.
|
||||
|
||||
invert-output:
|
||||
type: boolean
|
||||
description: |
|
||||
Invert the comparator output routed to GPIO.
|
||||
|
||||
async-output:
|
||||
type: boolean
|
||||
description: |
|
||||
Use asynchronous comparator output for pin routing and interrupts.
|
||||
Reference in New Issue
Block a user