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