Skip to content

Commit ab43ceb

Browse files
ttmutkartben
authored andcommitted
drivers: counter: Add MAX32 Wake-Up Timer driver
MAX32 Wake-Up Timer is a 32-bit timer that can wakeup the device from low-power modes. Signed-off-by: Tahsin Mutlugun <Tahsin.Mutlugun@analog.com>
1 parent 457ade4 commit ab43ceb

File tree

5 files changed

+300
-0
lines changed

5 files changed

+300
-0
lines changed

drivers/counter/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_RENESAS_RZ_GTM counter_renesas_
5858
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912_SLWTMR counter_realtek_rts5912_slwtmr.c)
5959
zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c)
6060
zephyr_library_sources_ifdef(CONFIG_COUNTER_NEORV32_GPTMR counter_neorv32_gptmr.c)
61+
zephyr_library_sources_ifdef(CONFIG_COUNTER_WUT_MAX32 counter_max32_wut.c)

drivers/counter/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,6 @@ source "drivers/counter/Kconfig.rts5912"
114114

115115
source "drivers/counter/Kconfig.neorv32"
116116

117+
source "drivers/counter/Kconfig.max32_wut"
118+
117119
endif # COUNTER

drivers/counter/Kconfig.max32_wut

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2025 Analog Devices, Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
config COUNTER_WUT_MAX32
5+
bool "MAX32xxx wake-up timer driver"
6+
default y
7+
depends on DT_HAS_ADI_MAX32_WUT_ENABLED
8+
help
9+
Enable the wake-up timer driver for MAX32 MCUs.

