267 lines
6.9 KiB
C
267 lines
6.9 KiB
C
/*
|
|
* Copyright (c) 2024 Analog Devices, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT adi_max32_rtc_counter
|
|
|
|
#include <zephyr/drivers/counter.h>
|
|
#include <zephyr/drivers/clock_control/adi_max32_clock_control.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/irq.h>
|
|
|
|
#include <rtc.h>
|
|
#include <wrap_max32_lp.h>
|
|
|
|
/* 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)
|