From 5e38168f0cd11a678bfacd448df07e16de71243d Mon Sep 17 00:00:00 2001 From: Sadik Ozer Date: Mon, 18 Mar 2024 14:54:25 +0300 Subject: [PATCH] drivers: counter: Add MAX32xxx counter RTC driver Common RTC counter driver for MAX32xxx MCUs. Time of day alarm is used to generate interrupt. The resolution of time of day interrupt is 1Hz. Subsecond alarm interrupt not works it does not meet zephyr counter driver requirement, so that not used. To use as wakeup source wakeup-source parameter shall be defined as below &rtc_counter { status = "okay"; wakeup-source; }; Co-authored-by: Okan Sahin Signed-off-by: Sadik Ozer --- drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.max32_rtc | 9 + drivers/counter/counter_max32_rtc.c | 266 ++++++++++++++++++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 drivers/counter/Kconfig.max32_rtc create mode 100644 drivers/counter/counter_max32_rtc.c 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)