You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
16 KiB
641 lines
16 KiB
/* |
|
* Copyright (c) 2024-2025 Gerson Fernando Budke <nandojve@gmail.com> |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT atmel_sam0_rtc |
|
|
|
/** @file |
|
* @brief RTC driver for Atmel SAM0 MCU family. |
|
*/ |
|
|
|
#include <stdlib.h> |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/rtc.h> |
|
#include "rtc_utils.h" |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(rtc_sam0, CONFIG_RTC_LOG_LEVEL); |
|
|
|
/* clang-format off */ |
|
|
|
#define RTC_SAM0_TIME_MASK \ |
|
(RTC_ALARM_TIME_MASK_SECOND \ |
|
| RTC_ALARM_TIME_MASK_MINUTE \ |
|
| RTC_ALARM_TIME_MASK_HOUR \ |
|
| RTC_ALARM_TIME_MASK_MONTHDAY \ |
|
| RTC_ALARM_TIME_MASK_MONTH \ |
|
| RTC_ALARM_TIME_MASK_YEAR \ |
|
) |
|
|
|
#define RTC_SAM0_CALIBRATE_PPB_MAX (127) |
|
#define RTC_SAM0_CALIBRATE_PPB_QUANTA (1000) |
|
|
|
enum rtc_sam0_counter_mode { |
|
COUNTER_MODE_0, |
|
COUNTER_MODE_1, |
|
COUNTER_MODE_2, |
|
}; |
|
|
|
struct rtc_sam0_config { |
|
Rtc *regs; |
|
enum rtc_sam0_counter_mode mode; |
|
uint16_t prescaler; |
|
|
|
volatile uint32_t *mclk; |
|
uint32_t mclk_mask; |
|
uint32_t gclk_gen; |
|
uint16_t gclk_id; |
|
bool has_gclk; |
|
bool has_osc32kctrl; |
|
uint8_t osc32_src; |
|
uint32_t evt_ctrl_msk; |
|
|
|
#ifdef CONFIG_RTC_ALARM |
|
uint8_t alarms_count; |
|
#endif /* CONFIG_RTC_ALARM */ |
|
#ifdef CONFIG_RTC_CALIBRATION |
|
int32_t cal_constant; |
|
#endif |
|
}; |
|
|
|
struct rtc_sam0_data_cb { |
|
rtc_alarm_callback cb; |
|
void *cb_data; |
|
}; |
|
|
|
struct rtc_sam0_data { |
|
struct k_spinlock lock; |
|
#ifdef CONFIG_RTC_ALARM |
|
struct rtc_sam0_data_cb *const alarms; |
|
#endif /* CONFIG_RTC_ALARM */ |
|
}; |
|
|
|
static inline void rtc_sam0_sync(Rtc *rtc) |
|
{ |
|
/* Wait for synchronization */ |
|
#ifdef MCLK |
|
while (rtc->MODE0.SYNCBUSY.reg & RTC_MODE0_SYNCBUSY_MASK) { |
|
} |
|
#else |
|
while (rtc->MODE0.STATUS.reg & RTC_STATUS_SYNCBUSY) { |
|
} |
|
#endif |
|
} |
|
|
|
static int rtc_sam0_set_time(const struct device *dev, const struct rtc_time *timeptr) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
uint32_t datetime = 0; |
|
|
|
if (rtc_utils_validate_rtc_time(timeptr, RTC_SAM0_TIME_MASK) == false) { |
|
return -EINVAL; |
|
} |
|
|
|
datetime |= RTC_MODE2_CLOCK_SECOND(timeptr->tm_sec); |
|
datetime |= RTC_MODE2_CLOCK_MINUTE(timeptr->tm_min); |
|
datetime |= RTC_MODE2_CLOCK_HOUR(timeptr->tm_hour); |
|
datetime |= RTC_MODE2_CLOCK_DAY(timeptr->tm_mday); |
|
datetime |= RTC_MODE2_CLOCK_MONTH(timeptr->tm_mon + 1); |
|
datetime |= RTC_MODE2_CLOCK_YEAR(timeptr->tm_year - 99); |
|
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock); |
|
|
|
#ifdef MCLK |
|
regs->CTRLA.reg &= ~RTC_MODE0_CTRLA_ENABLE; |
|
rtc_sam0_sync(cfg->regs); |
|
regs->CLOCK.reg = datetime; |
|
regs->CTRLA.reg |= RTC_MODE0_CTRLA_ENABLE; |
|
#else |
|
regs->CTRL.reg &= ~RTC_MODE0_CTRL_ENABLE; |
|
rtc_sam0_sync(cfg->regs); |
|
regs->CLOCK.reg = datetime; |
|
regs->CTRL.reg |= RTC_MODE0_CTRL_ENABLE; |
|
#endif |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
return 0; |
|
} |
|
|
|
static int rtc_sam0_get_time(const struct device *dev, struct rtc_time *timeptr) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
RTC_MODE2_CLOCK_Type calendar = cfg->regs->MODE2.CLOCK; |
|
|
|
timeptr->tm_sec = calendar.bit.SECOND; |
|
timeptr->tm_min = calendar.bit.MINUTE; |
|
timeptr->tm_hour = calendar.bit.HOUR; |
|
timeptr->tm_mday = calendar.bit.DAY; |
|
timeptr->tm_mon = calendar.bit.MONTH - 1; |
|
timeptr->tm_year = calendar.bit.YEAR + 99; |
|
timeptr->tm_wday = -1; |
|
timeptr->tm_yday = -1; |
|
timeptr->tm_isdst = -1; |
|
timeptr->tm_nsec = 0; |
|
|
|
LOG_DBG("D/M/Y H:M:S %02d/%02d/%02d %02d:%02d:%02d", |
|
timeptr->tm_mday, timeptr->tm_mon + 1, timeptr->tm_year - 99, |
|
timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); |
|
|
|
return 0; |
|
} |
|
|
|
#ifdef CONFIG_RTC_ALARM |
|
static inline uint32_t rtc_sam0_datetime_from_tm(const struct rtc_time *timeptr, |
|
uint32_t mask) |
|
{ |
|
uint32_t datetime = 0; |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_SECOND) { |
|
datetime |= RTC_MODE2_CLOCK_SECOND(timeptr->tm_sec); |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MINUTE) { |
|
datetime |= RTC_MODE2_CLOCK_MINUTE(timeptr->tm_min); |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_HOUR) { |
|
datetime |= RTC_MODE2_CLOCK_HOUR(timeptr->tm_hour); |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { |
|
datetime |= RTC_MODE2_CLOCK_DAY(timeptr->tm_mday); |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTH) { |
|
datetime |= RTC_MODE2_CLOCK_MONTH(timeptr->tm_mon + 1); |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_YEAR) { |
|
datetime |= RTC_MODE2_CLOCK_YEAR(timeptr->tm_year - 99); |
|
} |
|
|
|
return datetime; |
|
} |
|
|
|
static inline void rtc_sam0_tm_from_datetime(struct rtc_time *timeptr, uint32_t mask, |
|
RTC_MODE2_ALARM_Type calendar) |
|
{ |
|
memset(timeptr, 0x00, sizeof(struct rtc_time)); |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_SECOND) { |
|
timeptr->tm_sec = calendar.bit.SECOND; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MINUTE) { |
|
timeptr->tm_min = calendar.bit.MINUTE; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_HOUR) { |
|
timeptr->tm_hour = calendar.bit.HOUR; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { |
|
timeptr->tm_mday = calendar.bit.DAY; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTH) { |
|
timeptr->tm_mon = calendar.bit.MONTH - 1; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_YEAR) { |
|
timeptr->tm_year = calendar.bit.YEAR + 99; |
|
} |
|
|
|
timeptr->tm_wday = -1; |
|
timeptr->tm_yday = -1; |
|
timeptr->tm_isdst = -1; |
|
timeptr->tm_nsec = 0; |
|
} |
|
|
|
static inline uint32_t rtc_sam0_alarm_msk_from_mask(uint32_t mask) |
|
{ |
|
uint32_t alarm_mask = 0; |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_SECOND) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_SS_Val; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MINUTE) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_MMSS_Val; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_HOUR) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_HHMMSS_Val; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_DDHHMMSS_Val; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_MONTH) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_MMDDHHMMSS_Val; |
|
} |
|
|
|
if (mask & RTC_ALARM_TIME_MASK_YEAR) { |
|
alarm_mask = RTC_MODE2_MASK_SEL_YYMMDDHHMMSS_Val; |
|
} |
|
|
|
return alarm_mask; |
|
} |
|
|
|
static inline uint32_t rtc_sam0_mask_from_alarm_msk(uint32_t alarm_mask) |
|
{ |
|
uint32_t mask = 0; |
|
|
|
switch (alarm_mask) { |
|
case RTC_MODE2_MASK_SEL_YYMMDDHHMMSS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_YEAR; |
|
__fallthrough; |
|
case RTC_MODE2_MASK_SEL_MMDDHHMMSS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_MONTH; |
|
__fallthrough; |
|
case RTC_MODE2_MASK_SEL_DDHHMMSS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_MONTHDAY; |
|
__fallthrough; |
|
case RTC_MODE2_MASK_SEL_HHMMSS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_HOUR; |
|
__fallthrough; |
|
case RTC_MODE2_MASK_SEL_MMSS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_MINUTE; |
|
__fallthrough; |
|
case RTC_MODE2_MASK_SEL_SS_Val: |
|
mask |= RTC_ALARM_TIME_MASK_SECOND; |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return mask; |
|
} |
|
|
|
static int rtc_sam0_alarm_get_supported_fields(const struct device *dev, uint16_t id, |
|
uint16_t *mask) |
|
{ |
|
ARG_UNUSED(dev); |
|
ARG_UNUSED(id); |
|
|
|
*mask = RTC_SAM0_TIME_MASK; |
|
|
|
return 0; |
|
} |
|
|
|
static int rtc_sam0_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, |
|
const struct rtc_time *timeptr) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
uint32_t mask_supported = RTC_SAM0_TIME_MASK; |
|
uint32_t datetime; |
|
uint32_t alarm_msk; |
|
|
|
if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { |
|
return -EINVAL; |
|
} |
|
|
|
if ((mask > 0) && (timeptr == NULL)) { |
|
return -EINVAL; |
|
} |
|
|
|
if (mask & ~mask_supported) { |
|
return -EINVAL; |
|
} |
|
|
|
if (rtc_utils_validate_rtc_time(timeptr, mask) == false) { |
|
return -EINVAL; |
|
} |
|
|
|
datetime = rtc_sam0_datetime_from_tm(timeptr, mask); |
|
alarm_msk = rtc_sam0_alarm_msk_from_mask(mask); |
|
|
|
LOG_DBG("S: datetime: %d, mask: %d", datetime, alarm_msk); |
|
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock); |
|
|
|
irq_disable(DT_INST_IRQN(0)); |
|
|
|
rtc_sam0_sync(cfg->regs); |
|
regs->Mode2Alarm[id].ALARM.reg = datetime; |
|
regs->Mode2Alarm[id].MASK.reg = RTC_MODE2_MASK_SEL(alarm_msk); |
|
regs->INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM(BIT(id)); |
|
|
|
irq_enable(DT_INST_IRQN(0)); |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
return 0; |
|
} |
|
|
|
static int rtc_sam0_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, |
|
struct rtc_time *timeptr) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
RTC_MODE2_ALARM_Type datetime; |
|
uint32_t alarm_msk; |
|
|
|
if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { |
|
return -EINVAL; |
|
} |
|
|
|
if ((mask == NULL) || (timeptr == NULL)) { |
|
return -EINVAL; |
|
} |
|
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock); |
|
|
|
rtc_sam0_sync(cfg->regs); |
|
|
|
datetime = regs->Mode2Alarm[id].ALARM; |
|
alarm_msk = regs->Mode2Alarm[id].MASK.reg; |
|
|
|
LOG_DBG("G: datetime: %d, mask: %d", datetime.reg, alarm_msk); |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
*mask = rtc_sam0_mask_from_alarm_msk(alarm_msk); |
|
|
|
rtc_sam0_tm_from_datetime(timeptr, *mask, datetime); |
|
|
|
return 0; |
|
} |
|
|
|
static int rtc_sam0_alarm_is_pending(const struct device *dev, uint16_t id) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
|
|
if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { |
|
return -EINVAL; |
|
} |
|
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock); |
|
|
|
if ((regs->INTFLAG.reg & RTC_MODE2_INTFLAG_ALARM(BIT(id))) == 0) { |
|
k_spin_unlock(&data->lock, key); |
|
|
|
return 0; |
|
} |
|
|
|
regs->INTFLAG.reg = RTC_MODE2_INTFLAG_ALARM(BIT(id)); |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
return 1; |
|
} |
|
|
|
static int rtc_sam0_alarm_set_callback(const struct device *dev, uint16_t id, |
|
rtc_alarm_callback callback, void *user_data) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
|
|
if (BIT(id) > RTC_MODE2_INTFLAG_ALARM_Msk) { |
|
return -EINVAL; |
|
} |
|
|
|
k_spinlock_key_t key = k_spin_lock(&data->lock); |
|
|
|
data->alarms[id].cb = callback; |
|
data->alarms[id].cb_data = user_data; |
|
|
|
if (callback) { |
|
regs->INTENSET.reg = RTC_MODE2_INTENSET_ALARM(BIT(id)); |
|
} else { |
|
regs->INTENCLR.reg = RTC_MODE2_INTENCLR_ALARM(BIT(id)); |
|
} |
|
|
|
k_spin_unlock(&data->lock, key); |
|
|
|
return 0; |
|
} |
|
|
|
static void rtc_sam0_isr(const struct device *dev) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
struct rtc_sam0_data *data = dev->data; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
uint32_t int_flags = regs->INTFLAG.reg; |
|
|
|
for (int i = 0; i < cfg->alarms_count; ++i) { |
|
if (int_flags & RTC_MODE2_INTFLAG_ALARM(BIT(i))) { |
|
if (data->alarms[i].cb != NULL) { |
|
data->alarms[i].cb(dev, i, data->alarms[i].cb_data); |
|
} |
|
} |
|
} |
|
|
|
regs->INTFLAG.reg |= int_flags; |
|
} |
|
|
|
#endif /* CONFIG_RTC_ALARM */ |
|
|
|
#ifdef CONFIG_RTC_CALIBRATION |
|
static int rtc_sam0_set_calibration(const struct device *dev, int32_t calibration) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
int32_t correction = calibration / (1000000000 / cfg->cal_constant); |
|
uint32_t abs_correction = abs(correction); |
|
|
|
LOG_DBG("Correction: %d, Absolute: %d, Calibration: %d", |
|
correction, abs_correction, calibration); |
|
|
|
if (abs_correction == 0) { |
|
regs->FREQCORR.reg = 0; |
|
return 0; |
|
} |
|
|
|
if (abs_correction > RTC_SAM0_CALIBRATE_PPB_MAX) { |
|
LOG_ERR("The calibration %d result in an out of range value %d", |
|
calibration, abs_correction); |
|
return -EINVAL; |
|
} |
|
|
|
rtc_sam0_sync(cfg->regs); |
|
regs->FREQCORR.reg = RTC_FREQCORR_VALUE(abs_correction) |
|
| (correction < 0 ? RTC_FREQCORR_SIGN : 0); |
|
|
|
LOG_DBG("W REG: 0x%02x", regs->FREQCORR.reg); |
|
|
|
return 0; |
|
} |
|
|
|
static int rtc_sam0_get_calibration(const struct device *dev, int32_t *calibration) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
RtcMode2 *regs = &cfg->regs->MODE2; |
|
int32_t correction; |
|
|
|
if (calibration == NULL) { |
|
return -EINVAL; |
|
} |
|
|
|
correction = regs->FREQCORR.bit.VALUE; |
|
|
|
if (correction == 0) { |
|
*calibration = 0; |
|
} else { |
|
*calibration = (correction * 1000000000) / cfg->cal_constant; |
|
} |
|
|
|
if (regs->FREQCORR.bit.SIGN) { |
|
*calibration *= -1; |
|
} |
|
|
|
LOG_DBG("R REG: 0x%02x", regs->FREQCORR.reg); |
|
|
|
return 0; |
|
} |
|
#endif /* CONFIG_RTC_CALIBRATION */ |
|
|
|
static int rtc_sam0_init(const struct device *dev) |
|
{ |
|
const struct rtc_sam0_config *cfg = dev->config; |
|
RtcMode0 *regs = &cfg->regs->MODE0; |
|
|
|
LOG_DBG("Counter Mode %d selected", cfg->mode); |
|
LOG_DBG("gclk_id: %d, gclk_gen: %d, prescaler: %d, osc32k: %d", |
|
cfg->gclk_id, cfg->gclk_gen, cfg->prescaler, cfg->osc32_src); |
|
|
|
*cfg->mclk |= cfg->mclk_mask; |
|
|
|
#ifdef MCLK |
|
if (cfg->has_gclk) { |
|
GCLK->PCHCTRL[cfg->gclk_id].reg = GCLK_PCHCTRL_CHEN |
|
| GCLK_PCHCTRL_GEN(cfg->gclk_gen); |
|
} |
|
#else |
|
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |
|
| GCLK_CLKCTRL_GEN(cfg->gclk_gen) |
|
| GCLK_CLKCTRL_ID(cfg->gclk_id); |
|
#endif |
|
rtc_sam0_sync(cfg->regs); |
|
|
|
#ifdef MCLK |
|
if (cfg->has_osc32kctrl) { |
|
OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL(cfg->osc32_src); |
|
} |
|
#endif |
|
|
|
rtc_sam0_sync(cfg->regs); |
|
regs->EVCTRL.reg = (cfg->evt_ctrl_msk & RTC_MODE0_EVCTRL_MASK); |
|
|
|
#ifdef MCLK |
|
regs->CTRLA.reg = RTC_MODE0_CTRLA_ENABLE |
|
| RTC_MODE0_CTRLA_COUNTSYNC |
|
| RTC_MODE0_CTRLA_MODE(cfg->mode) |
|
| RTC_MODE0_CTRLA_PRESCALER(cfg->prescaler + 1); |
|
#else |
|
regs->CTRL.reg = RTC_MODE0_CTRL_ENABLE |
|
| RTC_MODE0_CTRL_MODE(cfg->mode) |
|
| RTC_MODE0_CTRL_PRESCALER(cfg->prescaler); |
|
#endif |
|
|
|
regs->INTFLAG.reg = 0; |
|
#ifdef CONFIG_RTC_ALARM |
|
IRQ_CONNECT(DT_INST_IRQN(0), |
|
DT_INST_IRQ(0, priority), |
|
rtc_sam0_isr, |
|
DEVICE_DT_INST_GET(0), 0); |
|
irq_enable(DT_INST_IRQN(0)); |
|
#endif |
|
return 0; |
|
} |
|
|
|
static DEVICE_API(rtc, rtc_sam0_driver_api) = { |
|
.set_time = rtc_sam0_set_time, |
|
.get_time = rtc_sam0_get_time, |
|
#ifdef CONFIG_RTC_ALARM |
|
.alarm_get_supported_fields = rtc_sam0_alarm_get_supported_fields, |
|
.alarm_set_time = rtc_sam0_alarm_set_time, |
|
.alarm_get_time = rtc_sam0_alarm_get_time, |
|
.alarm_is_pending = rtc_sam0_alarm_is_pending, |
|
.alarm_set_callback = rtc_sam0_alarm_set_callback, |
|
#endif /* CONFIG_RTC_ALARM */ |
|
#ifdef CONFIG_RTC_CALIBRATION |
|
.set_calibration = rtc_sam0_set_calibration, |
|
.get_calibration = rtc_sam0_get_calibration, |
|
#endif /* CONFIG_RTC_CALIBRATION */ |
|
}; |
|
|
|
#define ASSIGNED_CLOCKS_CELL_BY_NAME \ |
|
ATMEL_SAM0_DT_INST_ASSIGNED_CLOCKS_CELL_BY_NAME |
|
|
|
#define RTC_SAM0_GCLK(n) \ |
|
COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, gclk), \ |
|
( \ |
|
.has_gclk = true, \ |
|
.gclk_gen = ASSIGNED_CLOCKS_CELL_BY_NAME(n, gclk, gen), \ |
|
.gclk_id = DT_INST_CLOCKS_CELL_BY_NAME(n, gclk, id) \ |
|
), \ |
|
( \ |
|
.has_gclk = false, \ |
|
.gclk_gen = 0, \ |
|
.gclk_id = 0 \ |
|
)) |
|
|
|
#define RTC_SAM0_OSC32KCTRL(n) \ |
|
COND_CODE_1(DT_INST_CLOCKS_HAS_NAME(n, osc32kctrl), \ |
|
( \ |
|
.has_osc32kctrl = true, \ |
|
.osc32_src = ASSIGNED_CLOCKS_CELL_BY_NAME(n, osc32kctrl, src) \ |
|
), \ |
|
( \ |
|
.has_osc32kctrl = false, \ |
|
.osc32_src = 0 \ |
|
)) |
|
|
|
#define RTC_SAM0_DEVICE(n) \ |
|
BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, counter_mode), \ |
|
"sam0:rtc: Missing counter-mode devicetree property"); \ |
|
BUILD_ASSERT(DT_INST_NODE_HAS_PROP(n, prescaler), \ |
|
"sam0:rtc: Missing prescaler devicetree property"); \ |
|
\ |
|
static const struct rtc_sam0_config rtc_sam0_config_##n = { \ |
|
.regs = (Rtc *)DT_INST_REG_ADDR(n), \ |
|
.mode = DT_INST_ENUM_IDX(n, counter_mode), \ |
|
.prescaler = DT_INST_ENUM_IDX(n, prescaler), \ |
|
.evt_ctrl_msk = DT_INST_PROP(n, event_control_msk), \ |
|
RTC_SAM0_GCLK(n), \ |
|
RTC_SAM0_OSC32KCTRL(n), \ |
|
.mclk = ATMEL_SAM0_DT_INST_MCLK_PM_REG_ADDR_OFFSET(n), \ |
|
.mclk_mask = ATMEL_SAM0_DT_INST_MCLK_PM_PERIPH_MASK(n, bit), \ |
|
IF_ENABLED(CONFIG_RTC_ALARM, ( \ |
|
.alarms_count = DT_INST_PROP(n, alarms_count), \ |
|
)) \ |
|
IF_ENABLED(CONFIG_RTC_CALIBRATION, ( \ |
|
.cal_constant = DT_INST_PROP(n, cal_constant), \ |
|
)) \ |
|
}; \ |
|
\ |
|
IF_ENABLED(CONFIG_RTC_ALARM, ( \ |
|
static struct rtc_sam0_data_cb \ |
|
rtc_sam0_data_cb_##n[DT_INST_PROP(n, alarms_count)] = {}; \ |
|
)) \ |
|
\ |
|
static struct rtc_sam0_data rtc_sam0_data_##n = { \ |
|
IF_ENABLED(CONFIG_RTC_ALARM, ( \ |
|
.alarms = rtc_sam0_data_cb_##n, \ |
|
)) \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(n, rtc_sam0_init, \ |
|
NULL, \ |
|
&rtc_sam0_data_##n, \ |
|
&rtc_sam0_config_##n, POST_KERNEL, \ |
|
CONFIG_RTC_INIT_PRIORITY, \ |
|
&rtc_sam0_driver_api); \ |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(RTC_SAM0_DEVICE); |
|
|
|
/* clang-format on */
|
|
|