diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 157d5506040..d406f7e2d62 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -50,5 +50,6 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_SNPS_DW counter_dw_timer zephyr_library_sources_ifdef(CONFIG_COUNTER_SHELL counter_timer_shell.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_RPI_PICO counter_rpi_pico_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_MAX32 counter_max32_timer.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_RTC_MAX32 counter_max32_rtc.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_NXP_MRT counter_nxp_mrt.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_RA_AGT counter_renesas_ra_agt.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index c801b413d86..32baf8255ce 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -98,6 +98,8 @@ source "drivers/counter/Kconfig.rpi_pico" source "drivers/counter/Kconfig.max32_timer" +source "drivers/counter/Kconfig.max32_rtc" + source "drivers/counter/Kconfig.nxp_mrt" source "drivers/counter/Kconfig.renesas_ra" diff --git a/drivers/counter/Kconfig.max32_rtc b/drivers/counter/Kconfig.max32_rtc new file mode 100644 index 00000000000..19dbfd453df --- /dev/null +++ b/drivers/counter/Kconfig.max32_rtc @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Analog Devices, Inc. +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_RTC_MAX32 + bool "MAX32xxx counter rtc driver" + default y + depends on DT_HAS_ADI_MAX32_RTC_COUNTER_ENABLED + help + Enable the counter rtc driver for MAX32 MCUs. diff --git a/drivers/counter/counter_max32_rtc.c b/drivers/counter/counter_max32_rtc.c new file mode 100644 index 00000000000..b1e3b217381 --- /dev/null +++ b/drivers/counter/counter_max32_rtc.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2024 Analog Devices, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT adi_max32_rtc_counter + +#include +#include +#include +#include + +#include +#include + +/* Resoultion is 1sec for time of day alarm*/ +#define MAX32_RTC_COUNTER_FREQ 1 + +/* 20bits used for time of day alarm */ +#define MAX32_RTC_COUNTER_MAX_VALUE ((1 << 21) - 1) + +#define MAX32_RTC_COUNTER_INT_FL MXC_RTC_INT_FL_LONG +#define MAX32_RTC_COUNTER_INT_EN MXC_RTC_INT_EN_LONG + +struct max32_rtc_data { + counter_alarm_callback_t alarm_callback; + counter_top_callback_t top_callback; + void *alarm_user_data; + void *top_user_data; +}; + +struct max32_rtc_config { + struct counter_config_info info; + mxc_rtc_regs_t *regs; + void (*irq_func)(void); +}; + +static int api_start(const struct device *dev) +{ + /* Ensure that both sec and subsec are reset to 0 */ + while (MXC_RTC_Init(0, 0) == E_BUSY) { + ; + } + + while (MXC_RTC_Start() == E_BUSY) { + ; + } + + while (MXC_RTC_EnableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) { + ; + } + + return 0; +} + +static int api_stop(const struct device *dev) +{ + ARG_UNUSED(dev); + + while (MXC_RTC_DisableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) { + ; + } + MXC_RTC_Stop(); + + return 0; +} + +static int api_get_value(const struct device *dev, uint32_t *ticks) +{ + const struct max32_rtc_config *cfg = dev->config; + mxc_rtc_regs_t *regs = cfg->regs; + uint32_t sec = 0, subsec = 0; + + /* Read twice incase of glitch */ + sec = regs->sec; + if (regs->sec != sec) { + sec = regs->sec; + } + + /* Read twice incase of glitch */ + subsec = regs->ssec; + if (regs->ssec != subsec) { + subsec = regs->ssec; + } + + *ticks = sec; + if (subsec >= (MXC_RTC_MAX_SSEC / 2)) { + *ticks += 1; + } + + return 0; +} + +static int api_set_top_value(const struct device *dev, const struct counter_top_cfg *counter_cfg) +{ + const struct max32_rtc_config *cfg = dev->config; + struct max32_rtc_data *const data = dev->data; + + if (counter_cfg->ticks == 0) { + return -EINVAL; + } + + if (counter_cfg->ticks != cfg->info.max_top_value) { + return -ENOTSUP; + } + + data->top_callback = counter_cfg->callback; + data->top_user_data = counter_cfg->user_data; + + return 0; +} + +static uint32_t api_get_pending_int(const struct device *dev) +{ + ARG_UNUSED(dev); + int flags = MXC_RTC_GetFlags(); + + if (flags & MAX32_RTC_COUNTER_INT_FL) { + return 1; + } + + return 0; +} + +static uint32_t api_get_top_value(const struct device *dev) +{ + const struct max32_rtc_config *cfg = dev->config; + + return cfg->info.max_top_value; +} + +static int api_set_alarm(const struct device *dev, uint8_t chan, + const struct counter_alarm_cfg *alarm_cfg) +{ + int ret; + struct max32_rtc_data *data = dev->data; + uint32_t ticks = alarm_cfg->ticks; + uint32_t current; + + /* Alarm frequenct is 1Hz so that it seems ticks becomes 0 + * some times, in that case system being blocked. + * Set it to 1 if ticks is 0 + */ + if (ticks == 0) { + ticks = 1; + } + + if (alarm_cfg->ticks > api_get_top_value(dev)) { + return -EINVAL; + } + + if (data->alarm_callback != NULL) { + return -EBUSY; + } + + api_stop(dev); + + api_get_value(dev, ¤t); + if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) { + ticks += current; + } + + ret = MXC_RTC_SetTimeofdayAlarm(ticks); + if (ret == E_BUSY) { + ret = -EBUSY; + } + + if (ret == 0) { + data->alarm_callback = alarm_cfg->callback; + data->alarm_user_data = alarm_cfg->user_data; + } + + api_start(dev); + + return ret; +} + +static int api_cancel_alarm(const struct device *dev, uint8_t chan) +{ + struct max32_rtc_data *data = dev->data; + + while (MXC_RTC_DisableInt(MAX32_RTC_COUNTER_INT_EN) == E_BUSY) { + ; + } + data->alarm_callback = NULL; + + return 0; +} + +static void rtc_max32_isr(const struct device *dev) +{ + struct max32_rtc_data *const data = dev->data; + int flags = MXC_RTC_GetFlags(); + + if (flags & MAX32_RTC_COUNTER_INT_FL) { + if (data->alarm_callback) { + counter_alarm_callback_t cb; + uint32_t current; + + api_get_value(dev, ¤t); + + cb = data->alarm_callback; + data->alarm_callback = NULL; + cb(dev, 0, current, data->alarm_user_data); + } + } + + /* Clear all flags */ + MXC_RTC_ClearFlags(flags); +} + +static int rtc_max32_init(const struct device *dev) +{ + const struct max32_rtc_config *cfg = dev->config; + + while (MXC_RTC_Init(0, 0) == E_BUSY) { + ; + } + + api_stop(dev); + + cfg->irq_func(); + + return 0; +} + +static const struct counter_driver_api counter_rtc_max32_driver_api = { + .start = api_start, + .stop = api_stop, + .get_value = api_get_value, + .set_top_value = api_set_top_value, + .get_pending_int = api_get_pending_int, + .get_top_value = api_get_top_value, + .set_alarm = api_set_alarm, + .cancel_alarm = api_cancel_alarm, +}; + +#define COUNTER_RTC_MAX32_INIT(_num) \ + static struct max32_rtc_data rtc_max32_data_##_num; \ + static void max32_rtc_irq_init_##_num(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(_num), DT_INST_IRQ(_num, priority), rtc_max32_isr, \ + DEVICE_DT_INST_GET(_num), 0); \ + irq_enable(DT_INST_IRQN(_num)); \ + if (DT_INST_PROP(_num, wakeup_source)) { \ + MXC_LP_EnableRTCAlarmWakeup(); \ + } \ + }; \ + static const struct max32_rtc_config rtc_max32_config_##_num = { \ + .info = \ + { \ + .max_top_value = MAX32_RTC_COUNTER_MAX_VALUE, \ + .freq = MAX32_RTC_COUNTER_FREQ, \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = 1, \ + }, \ + .regs = (mxc_rtc_regs_t *)DT_INST_REG_ADDR(_num), \ + .irq_func = max32_rtc_irq_init_##_num, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(_num, &rtc_max32_init, NULL, &rtc_max32_data_##_num, \ + &rtc_max32_config_##_num, PRE_KERNEL_1, \ + CONFIG_COUNTER_INIT_PRIORITY, &counter_rtc_max32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(COUNTER_RTC_MAX32_INIT)