| /* | 
 |  * 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) |