blob: 31e21f5ef9f8117f6786d4b7a8aa8d0451220a16 [file] [log] [blame]
/*
* Copyright (c) 2025 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT adi_max32_wut
#include <zephyr/drivers/counter.h>
#include <zephyr/dt-bindings/clock/adi_max32_clock.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <wut.h>
#include <wrap_max32_lp.h>
#include <wrap_max32_sys.h>
LOG_MODULE_REGISTER(counter_max32_wut, CONFIG_COUNTER_LOG_LEVEL);
#define MAX32_WUT_COUNTER_FREQ 32768
struct max32_wut_alarm_data {
counter_alarm_callback_t callback;
void *user_data;
};
struct max32_wut_data {
struct max32_wut_alarm_data alarm;
uint32_t guard_period;
};
struct max32_wut_config {
struct counter_config_info info;
mxc_wut_regs_t *regs;
int clock_source;
int prescaler;
void (*irq_config)(const struct device *dev);
uint32_t irq_number;
bool wakeup_source;
};
static int counter_max32_wut_start(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
MXC_WUT_Enable(cfg->regs);
return 0;
}
static int counter_max32_wut_stop(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
MXC_WUT_Disable(cfg->regs);
return 0;
}
static int counter_max32_wut_get_value(const struct device *dev, uint32_t *ticks)
{
const struct max32_wut_config *cfg = dev->config;
*ticks = MXC_WUT_GetCount(cfg->regs);
return 0;
}
static int counter_max32_wut_set_top_value(const struct device *dev,
const struct counter_top_cfg *top_cfg)
{
ARG_UNUSED(dev);
ARG_UNUSED(top_cfg);
return -ENOTSUP;
}
static uint32_t counter_max32_wut_get_pending_int(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
return MXC_WUT_GetFlags(cfg->regs);
}
static uint32_t counter_max32_wut_get_top_value(const struct device *dev)
{
ARG_UNUSED(dev);
return UINT32_MAX;
}
static uint32_t counter_max32_wut_get_freq(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
return cfg->info.freq;
}
static uint32_t counter_max32_wut_get_guard_period(const struct device *dev, uint32_t flags)
{
struct max32_wut_data *data = dev->data;
ARG_UNUSED(flags);
return data->guard_period;
}
static int counter_max32_wut_set_guard_period(const struct device *dev, uint32_t ticks,
uint32_t flags)
{
struct max32_wut_data *data = dev->data;
ARG_UNUSED(flags);
if (ticks > counter_max32_wut_get_top_value(dev)) {
return -EINVAL;
}
data->guard_period = ticks;
return 0;
}
static int counter_max32_wut_set_alarm(const struct device *dev, uint8_t chan,
const struct counter_alarm_cfg *alarm_cfg)
{
const struct max32_wut_config *cfg = dev->config;
struct max32_wut_data *data = dev->data;
uint32_t now_ticks, top_ticks;
uint64_t abs_ticks, min_abs_ticks;
bool irq_on_late = false;
bool absolute = alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE;
counter_max32_wut_get_value(dev, &now_ticks);
top_ticks = counter_max32_wut_get_top_value(dev);
if (alarm_cfg->ticks > top_ticks) {
return -EINVAL;
}
if (data->alarm.callback != NULL) {
return -EBUSY;
}
MXC_WUT_ClearFlags(cfg->regs);
data->alarm.callback = alarm_cfg->callback;
data->alarm.user_data = alarm_cfg->user_data;
if (absolute) {
abs_ticks = alarm_cfg->ticks;
} else {
abs_ticks = (uint64_t)now_ticks + alarm_cfg->ticks;
}
min_abs_ticks = (uint64_t)now_ticks + data->guard_period;
if ((!absolute && (abs_ticks < now_ticks)) || (abs_ticks > min_abs_ticks)) {
MXC_WUT_SetCompare(cfg->regs, abs_ticks & top_ticks);
MXC_WUT_Enable(cfg->regs);
return 0;
}
irq_on_late = alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
if (irq_on_late || !absolute) {
NVIC_SetPendingIRQ(cfg->irq_number);
} else {
data->alarm.callback = NULL;
data->alarm.user_data = NULL;
}
return -ETIME;
}
static int counter_max32_wut_cancel_alarm(const struct device *dev, uint8_t chan)
{
ARG_UNUSED(chan);
struct max32_wut_data *data = dev->data;
counter_max32_wut_stop(dev);
data->alarm.callback = NULL;
data->alarm.user_data = NULL;
return 0;
}
static void counter_max32_wut_isr(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
struct max32_wut_data *data = dev->data;
if (data->alarm.callback) {
counter_alarm_callback_t cb = data->alarm.callback;
data->alarm.callback = NULL;
cb(dev, 0, MXC_WUT_GetCount(cfg->regs), data->alarm.user_data);
}
MXC_WUT_ClearFlags(cfg->regs);
}
static void counter_max32_wut_hw_init(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
Wrap_MXC_SYS_Select32KClockSource(cfg->clock_source);
cfg->irq_config(dev);
if (cfg->wakeup_source) {
MXC_LP_EnableWUTAlarmWakeup();
}
}
static int counter_max32_wut_init(const struct device *dev)
{
const struct max32_wut_config *cfg = dev->config;
uint8_t prescaler_lo, prescaler_hi;
mxc_wut_pres_t pres;
mxc_wut_cfg_t wut_cfg;
counter_max32_wut_hw_init(dev);
prescaler_lo = FIELD_GET(GENMASK(2, 0), LOG2(cfg->prescaler));
prescaler_hi = FIELD_GET(BIT(3), LOG2(cfg->prescaler));
pres = (prescaler_hi << MXC_F_WUT_CTRL_PRES3_POS) |
(prescaler_lo << MXC_F_WUT_CTRL_PRES_POS);
MXC_WUT_Init(cfg->regs, pres);
wut_cfg.mode = MXC_WUT_MODE_COMPARE;
wut_cfg.cmp_cnt = cfg->info.max_top_value;
MXC_WUT_Config(cfg->regs, &wut_cfg);
MXC_WUT_SetCount(cfg->regs, 0);
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int counter_max32_wut_pm_action(const struct device *dev, enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_RESUME:
counter_max32_wut_hw_init(dev);
break;
case PM_DEVICE_ACTION_SUSPEND:
break;
default:
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_PM_DEVICE */
static DEVICE_API(counter, counter_max32_wut_driver_api) = {
.start = counter_max32_wut_start,
.stop = counter_max32_wut_stop,
.get_value = counter_max32_wut_get_value,
.set_top_value = counter_max32_wut_set_top_value,
.get_pending_int = counter_max32_wut_get_pending_int,
.get_top_value = counter_max32_wut_get_top_value,
.get_freq = counter_max32_wut_get_freq,
.set_alarm = counter_max32_wut_set_alarm,
.cancel_alarm = counter_max32_wut_cancel_alarm,
.get_guard_period = counter_max32_wut_get_guard_period,
.set_guard_period = counter_max32_wut_set_guard_period,
};
#define TIMER(_num) DT_INST_PARENT(_num)
#define MAX32_TIM(idx) ((mxc_wut_regs_t *)DT_REG_ADDR(TIMER(idx)))
#define COUNTER_MAX32_WUT_DEFINE(_num) \
static void max32_wut_irq_init_##_num(const struct device *dev) \
{ \
IRQ_CONNECT(DT_IRQN(TIMER(_num)), DT_IRQ(TIMER(_num), priority), \
counter_max32_wut_isr, DEVICE_DT_INST_GET(_num), 0); \
irq_enable(DT_IRQN(TIMER(_num))); \
}; \
static const struct max32_wut_config max32_wut_config_##_num = { \
.info = \
{ \
.max_top_value = UINT32_MAX, \
.freq = MAX32_WUT_COUNTER_FREQ / DT_PROP(TIMER(_num), prescaler), \
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
.channels = 1, \
}, \
.regs = (mxc_wut_regs_t *)DT_REG_ADDR(TIMER(_num)), \
.clock_source = \
DT_PROP_OR(TIMER(_num), clock_source, ADI_MAX32_PRPH_CLK_SRC_ERTCO), \
.prescaler = DT_PROP(TIMER(_num), prescaler), \
.irq_config = max32_wut_irq_init_##_num, \
.irq_number = DT_IRQN(TIMER(_num)), \
.wakeup_source = DT_PROP(TIMER(_num), wakeup_source), \
}; \
static struct max32_wut_data max32_wut_data##_num; \
PM_DEVICE_DT_INST_DEFINE(_num, counter_max32_wut_pm_action); \
DEVICE_DT_INST_DEFINE(_num, &counter_max32_wut_init, PM_DEVICE_DT_INST_GET(_num), \
&max32_wut_data##_num, &max32_wut_config_##_num, PRE_KERNEL_1, \
CONFIG_COUNTER_INIT_PRIORITY, &counter_max32_wut_driver_api);
DT_INST_FOREACH_STATUS_OKAY(COUNTER_MAX32_WUT_DEFINE)