drivers/counter/counter_max32_wut.c

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/*
2+
* Copyright (c) 2025 Analog Devices, Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT adi_max32_wut
8+
9+
#include <zephyr/drivers/counter.h>
10+
#include <zephyr/irq.h>
11+
#include <zephyr/logging/log.h>
12+
13+
#include <wut.h>
14+
#include <wrap_max32_lp.h>
15+
#include <wrap_max32_sys.h>
16+
17+
LOG_MODULE_REGISTER(counter_max32_wut, CONFIG_COUNTER_LOG_LEVEL);
18+
19+
#define MAX32_WUT_COUNTER_FREQ 32768
20+
21+
struct max32_wut_alarm_data {
22+
counter_alarm_callback_t callback;
23+
void *user_data;
24+
};
25+
26+
struct max32_wut_data {
27+
struct max32_wut_alarm_data alarm;
28+
uint32_t guard_period;
29+
};
30+
31+
struct max32_wut_config {
32+
struct counter_config_info info;
33+
mxc_wut_regs_t *regs;
34+
int clock_source;
35+
int prescaler;
36+
void (*irq_func)(const struct device *dev);
37+
uint32_t irq_number;
38+
bool wakeup_source;
39+
};
40+
41+
static int counter_max32_wut_start(const struct device *dev)
42+
{
43+
const struct max32_wut_config *cfg = dev->config;
44+
45+
MXC_WUT_Enable(cfg->regs);
46+
47+
return 0;
48+
}
49+
50+
static int counter_max32_wut_stop(const struct device *dev)
51+
{
52+
const struct max32_wut_config *cfg = dev->config;
53+
54+
MXC_WUT_Disable(cfg->regs);
55+
56+
return 0;
57+
}
58+
59+
static int counter_max32_wut_get_value(const struct device *dev, uint32_t *ticks)
60+
{
61+
const struct max32_wut_config *cfg = dev->config;
62+
63+
*ticks = MXC_WUT_GetCount(cfg->regs);
64+
65+
return 0;
66+
}
67+
68+
static int counter_max32_wut_set_top_value(const struct device *dev,
69+
const struct counter_top_cfg *top_cfg)
70+
{
71+
ARG_UNUSED(dev);
72+
ARG_UNUSED(top_cfg);
73+
74+
return -ENOTSUP;
75+
}
76+
77+
static uint32_t counter_max32_wut_get_pending_int(const struct device *dev)
78+
{
79+
const struct max32_wut_config *cfg = dev->config;
80+
81+
return MXC_WUT_GetFlags(cfg->regs);
82+
}
83+
84+
static uint32_t counter_max32_wut_get_top_value(const struct device *dev)
85+
{
86+
ARG_UNUSED(dev);
87+
88+
return UINT32_MAX;
89+
}
90+
91+
static uint32_t counter_max32_wut_get_freq(const struct device *dev)
92+
{
93+
const struct max32_wut_config *cfg = dev->config;
94+
95+
return cfg->info.freq;
96+
}
97+
98+
static uint32_t counter_max32_wut_get_guard_period(const struct device *dev, uint32_t flags)
99+
{
100+
struct max32_wut_data *data = dev->data;
101+
102+
ARG_UNUSED(flags);
103+
104+
return data->guard_period;
105+
}
106+
107+
static int counter_max32_wut_set_guard_period(const struct device *dev, uint32_t ticks,
108+
uint32_t flags)
109+
{
110+
struct max32_wut_data *data = dev->data;
111+
112+
ARG_UNUSED(flags);
113+
114+
if (ticks > counter_max32_wut_get_top_value(dev)) {
115+
return -EINVAL;
116+
}
117+
118+
data->guard_period = ticks;
119+
120+
return 0;
121+
}
122+
123+
static int counter_max32_wut_set_alarm(const struct device *dev, uint8_t chan,
124+
const struct counter_alarm_cfg *alarm_cfg)
125+
{
126+
const struct max32_wut_config *cfg = dev->config;
127+
struct max32_wut_data *data = dev->data;
128+
uint32_t now_ticks, top_ticks;
129+
uint64_t abs_ticks, min_abs_ticks;
130+
bool irq_on_late = false;
131+
bool absolute = alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE;
132+
133+
counter_max32_wut_get_value(dev, &now_ticks);
134+
135+
top_ticks = counter_max32_wut_get_top_value(dev);
136+
if (alarm_cfg->ticks > top_ticks) {
137+
return -EINVAL;
138+
}
139+
140+
if (data->alarm.callback != NULL) {
141+
return -EBUSY;
142+
}
143+
144+
MXC_WUT_ClearFlags(cfg->regs);
145+
146+
data->alarm.callback = alarm_cfg->callback;
147+
data->alarm.user_data = alarm_cfg->user_data;
148+
149+
if (absolute) {
150+
abs_ticks = alarm_cfg->ticks;
151+
} else {
152+
abs_ticks = (uint64_t)now_ticks + alarm_cfg->ticks;
153+
}
154+
155+
min_abs_ticks = (uint64_t)now_ticks + data->guard_period;
156+
if ((!absolute && (abs_ticks < now_ticks)) || (abs_ticks > min_abs_ticks)) {
157+
MXC_WUT_SetCompare(cfg->regs, abs_ticks & top_ticks);
158+
MXC_WUT_Enable(cfg->regs);
159+
return 0;
160+
}
161+
162+
irq_on_late = alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
163+
if (irq_on_late || !absolute) {
164+
NVIC_SetPendingIRQ(cfg->irq_number);
165+
} else {
166+
data->alarm.callback = NULL;
167+
data->alarm.user_data = NULL;
168+
}
169+
170+
return -ETIME;
171+
}
172+
173+
static int counter_max32_wut_cancel_alarm(const struct device *dev, uint8_t chan)
174+
{
175+
ARG_UNUSED(chan);
176+
struct max32_wut_data *data = dev->data;
177+
178+
counter_max32_wut_stop(dev);
179+
180+
data->alarm.callback = NULL;
181+
data->alarm.user_data = NULL;
182+
183+
return 0;
184+
}
185+
186+
static void counter_max32_wut_isr(const struct device *dev)
187+
{
188+
const struct max32_wut_config *cfg = dev->config;
189+
struct max32_wut_data *data = dev->data;
190+
191+
if (data->alarm.callback) {
192+
counter_alarm_callback_t cb = data->alarm.callback;
193+
194+
data->alarm.callback = NULL;
195+
cb(dev, 0, MXC_WUT_GetCount(cfg->regs), data->alarm.user_data);
196+
}
197+
198+
MXC_WUT_ClearFlags(cfg->regs);
199+
}
200+
201+
static int counter_max32_wut_init(const struct device *dev)
202+
{
203+
const struct max32_wut_config *cfg = dev->config;
204+
uint8_t prescaler_lo, prescaler_hi;
205+
mxc_wut_pres_t pres;
206+
mxc_wut_cfg_t wut_cfg;
207+
208+
Wrap_MXC_SYS_Select32KClockSource(cfg->clock_source);
209+
210+
prescaler_lo = FIELD_GET(GENMASK(2, 0), LOG2(cfg->prescaler));
211+
prescaler_hi = FIELD_GET(BIT(3), LOG2(cfg->prescaler));
212+
213+
pres = (prescaler_hi << MXC_F_WUT_CTRL_PRES3_POS) |
214+
(prescaler_lo << MXC_F_WUT_CTRL_PRES_POS);
215+
216+
MXC_WUT_Init(cfg->regs, pres);
217+
218+
wut_cfg.mode = MXC_WUT_MODE_COMPARE;
219+
wut_cfg.cmp_cnt = cfg->info.max_top_value;
220+
MXC_WUT_Config(cfg->regs, &wut_cfg);
221+
222+
MXC_WUT_SetCount(cfg->regs, 0);
223+
224+
cfg->irq_func(dev);
225+
226+
if (cfg->wakeup_source) {
227+
MXC_LP_EnableWUTAlarmWakeup();
228+
}
229+
230+
return 0;
231+
}
232+
233+
static const struct counter_driver_api counter_max32_wut_driver_api = {
234+
.start = counter_max32_wut_start,
235+
.stop = counter_max32_wut_stop,
236+
.get_value = counter_max32_wut_get_value,
237+
.set_top_value = counter_max32_wut_set_top_value,
238+
.get_pending_int = counter_max32_wut_get_pending_int,
239+
.get_top_value = counter_max32_wut_get_top_value,
240+
.get_freq = counter_max32_wut_get_freq,
241+
.set_alarm = counter_max32_wut_set_alarm,
242+
.cancel_alarm = counter_max32_wut_cancel_alarm,
243+
.get_guard_period = counter_max32_wut_get_guard_period,
244+
.set_guard_period = counter_max32_wut_set_guard_period,
245+
};
246+
247+
#define TIMER(_num) DT_INST_PARENT(_num)
248+
#define MAX32_TIM(idx) ((mxc_wut_regs_t *)DT_REG_ADDR(TIMER(idx)))
249+
250+
#define COUNTER_MAX32_WUT_DEFINE(_num) \
251+
static void max32_wut_irq_init_##_num(const struct device *dev) \
252+
{ \
253+
IRQ_CONNECT(DT_IRQN(TIMER(_num)), DT_IRQ(TIMER(_num), priority), \
254+
counter_max32_wut_isr, DEVICE_DT_INST_GET(_num), 0); \
255+
irq_enable(DT_IRQN(TIMER(_num))); \
256+
}; \
257+
static const struct max32_wut_config max32_wut_config_##_num = { \
258+
.info = \
259+
{ \
260+
.max_top_value = UINT32_MAX, \
261+
.freq = MAX32_WUT_COUNTER_FREQ / DT_PROP(TIMER(_num), prescaler), \
262+
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
263+
.channels = 1, \
264+
}, \
265+
.regs = (mxc_wut_regs_t *)DT_REG_ADDR(TIMER(_num)), \
266+
.clock_source = \
267+
DT_PROP_OR(TIMER(_num), clock_source, ADI_MAX32_PRPH_CLK_SRC_ERTCO), \
268+
.prescaler = DT_PROP(TIMER(_num), prescaler), \
269+
.irq_func = max32_wut_irq_init_##_num, \
270+
.irq_number = DT_IRQN(TIMER(_num)), \
271+
.wakeup_source = DT_PROP(TIMER(_num), wakeup_source), \
272+
}; \
273+
static struct max32_wut_data max32_wut_data##_num; \
274+
DEVICE_DT_INST_DEFINE(_num, &counter_max32_wut_init, NULL, &max32_wut_data##_num, \
275+
&max32_wut_config_##_num, PRE_KERNEL_1, \
276+
CONFIG_COUNTER_INIT_PRIORITY, &counter_max32_wut_driver_api);
277+
278+
DT_INST_FOREACH_STATUS_OKAY(COUNTER_MAX32_WUT_DEFINE)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Copyright (c) 2025 Analog Devices, Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: |
5+
ADI MAX32 Wake-Up Timer is a unique instance of a 32-bit timer that can wake up the
6+
device from sleep, standby and backup modes.
7+
8+
compatible: "adi,max32-wut"
9+
10+
include: [base.yaml]

0 commit comments

Comments
 (0)