From 99ce7d071fe63e47f9f6352e236987c47fe5b5e8 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Sun, 15 Oct 2023 20:34:17 +0200 Subject: [PATCH] drivers: rtc: Add atmel sam series RTC driver This commit adds an RTC device driver for the atmel SAM series chips. Signed-off-by: Bjarki Arge Andreasen --- drivers/rtc/CMakeLists.txt | 1 + drivers/rtc/Kconfig | 1 + drivers/rtc/Kconfig.sam | 9 + drivers/rtc/rtc_sam.c | 712 ++++++++++++++++++++++++++++ dts/bindings/rtc/atmel,sam-rtc.yaml | 18 + 5 files changed, 741 insertions(+) create mode 100644 drivers/rtc/Kconfig.sam create mode 100644 drivers/rtc/rtc_sam.c create mode 100644 dts/bindings/rtc/atmel,sam-rtc.yaml diff --git a/drivers/rtc/CMakeLists.txt b/drivers/rtc/CMakeLists.txt index 2475174ac68..5addee4faed 100644 --- a/drivers/rtc/CMakeLists.txt +++ b/drivers/rtc/CMakeLists.txt @@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_RTC_STM32 rtc_ll_stm32.c) zephyr_library_sources_ifdef(CONFIG_RTC_SHELL rtc_shell.c) zephyr_library_sources_ifdef(CONFIG_RTC_FAKE rtc_fake.c) zephyr_library_sources_ifdef(CONFIG_RTC_SMARTBOND rtc_smartbond.c) +zephyr_library_sources_ifdef(CONFIG_RTC_ATMEL_SAM rtc_sam.c) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e480aae0388..f9da0c293d7 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -47,6 +47,7 @@ source "drivers/rtc/Kconfig.fake" source "drivers/rtc/Kconfig.pcf8523" source "drivers/rtc/Kconfig.pcf8563" source "drivers/rtc/Kconfig.mc146818" +source "drivers/rtc/Kconfig.sam" source "drivers/rtc/Kconfig.stm32" source "drivers/rtc/Kconfig.smartbond" diff --git a/drivers/rtc/Kconfig.sam b/drivers/rtc/Kconfig.sam new file mode 100644 index 00000000000..5968f66bc42 --- /dev/null +++ b/drivers/rtc/Kconfig.sam @@ -0,0 +1,9 @@ +# Copyright (c) 2023 Bjarki Arge Andreasen +# SPDX-License-Identifier: Apache-2.0 + +config RTC_ATMEL_SAM + bool "Atmel SAM RTC driver" + default y + depends on DT_HAS_ATMEL_SAM_RTC_ENABLED + help + Atmel Real-Time Clock (RTC) driver used on SAM SoC series. diff --git a/drivers/rtc/rtc_sam.c b/drivers/rtc/rtc_sam.c new file mode 100644 index 00000000000..059a2b1b33e --- /dev/null +++ b/drivers/rtc/rtc_sam.c @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2023 Bjarki Arge Andreasen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT atmel_sam_rtc + +#include +#include +#include +#include +#include + +#include +#include + +#define RTC_SAM_REG_GET_FIELD(value, field) \ + ((RTC_##field##_Msk & value) >> RTC_##field##_Pos) + +#define RTC_SAM_WPMR_DISABLE 0x52544300 +#define RTC_SAM_WPMR_ENABLE 0x52544301 + +#define RTC_SAM_CALIBRATE_PPB_MAX (1950000) +#define RTC_SAM_CALIBRATE_PPB_MIN (-1950000) +#define RTC_SAM_CALIBRATE_PPB_QUANTA (1500) +#define RTC_SAM_CALIBRATE_PPB_LOW_SCALE (30500) + +typedef void (*rtc_sam_irq_init_fn_ptr)(void); + +struct rtc_sam_config { + Rtc *regs; + uint16_t irq_num; + rtc_sam_irq_init_fn_ptr irq_init_fn_ptr; +}; + +struct rtc_sam_data { +#ifdef CONFIG_RTC_ALARM + rtc_alarm_callback alarm_callback; + void *alarm_user_data; +#endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_UPDATE + rtc_update_callback update_callback; + void *update_user_data; +#endif /* CONFIG_RTC_UPDATE */ + struct k_spinlock lock; + struct k_sem cr_sec_evt_sem; + struct k_sem cr_upd_ack_sem; +}; + +static void rtc_sam_disable_wp(void) +{ + REG_RTC_WPMR = RTC_SAM_WPMR_DISABLE; +} + +static void rtc_sam_enable_wp(void) +{ + REG_RTC_WPMR = RTC_SAM_WPMR_ENABLE; +} + +static bool rtc_sam_validate_tm(const struct rtc_time *timeptr, uint32_t mask) +{ + if ((mask & RTC_ALARM_TIME_MASK_SECOND) && + (timeptr->tm_sec < 0 || timeptr->tm_sec > 59)) { + return false; + } + + if ((mask & RTC_ALARM_TIME_MASK_MINUTE) && + (timeptr->tm_min < 0 || timeptr->tm_min > 59)) { + return false; + } + + if ((mask & RTC_ALARM_TIME_MASK_HOUR) && + (timeptr->tm_hour < 0 || timeptr->tm_hour > 23)) { + return false; + } + + if ((mask & RTC_ALARM_TIME_MASK_MONTH) && + (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)) { + return false; + } + + if ((mask & RTC_ALARM_TIME_MASK_MONTHDAY) && + (timeptr->tm_mday < 1 || timeptr->tm_mday > 31)) { + return false; + } + + if ((mask & RTC_ALARM_TIME_MASK_YEAR) && + (timeptr->tm_year < 0 || timeptr->tm_year > 199)) { + return false; + } + + return true; +} + +static uint32_t rtc_sam_timr_from_tm(const struct rtc_time *timeptr) +{ + uint32_t timr; + + timr = RTC_TIMR_SEC(bin2bcd(timeptr->tm_sec)); + timr |= RTC_TIMR_MIN(bin2bcd(timeptr->tm_min)); + timr |= RTC_TIMR_HOUR(bin2bcd(timeptr->tm_hour)); + + return timr; +} + +static uint32_t rtc_sam_calr_from_tm(const struct rtc_time *timeptr) +{ + uint32_t calr; + uint8_t centuries; + uint8_t years; + + calr = RTC_CALR_DATE(bin2bcd(timeptr->tm_mday)); + calr |= RTC_CALR_MONTH(bin2bcd(timeptr->tm_mon + 1)); + centuries = (uint8_t)((timeptr->tm_year / 100) + 19); + years = (uint8_t)((timeptr->tm_year % 100)); + calr |= RTC_CALR_CENT(bin2bcd(centuries)); + calr |= RTC_CALR_YEAR(bin2bcd(years)); + calr |= RTC_CALR_DAY(bin2bcd(timeptr->tm_wday + 1)); + return calr; +} + +static int rtc_sam_set_time(const struct device *dev, const struct rtc_time *timeptr) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + if (rtc_sam_validate_tm(timeptr, UINT32_MAX) == false) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + k_sem_reset(&data->cr_sec_evt_sem); + k_sem_take(&data->cr_sec_evt_sem, K_MSEC(1100)); + k_sem_reset(&data->cr_upd_ack_sem); + + /* Enable update acknowledge interrupt */ + regs->RTC_IER = RTC_IER_ACKEN; + + rtc_sam_disable_wp(); + + /* Request update */ + regs->RTC_CR = (RTC_CR_UPDTIM | RTC_CR_UPDCAL); + + /* Await update acknowledge */ + if (k_sem_take(&data->cr_upd_ack_sem, K_MSEC(1100)) < 0) { + regs->RTC_CR = 0; + + rtc_sam_enable_wp(); + + /* Disable update acknowledge interrupt */ + regs->RTC_IDR = RTC_IDR_ACKDIS; + + k_spin_unlock(&data->lock, key); + return -EAGAIN; + } + + regs->RTC_TIMR = rtc_sam_timr_from_tm(timeptr); + regs->RTC_CALR = rtc_sam_calr_from_tm(timeptr); + regs->RTC_CR = 0; + rtc_sam_enable_wp(); + regs->RTC_IDR = RTC_IDR_ACKDIS; + k_spin_unlock(&data->lock, key); + return 0; +} + +static int rtc_sam_get_time(const struct device *dev, struct rtc_time *timeptr) +{ + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + uint32_t timr0; + uint32_t calr0; + uint32_t timr1; + uint32_t calr1; + + /* Validate time and date */ + if (regs->RTC_VER & (RTC_VER_NVTIM | RTC_VER_NVCAL)) { + return -ENODATA; + } + + /* Read until synchronized (registers updated async) */ + while (1) { + timr0 = regs->RTC_TIMR; + calr0 = regs->RTC_CALR; + timr1 = regs->RTC_TIMR; + calr1 = regs->RTC_CALR; + + if ((timr0 == timr1) && (calr0 == calr1)) { + break; + } + } + + timeptr->tm_sec = bcd2bin(RTC_SAM_REG_GET_FIELD(timr0, TIMR_SEC)); + timeptr->tm_min = bcd2bin(RTC_SAM_REG_GET_FIELD(timr0, TIMR_MIN)); + timeptr->tm_hour = bcd2bin(RTC_SAM_REG_GET_FIELD(timr0, TIMR_HOUR)); + timeptr->tm_mday = bcd2bin(RTC_SAM_REG_GET_FIELD(calr0, CALR_DATE)); + timeptr->tm_mon = bcd2bin(RTC_SAM_REG_GET_FIELD(calr0, CALR_MONTH)) - 1; + + timeptr->tm_year = bcd2bin(RTC_SAM_REG_GET_FIELD(calr0, CALR_YEAR)); + timeptr->tm_year += ((int)bcd2bin(RTC_SAM_REG_GET_FIELD(calr0, CALR_CENT))) * 100; + timeptr->tm_year -= 1900; + + timeptr->tm_wday = bcd2bin(RTC_SAM_REG_GET_FIELD(calr0, CALR_DAY)) - 1; + timeptr->tm_yday = -1; + timeptr->tm_isdst = -1; + timeptr->tm_nsec = 0; + return 0; +} + +static void rtc_sam_isr(const struct device *dev) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + uint32_t sr = regs->RTC_SR; + + if (sr & RTC_SR_ACKUPD) { + regs->RTC_SCCR = RTC_SCCR_ACKCLR; + k_sem_give(&data->cr_upd_ack_sem); + } + +#ifdef CONFIG_RTC_ALARM + if (sr & RTC_SR_ALARM) { + regs->RTC_SCCR = RTC_SCCR_ALRCLR; + if (data->alarm_callback != NULL) { + data->alarm_callback(dev, 0, data->alarm_user_data); + } + } +#endif /* CONFIG_RTC_ALARM */ + +#ifdef CONFIG_RTC_UPDATE + if (sr & RTC_SR_SEC) { + regs->RTC_SCCR = RTC_SCCR_SECCLR; + if (data->update_callback != NULL) { + data->update_callback(dev, data->update_user_data); + } + + k_sem_give(&data->cr_sec_evt_sem); + } +#endif /* CONFIG_RTC_UPDATE */ +} + +#ifdef CONFIG_RTC_ALARM +static uint16_t rtc_sam_alarm_get_supported_mask(void) +{ + return (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); +} + +static uint32_t rtc_atmel_timalr_from_tm(const struct rtc_time *timeptr, uint32_t mask) +{ + uint32_t timalr = 0; + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + timalr |= RTC_TIMALR_SECEN; + timalr |= RTC_TIMALR_SEC(bin2bcd(timeptr->tm_sec)); + } + + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + timalr |= RTC_TIMALR_MINEN; + timalr |= RTC_TIMALR_MIN(bin2bcd(timeptr->tm_min)); + } + + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + timalr |= RTC_TIMALR_HOUREN; + timalr |= RTC_TIMALR_HOUR(bin2bcd(timeptr->tm_hour)); + } + + return timalr; +} + +static uint32_t rtc_atmel_calalr_from_tm(const struct rtc_time *timeptr, uint32_t mask) +{ + uint32_t calalr = RTC_CALALR_MONTH(1) | RTC_CALALR_DATE(1); + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + calalr |= RTC_CALALR_MTHEN; + calalr |= RTC_CALALR_MONTH(bin2bcd(timeptr->tm_mon + 1)); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + calalr |= RTC_CALALR_DATEEN; + calalr |= RTC_CALALR_DATE(bin2bcd(timeptr->tm_mday)); + } + + return calalr; +} + +static uint32_t rtc_sam_alarm_mask_from_timalr(uint32_t timalr) +{ + uint32_t mask = 0; + + if (timalr & RTC_TIMALR_SECEN) { + mask |= RTC_ALARM_TIME_MASK_SECOND; + } + + if (timalr & RTC_TIMALR_MINEN) { + mask |= RTC_ALARM_TIME_MASK_MINUTE; + } + + if (timalr & RTC_TIMALR_HOUREN) { + mask |= RTC_ALARM_TIME_MASK_HOUR; + } + + return mask; +} + +static uint32_t rtc_sam_alarm_mask_from_calalr(uint32_t calalr) +{ + uint32_t mask = 0; + + if (calalr & RTC_CALALR_MTHEN) { + mask |= RTC_ALARM_TIME_MASK_MONTH; + } + + if (calalr & RTC_CALALR_DATEEN) { + mask |= RTC_ALARM_TIME_MASK_MONTHDAY; + } + + return mask; +} + +static void rtc_sam_tm_from_timalr_calalr(struct rtc_time *timeptr, uint32_t mask, + uint32_t timalr, uint32_t calalr) +{ + memset(timeptr, 0x00, sizeof(*timeptr)); + + if (mask & RTC_ALARM_TIME_MASK_SECOND) { + timeptr->tm_sec = bcd2bin(RTC_SAM_REG_GET_FIELD(timalr, TIMALR_SEC)); + } + + if (mask & RTC_ALARM_TIME_MASK_MINUTE) { + timeptr->tm_min = bcd2bin(RTC_SAM_REG_GET_FIELD(timalr, TIMALR_MIN)); + } + + if (mask & RTC_ALARM_TIME_MASK_HOUR) { + timeptr->tm_hour = bcd2bin(RTC_SAM_REG_GET_FIELD(timalr, TIMALR_HOUR)); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) { + timeptr->tm_mday = bcd2bin(RTC_SAM_REG_GET_FIELD(calalr, CALALR_DATE)); + } + + if (mask & RTC_ALARM_TIME_MASK_MONTH) { + timeptr->tm_mon = bcd2bin(RTC_SAM_REG_GET_FIELD(calalr, CALALR_MONTH)) - 1; + } +} + +static int rtc_sam_alarm_get_supported_fields(const struct device *dev, uint16_t id, + uint16_t *mask) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id); + + *mask = rtc_sam_alarm_get_supported_mask(); + return 0; +} + +static int rtc_sam_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask, + const struct rtc_time *timeptr) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + uint32_t timalr; + uint32_t calalr; + uint32_t mask_supported; + + mask_supported = rtc_sam_alarm_get_supported_mask(); + + if ((id != 0)) { + return -EINVAL; + } + + if ((mask > 0) && (timeptr == NULL)) { + return -EINVAL; + } + + if (mask & ~mask_supported) { + return -EINVAL; + } + + if (rtc_sam_validate_tm(timeptr, mask) == false) { + return -EINVAL; + } + + timalr = rtc_atmel_timalr_from_tm(timeptr, mask); + calalr = rtc_atmel_calalr_from_tm(timeptr, mask); + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + irq_disable(config->irq_num); + rtc_sam_disable_wp(); + + /* Set RTC alarm time */ + regs->RTC_TIMALR = timalr; + regs->RTC_CALALR = calalr; + + rtc_sam_enable_wp(); + + /* Clear alarm pending status */ + regs->RTC_SCCR = RTC_SCCR_ALRCLR; + + irq_enable(config->irq_num); + k_spin_unlock(&data->lock, key); + return 0; +} + +static int rtc_sam_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask, + struct rtc_time *timeptr) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + uint32_t timalr; + uint32_t calalr; + + if ((id != 0) || (mask == NULL) || (timeptr == NULL)) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + timalr = regs->RTC_TIMALR; + calalr = regs->RTC_CALALR; + + k_spin_unlock(&data->lock, key); + + *mask = rtc_sam_alarm_mask_from_timalr(timalr); + *mask |= rtc_sam_alarm_mask_from_calalr(calalr); + + rtc_sam_tm_from_timalr_calalr(timeptr, *mask, timalr, calalr); + return 0; +} + +static int rtc_sam_alarm_is_pending(const struct device *dev, uint16_t id) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + if (id != 0) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + if ((regs->RTC_SR & RTC_SR_ALARM) == 0) { + k_spin_unlock(&data->lock, key); + + return 0; + } + + regs->RTC_SCCR = RTC_SCCR_ALRCLR; + k_spin_unlock(&data->lock, key); + return 1; +} + +static int rtc_sam_alarm_set_callback(const struct device *dev, uint16_t id, + rtc_alarm_callback callback, void *user_data) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + if (id != 0) { + return -EINVAL; + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + irq_disable(config->irq_num); + data->alarm_callback = callback; + data->alarm_user_data = user_data; + + if (data->alarm_callback) { + regs->RTC_IER = RTC_IER_ALREN; + } else { + regs->RTC_IDR = RTC_IDR_ALRDIS; + } + + irq_enable(config->irq_num); + k_spin_unlock(&data->lock, key); + return 0; +} +#endif /* CONFIG_RTC_ALARM */ + +#ifdef CONFIG_RTC_UPDATE +static int rtc_sam_update_set_callback(const struct device *dev, rtc_update_callback callback, + void *user_data) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + irq_disable(config->irq_num); + + data->update_callback = callback; + data->update_user_data = user_data; + + if (data->update_callback) { + regs->RTC_IER = RTC_IER_SECEN; + } else { + regs->RTC_IDR = RTC_IDR_SECDIS; + } + + irq_enable(config->irq_num); + + k_spin_unlock(&data->lock, key); + + return 0; +} +#endif /* CONFIG_RTC_UPDATE */ + +#ifdef CONFIG_RTC_CALIBRATION +static int rtc_sam_set_calibration(const struct device *dev, int32_t calibration) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + bool negative_calibration; + bool high_calibration; + uint32_t slow_clock_calibration; + uint32_t mr; + + if ((calibration < RTC_SAM_CALIBRATE_PPB_MIN) || + (calibration > RTC_SAM_CALIBRATE_PPB_MAX)) { + return -EINVAL; + } + + /* The value written to the register is absolute */ + if (calibration < 0) { + negative_calibration = true; + calibration = -calibration; + } else { + negative_calibration = false; + } + + /* + * Formula adapted from + * Atmel-11157-32-bit-Cortex-M4-Microcontroller-SAM4E16-SAM4E8_Datasheet.pdf + * section 15.6.2 + * + * Formula if RTC_MR_HIGHPPM is 0 + * + * RTC_MR_CORRECTION = (3906 / (20 * ppm)) - 1 + * + * Formula if RTC_MR_HIGHPPM is 1 + * + * RTC_MR_CORRECTION = (3906 / ppm) - 1 + * + * Since we are working with ppb, we adapt the formula by increasing the + * terms of the fraction by 1000, turning the ppm into ppb + * + * Adapted formula if RTC_MR_HIGHPPM is 0 + * + * RTC_MR_CORRECTION = (3906000 / (20 * ppb)) - 1 + * + * Adapted formula if RTC_MR_HIGHPPM is 1 + * + * RTC_MR_CORRECTION = (3906000 / ppb) - 1 + */ + if (calibration < RTC_SAM_CALIBRATE_PPB_QUANTA) { + high_calibration = false; + slow_clock_calibration = 0; + } else if (calibration < RTC_SAM_CALIBRATE_PPB_LOW_SCALE) { + high_calibration = false; + slow_clock_calibration = (uint32_t)((3906000 / (20 * calibration)) - 1); + } else { + high_calibration = true; + slow_clock_calibration = (uint32_t)((3906000 / calibration) - 1); + } + + k_spinlock_key_t key = k_spin_lock(&data->lock); + + rtc_sam_disable_wp(); + + mr = regs->RTC_MR; + + if (negative_calibration == true) { + mr |= RTC_MR_NEGPPM; + } else { + mr &= ~RTC_MR_NEGPPM; + } + + mr &= ~RTC_MR_CORRECTION_Msk; + mr |= RTC_MR_CORRECTION(slow_clock_calibration); + + if (high_calibration == true) { + mr |= RTC_MR_HIGHPPM; + } else { + mr &= ~RTC_MR_HIGHPPM; + } + + regs->RTC_MR = mr; + + rtc_sam_enable_wp(); + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int rtc_sam_get_calibration(const struct device *dev, int32_t *calibration) +{ + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + uint32_t mr; + int32_t correction; + + if (calibration == NULL) { + return -EINVAL; + } + + mr = regs->RTC_MR; + + correction = (int32_t)RTC_SAM_REG_GET_FIELD(mr, MR_CORRECTION); + + /* Formula documented in rtc_sam_set_calibration() */ + if (correction == 0) { + *calibration = 0; + } else if (mr & RTC_MR_HIGHPPM) { + *calibration = 3906000 / (correction + 1); + } else { + *calibration = 3906000 / ((correction + 1) * 20); + } + + if (mr & RTC_MR_NEGPPM) { + *calibration = -*calibration; + } + + return 0; +} +#endif /* CONFIG_RTC_CALIBRATION */ + +static struct rtc_driver_api rtc_sam_driver_api = { + .set_time = rtc_sam_set_time, + .get_time = rtc_sam_get_time, +#ifdef CONFIG_RTC_ALARM + .alarm_get_supported_fields = rtc_sam_alarm_get_supported_fields, + .alarm_set_time = rtc_sam_alarm_set_time, + .alarm_get_time = rtc_sam_alarm_get_time, + .alarm_is_pending = rtc_sam_alarm_is_pending, + .alarm_set_callback = rtc_sam_alarm_set_callback, +#endif /* CONFIG_RTC_ALARM */ +#ifdef CONFIG_RTC_UPDATE + .update_set_callback = rtc_sam_update_set_callback, +#endif /* CONFIG_RTC_UPDATE */ +#ifdef CONFIG_RTC_CALIBRATION + .set_calibration = rtc_sam_set_calibration, + .get_calibration = rtc_sam_get_calibration, +#endif /* CONFIG_RTC_CALIBRATION */ +}; + +static int rtc_sam_init(const struct device *dev) +{ + struct rtc_sam_data *data = dev->data; + const struct rtc_sam_config *config = dev->config; + Rtc *regs = config->regs; + + rtc_sam_disable_wp(); + regs->RTC_MR &= ~(RTC_MR_HRMOD | RTC_MR_PERSIAN); + regs->RTC_CR = 0; + rtc_sam_enable_wp(); + regs->RTC_IDR = (RTC_IDR_ACKDIS + | RTC_IDR_ALRDIS + | RTC_IDR_SECDIS + | RTC_IDR_TIMDIS + | RTC_IDR_CALDIS + | RTC_IDR_TDERRDIS); + + k_sem_init(&data->cr_sec_evt_sem, 0, 1); + k_sem_init(&data->cr_upd_ack_sem, 0, 1); + config->irq_init_fn_ptr(); + irq_enable(config->irq_num); + return 0; +} + +#define RTC_SAM_DEVICE(id) \ + static void rtc_sam_irq_init_##id(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), \ + rtc_sam_isr, DEVICE_DT_INST_GET(id), 0); \ + } \ + \ + static const struct rtc_sam_config rtc_sam_config_##id = { \ + .regs = (Rtc *)DT_INST_REG_ADDR(id), \ + .irq_num = DT_INST_IRQN(id), \ + .irq_init_fn_ptr = rtc_sam_irq_init_##id, \ + }; \ + \ + static struct rtc_sam_data rtc_sam_data_##id; \ + \ + DEVICE_DT_INST_DEFINE(id, rtc_sam_init, NULL, \ + &rtc_sam_data_##id, \ + &rtc_sam_config_##id, POST_KERNEL, \ + CONFIG_RTC_INIT_PRIORITY, \ + &rtc_sam_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(RTC_SAM_DEVICE); diff --git a/dts/bindings/rtc/atmel,sam-rtc.yaml b/dts/bindings/rtc/atmel,sam-rtc.yaml new file mode 100644 index 00000000000..7e787ebaf10 --- /dev/null +++ b/dts/bindings/rtc/atmel,sam-rtc.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2023 Bjarki Arge Andreasen +# SPDX-License-Identifier: Apache-2.0 + +description: Atmel SAM family RTC device + +compatible: "atmel,sam-rtc" + +include: rtc-device.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + required: true