Skip to content

Commit

Permalink
drivers: comparator: implement the silabs_acmp compatible driver
Browse files Browse the repository at this point in the history
This implements the comparator driver for silabs acmp peripherals
using the silabs,acmp compatible binding.

Signed-off-by: Christian Galante <[email protected]>
  • Loading branch information
silabs-chgalant committed Jan 17, 2025
1 parent 7fd0c05 commit 5925121
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/comparator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/comparator.h)
zephyr_library()

zephyr_library_sources_ifdef(CONFIG_USERSPACE comparator_handlers.c)
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_SILABS_ACMP comparator_silabs_acmp.c)
zephyr_library_sources_ifdef(CONFIG_COMPARATOR_FAKE_COMP comparator_fake_comp.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)
Expand Down
1 change: 1 addition & 0 deletions drivers/comparator/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ config COMPARATOR_INIT_PRIORITY
Comparator device driver initialization priority.

rsource "Kconfig.fake_comp"
rsource "Kconfig.silabs_acmp"
rsource "Kconfig.mcux_acmp"
rsource "Kconfig.nrf_comp"
rsource "Kconfig.nrf_lpcomp"
Expand Down
9 changes: 9 additions & 0 deletions drivers/comparator/Kconfig.silabs_acmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
config COMPARATOR_SILABS_ACMP
bool "Silabs ACMP comparator driver"
default y
depends on DT_HAS_SILABS_ACMP_ENABLED
select SOC_SILABS_ACMP
help
Enable the comparator driver for the Analog Comparator hardware block
present on Silicon Labs devices. This block is commonly used to
monitor the power supply.
184 changes: 184 additions & 0 deletions drivers/comparator/comparator_silabs_acmp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2024 Silicon Labs
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <em_device.h>
#include <em_cmu.h>
#include <em_acmp.h>
#include <zephyr/device.h>
#include <zephyr/drivers/comparator.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/clock_control_silabs.h>
#include <zephyr/irq.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(silabs_acmp, CONFIG_COMPARATOR_LOG_LEVEL);

#define DT_DRV_COMPAT silabs_acmp

struct acmp_config {
ACMP_TypeDef *base;
const struct device *clock_dev;
const struct silabs_clock_control_cmu_config clock_cfg;
void (*irq_init)(void);
ACMP_Init_TypeDef init;
int input_negative;
int input_positive;
};

struct acmp_data {
uint32_t interrupt_mask;
comparator_callback_t callback;
void *user_data;
};

static int acmp_init(const struct device *dev)
{
int err;
const struct acmp_config *config = dev->config;

/* Enable ACMP Clock */
err = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_cfg);
if (err < 0) {
return err;
}

/* Initialize the ACMP */
ACMP_Init(config->base, &config->init);

/* Configure the ACMP Input Channels */
ACMP_ChannelSet(config->base, config->input_negative, config->input_positive);

/* Initialize the irq handler */
config->irq_init();

return 0;
}

static int acmp_get_output(const struct device *dev)
{
const struct acmp_config *config = dev->config;

return config->base->STATUS & ACMP_STATUS_ACMPOUT;
}

static int acmp_set_trigger(const struct device *dev, enum comparator_trigger trigger)
{
const struct acmp_config *config = dev->config;
struct acmp_data *data = dev->data;

/* Disable ACMP trigger interrupts */
ACMP_IntDisable(config->base, ACMP_IEN_RISE | ACMP_IEN_FALL);

switch (trigger) {
case COMPARATOR_TRIGGER_BOTH_EDGES:
ACMP_IntEnable(config->base, ACMP_IEN_RISE | ACMP_IEN_FALL);
data->interrupt_mask = ACMP_IEN_RISE | ACMP_IEN_FALL;
break;
case COMPARATOR_TRIGGER_RISING_EDGE:
ACMP_IntEnable(config->base, ACMP_IEN_RISE);
data->interrupt_mask = ACMP_IEN_RISE;
break;
case COMPARATOR_TRIGGER_FALLING_EDGE:
ACMP_IntEnable(config->base, ACMP_IEN_FALL);
data->interrupt_mask = ACMP_IEN_FALL;
break;
case COMPARATOR_TRIGGER_NONE:
data->interrupt_mask = 0;
break;
default:
return -EINVAL;
}

return 0;
}

static int acmp_set_trigger_callback(const struct device *dev, comparator_callback_t callback,
void *user_data)
{
const struct acmp_config *config = dev->config;
struct acmp_data *data = dev->data;

/* Disable ACMP trigger interrupts while setting callback */
ACMP_IntDisable(config->base, ACMP_IEN_RISE | ACMP_IEN_FALL);

data->callback = callback;
data->user_data = user_data;

if (data->callback == NULL) {
return 0;
}

/* Re-enable currently set ACMP trigger interrupts */
if (data->interrupt_mask) {
ACMP_IntEnable(config->base, data->interrupt_mask);
}

return 0;
}

static int acmp_trigger_is_pending(const struct device *dev)
{
const struct acmp_config *config = dev->config;

if (ACMP_IntGetEnabled(config->base)) {
return 1;
}

return 0;
}

static void acmp_irq_handler(const struct device *dev)
{
const struct acmp_config *config = dev->config;
struct acmp_data *data = dev->data;

ACMP_IntClear(config->base, ACMP_IF_RISE | ACMP_IF_FALL);

if (data->callback == NULL) {
return;
}

data->callback(dev, data->user_data);
}

static DEVICE_API(comparator, acmp_api) = {
.get_output = acmp_get_output,
.set_trigger = acmp_set_trigger,
.set_trigger_callback = acmp_set_trigger_callback,
.trigger_is_pending = acmp_trigger_is_pending,
};

#define ACMP_DEVICE(inst) \
static void acmp_irq_init##inst(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), acmp_irq_handler, \
DEVICE_DT_INST_GET(inst), 0); \
\
irq_enable(DT_INST_IRQN(inst)); \
} \
\
static struct acmp_data acmp_data##inst; \
\
static const struct acmp_config acmp_config##inst = { \
.base = (ACMP_TypeDef *)DT_INST_REG_ADDR(inst), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
.clock_cfg = SILABS_DT_INST_CLOCK_CFG(inst), \
.irq_init = acmp_irq_init##inst, \
.init.biasProg = DT_INST_PROP(inst, bias), \
.init.inputRange = DT_INST_ENUM_IDX(inst, input_range), \
.init.accuracy = DT_INST_ENUM_IDX(inst, accuracy_mode), \
.init.hysteresisLevel = DT_INST_ENUM_IDX(inst, hysteresis_mode), \
.init.inactiveValue = false, \
.init.vrefDiv = DT_INST_PROP(inst, vref_divider), \
.init.enable = true, \
.input_negative = DT_INST_PROP(inst, input_negative), \
.input_positive = DT_INST_PROP(inst, input_positive), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, acmp_init, NULL, &acmp_data##inst, &acmp_config##inst, \
POST_KERNEL, CONFIG_COMPARATOR_INIT_PRIORITY, &acmp_api);

DT_INST_FOREACH_STATUS_OKAY(ACMP_DEVICE)

0 comments on commit 5925121

Please sign in to comment.