Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/counter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_RENESAS_RZ_GTM counter_renesas_
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912_SLWTMR counter_realtek_rts5912_slwtmr.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_NEORV32_GPTMR counter_neorv32_gptmr.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_WUT_MAX32 counter_max32_wut.c)
2 changes: 2 additions & 0 deletions drivers/counter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,6 @@ source "drivers/counter/Kconfig.rts5912"

source "drivers/counter/Kconfig.neorv32"

source "drivers/counter/Kconfig.max32_wut"

endif # COUNTER
9 changes: 9 additions & 0 deletions drivers/counter/Kconfig.max32_wut
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2025 Analog Devices, Inc.
# SPDX-License-Identifier: Apache-2.0

config COUNTER_WUT_MAX32
bool "MAX32xxx wake-up timer driver"
default y
depends on DT_HAS_ADI_MAX32_WUT_ENABLED
help
Enable the wake-up timer driver for MAX32 MCUs.
304 changes: 304 additions & 0 deletions drivers/counter/counter_max32_wut.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
/*
* 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/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)
10 changes: 10 additions & 0 deletions dts/bindings/counter/adi,max32-wut.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2025 Analog Devices, Inc.
# SPDX-License-Identifier: Apache-2.0

description: |
ADI MAX32 Wake-Up Timer is a unique instance of a 32-bit timer that can wake up the
device from sleep, standby and backup modes.

compatible: "adi,max32-wut"

include: [base.yaml]