drivers: rtc: smartbond: Support RTC peripheral.
Add support for the RTC peripheral. Signed-off-by: Ioannis Karachalios <ioannis.karachalios.px@renesas.com>
This commit is contained in:
parent
0e67c990cd
commit
9f76879a0b
5 changed files with 667 additions and 0 deletions
|
@ -14,3 +14,4 @@ zephyr_library_sources_ifdef(CONFIG_RTC_MOTOROLA_MC146818 rtc_mc146818.c)
|
|||
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)
|
||||
|
|
|
@ -48,5 +48,6 @@ source "drivers/rtc/Kconfig.pcf8523"
|
|||
source "drivers/rtc/Kconfig.pcf8563"
|
||||
source "drivers/rtc/Kconfig.mc146818"
|
||||
source "drivers/rtc/Kconfig.stm32"
|
||||
source "drivers/rtc/Kconfig.smartbond"
|
||||
|
||||
endif # RTC
|
||||
|
|
9
drivers/rtc/Kconfig.smartbond
Normal file
9
drivers/rtc/Kconfig.smartbond
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Copyright (c) 2023 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config RTC_SMARTBOND
|
||||
bool "Smartbond RTC driver"
|
||||
depends on DT_HAS_RENESAS_SMARTBOND_RTC_ENABLED
|
||||
default y
|
||||
help
|
||||
Build RTC driver for Smartbond SoCs.
|
641
drivers/rtc/rtc_smartbond.c
Normal file
641
drivers/rtc/rtc_smartbond.c
Normal file
|
@ -0,0 +1,641 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Renesas Electronics Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/rtc.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/clock_control/smartbond_clock_control.h>
|
||||
#include <DA1469xAB.h>
|
||||
#include <da1469x_config.h>
|
||||
#include <da1469x_pdc.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(rtc_smartbond, CONFIG_RTC_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT renesas_smartbond_rtc
|
||||
|
||||
#define SMARTBOND_IRQN DT_INST_IRQN(0)
|
||||
#define SMARTBOND_IRQ_PRIO DT_INST_IRQ(0, priority)
|
||||
|
||||
#define RTC_ALARMS_COUNT DT_PROP(DT_NODELABEL(rtc), alarms_count)
|
||||
|
||||
#define TM_YEAR_REF 1900
|
||||
#define RTC_DIV_DENOM_1000 0
|
||||
#define RTC_DIV_DENOM_1024 1
|
||||
|
||||
#define RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS \
|
||||
(RTC_ALARM_TIME_MASK_SECOND | RTC_ALARM_TIME_MASK_MINUTE | RTC_ALARM_TIME_MASK_HOUR | \
|
||||
RTC_ALARM_TIME_MASK_MONTH | RTC_ALARM_TIME_MASK_MONTHDAY)
|
||||
|
||||
#define RTC_TIME_REG_SET_FIELD(_field, _var, _val) \
|
||||
((_var) = \
|
||||
((_var) & ~(RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk)) | \
|
||||
(((_val) << RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Pos) & \
|
||||
(RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk)))
|
||||
|
||||
#define RTC_CALENDAR_REG_SET_FIELD(_field, _var, _val) \
|
||||
((_var) = \
|
||||
((_var) & ~(RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk)) | \
|
||||
(((_val) << RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Pos) & \
|
||||
(RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk)))
|
||||
|
||||
#define RTC_CALENDAR_ALARM_REG_SET_FIELD(_field, _var, _val) \
|
||||
((_var) = \
|
||||
((_var) & ~(RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk)) | \
|
||||
(((_val) << RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Pos) & \
|
||||
(RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk)))
|
||||
|
||||
#define RTC_TIME_ALARM_REG_SET_FIELD(_field, _var, _val) \
|
||||
((_var) = \
|
||||
((_var) & ~(RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk)) | \
|
||||
(((_val) << RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Pos) & \
|
||||
(RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk)))
|
||||
|
||||
#define RTC_TIME_REG_GET_FIELD(_field, _var) \
|
||||
(((_var) & (RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Msk)) >> \
|
||||
RTC_RTC_TIME_REG_RTC_TIME_ ## _field ## _U_Pos)
|
||||
|
||||
#define RTC_CALENDAR_REG_GET_FIELD(_field, _var) \
|
||||
(((_var) & (RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Msk)) >> \
|
||||
RTC_RTC_CALENDAR_REG_RTC_CAL_ ## _field ## _U_Pos)
|
||||
|
||||
#define RTC_CALENDAR_ALARM_REG_GET_FIELD(_field, _var) \
|
||||
(((_var) & (RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Msk)) >> \
|
||||
RTC_RTC_CALENDAR_ALARM_REG_RTC_CAL_ ## _field ## _U_Pos)
|
||||
|
||||
#define RTC_TIME_ALARM_REG_GET_FIELD(_field, _var) \
|
||||
(((_var) & (RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _T_Msk | \
|
||||
RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Msk)) >> \
|
||||
RTC_RTC_TIME_ALARM_REG_RTC_TIME_ ## _field ## _U_Pos)
|
||||
|
||||
#define CLK_RTCDIV_REG_SET_FIELD(_field, _var, _val) \
|
||||
((_var) = \
|
||||
((_var) & ~CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Msk) | \
|
||||
(((_val) << CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Pos) & \
|
||||
CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ ## _field ## _Msk))
|
||||
|
||||
struct rtc_smartbond_data {
|
||||
struct k_mutex lock;
|
||||
bool is_rtc_configured;
|
||||
#if defined(CONFIG_RTC_ALARM)
|
||||
volatile bool is_alarm_pending;
|
||||
rtc_alarm_callback alarm_cb;
|
||||
void *alarm_user_data;
|
||||
#endif
|
||||
#if defined(CONFIG_RTC_UPDATE)
|
||||
rtc_update_callback update_cb;
|
||||
void *update_user_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
|
||||
static void smartbond_rtc_isr(const struct device *dev)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
/* Exercise which events asserted the RTC IRQ line. Register is cleared upon read. */
|
||||
uint32_t rtc_event_flags_reg = RTC->RTC_EVENT_FLAGS_REG;
|
||||
/* RTC_EVENT_FLASH_REG will be updated regardless of the interrupt mask. */
|
||||
uint32_t rtc_interrupt_mask_reg = RTC->RTC_INTERRUPT_MASK_REG;
|
||||
|
||||
#if defined(CONFIG_RTC_ALARM)
|
||||
if ((rtc_event_flags_reg & RTC_RTC_EVENT_FLAGS_REG_RTC_EVENT_ALRM_Msk) &&
|
||||
!(rtc_interrupt_mask_reg & RTC_RTC_INTERRUPT_MASK_REG_RTC_ALRM_INT_MSK_Msk)) {
|
||||
if (data->alarm_cb) {
|
||||
data->alarm_cb(dev, 0, data->alarm_user_data);
|
||||
data->is_alarm_pending = false;
|
||||
} else {
|
||||
data->is_alarm_pending = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RTC_UPDATE)
|
||||
if ((rtc_event_flags_reg & RTC_RTC_EVENT_FLAGS_REG_RTC_EVENT_SEC_Msk) &&
|
||||
!(rtc_interrupt_mask_reg & RTC_RTC_INTERRUPT_MASK_REG_RTC_SEC_INT_MSK_Msk)) {
|
||||
if (data->update_cb) {
|
||||
data->update_cb(dev, data->update_user_data);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void rtc_smartbond_set_status(bool status)
|
||||
{
|
||||
if (status) {
|
||||
CRG_TOP->CLK_RTCDIV_REG |= CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ENABLE_Msk;
|
||||
RTC->RTC_CONTROL_REG = 0;
|
||||
} else {
|
||||
RTC->RTC_CONTROL_REG = (RTC_RTC_CONTROL_REG_RTC_CAL_DISABLE_Msk |
|
||||
RTC_RTC_CONTROL_REG_RTC_TIME_DISABLE_Msk);
|
||||
CRG_TOP->CLK_RTCDIV_REG &= ~CRG_TOP_CLK_RTCDIV_REG_RTC_DIV_ENABLE_Msk;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t rtc_time_to_bcd(const struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_time_reg = 0;
|
||||
|
||||
RTC_TIME_REG_SET_FIELD(S, rtc_time_reg, bin2bcd(timeptr->tm_sec)); /*[0, 59]*/
|
||||
RTC_TIME_REG_SET_FIELD(M, rtc_time_reg, bin2bcd(timeptr->tm_min)); /*[0, 59]*/
|
||||
RTC_TIME_REG_SET_FIELD(HR, rtc_time_reg, bin2bcd(timeptr->tm_hour)); /*[0, 23]*/
|
||||
|
||||
return rtc_time_reg;
|
||||
}
|
||||
|
||||
static uint32_t rtc_calendar_to_bcd(const struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_calendar_reg = 0;
|
||||
|
||||
RTC_CALENDAR_REG_SET_FIELD(D, rtc_calendar_reg, bin2bcd(timeptr->tm_mday)); /*[1, 31]*/
|
||||
RTC_CALENDAR_REG_SET_FIELD(Y, rtc_calendar_reg,
|
||||
bin2bcd((timeptr->tm_year + TM_YEAR_REF) % 100)); /*[year - 1900]*/
|
||||
RTC_CALENDAR_REG_SET_FIELD(C, rtc_calendar_reg,
|
||||
bin2bcd((timeptr->tm_year + TM_YEAR_REF) / 100));
|
||||
RTC_CALENDAR_REG_SET_FIELD(M, rtc_calendar_reg, bin2bcd(timeptr->tm_mon + 1)); /*[0, 11]*/
|
||||
|
||||
if (timeptr->tm_wday != -1) {
|
||||
rtc_calendar_reg |= ((timeptr->tm_wday + 1) &
|
||||
RTC_RTC_CALENDAR_REG_RTC_DAY_Msk); /*[0, 6]*/
|
||||
}
|
||||
|
||||
return rtc_calendar_reg;
|
||||
}
|
||||
|
||||
static void bcd_to_rtc_time(struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_time_reg = RTC->RTC_TIME_REG;
|
||||
|
||||
timeptr->tm_sec = bcd2bin(RTC_TIME_REG_GET_FIELD(S, rtc_time_reg));
|
||||
timeptr->tm_min = bcd2bin(RTC_TIME_REG_GET_FIELD(M, rtc_time_reg));
|
||||
timeptr->tm_hour = bcd2bin(RTC_TIME_REG_GET_FIELD(HR, rtc_time_reg));
|
||||
|
||||
timeptr->tm_nsec = 0; /*Unknown*/
|
||||
}
|
||||
|
||||
static void bcd_to_rtc_calendar(struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_calendar_reg = RTC->RTC_CALENDAR_REG;
|
||||
|
||||
timeptr->tm_mday = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(D, rtc_calendar_reg));
|
||||
timeptr->tm_mon = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(M, rtc_calendar_reg)) - 1;
|
||||
timeptr->tm_year = bcd2bin(RTC_CALENDAR_REG_GET_FIELD(Y, rtc_calendar_reg)) +
|
||||
(bcd2bin(RTC_CALENDAR_REG_GET_FIELD(C, rtc_calendar_reg)) * 100) - TM_YEAR_REF;
|
||||
timeptr->tm_wday = (rtc_calendar_reg & RTC_RTC_CALENDAR_REG_RTC_DAY_Msk) - 1;
|
||||
|
||||
timeptr->tm_yday = timeptr->tm_isdst = -1; /*Unknown*/
|
||||
}
|
||||
|
||||
static int rtc_smartbond_set_time(const struct device *dev, const struct rtc_time *timeptr)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
int ret = 0;
|
||||
uint32_t rtc_time_reg, rtc_calendar_reg, rtc_status_reg;
|
||||
|
||||
if (timeptr == NULL) {
|
||||
LOG_ERR("No pointer is provided to set time");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (timeptr->tm_year + TM_YEAR_REF < TM_YEAR_REF) {
|
||||
LOG_ERR("RTC time exceeds HW capabilities");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((timeptr->tm_yday != -1) || (timeptr->tm_isdst != -1) || (timeptr->tm_nsec != 0)) {
|
||||
LOG_WRN("Unsupported RTC sub-values");
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
rtc_smartbond_set_status(false);
|
||||
|
||||
/* Store current counter values as it might happen that the requested time is not valid */
|
||||
rtc_time_reg = RTC->RTC_TIME_REG;
|
||||
rtc_calendar_reg = RTC->RTC_CALENDAR_REG;
|
||||
|
||||
RTC->RTC_TIME_REG = rtc_time_to_bcd(timeptr);
|
||||
RTC->RTC_CALENDAR_REG = rtc_calendar_to_bcd(timeptr);
|
||||
|
||||
/* Check if the new values were valid, otherwise reset back to the previous ones. */
|
||||
rtc_status_reg = RTC->RTC_STATUS_REG;
|
||||
if (!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_CAL_Msk) ||
|
||||
!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_TIME_Msk)) {
|
||||
RTC->RTC_TIME_REG = rtc_time_reg;
|
||||
RTC->RTC_CALENDAR_REG = rtc_calendar_reg;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
/* Mark the very first valid RTC configuration; used to check if RTC contains valid data. */
|
||||
if (!data->is_rtc_configured && (ret == 0)) {
|
||||
data->is_rtc_configured = true;
|
||||
}
|
||||
|
||||
/* It might happen that the very first time RTC is not configured correctly; do not care. */
|
||||
rtc_smartbond_set_status(true);
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_get_time(const struct device *dev, struct rtc_time *timeptr)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
|
||||
if (timeptr == NULL) {
|
||||
LOG_ERR("No pointer is provided to store the requested time");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!data->is_rtc_configured) {
|
||||
LOG_ERR("RTC is not initialized yet");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
/* Stop RTC counters to obtain coherent data. */
|
||||
rtc_smartbond_set_status(false);
|
||||
|
||||
bcd_to_rtc_time(timeptr);
|
||||
bcd_to_rtc_calendar(timeptr);
|
||||
|
||||
rtc_smartbond_set_status(true);
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_RTC_ALARM)
|
||||
BUILD_ASSERT(RTC_ALARMS_COUNT, "At least one alarm event should be supported");
|
||||
|
||||
/* Define a valid calendar value as a zero sub-field is not valid for the alarm calendar value */
|
||||
static uint32_t alarm_calendar_to_bcd(const struct rtc_time *timeptr, uint16_t mask)
|
||||
{
|
||||
uint32_t rtc_calendar_alarm_reg = 0x0108;
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
|
||||
RTC_CALENDAR_ALARM_REG_SET_FIELD(D, rtc_calendar_alarm_reg,
|
||||
bin2bcd(timeptr->tm_mday));
|
||||
}
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_MONTH) {
|
||||
RTC_CALENDAR_ALARM_REG_SET_FIELD(M, rtc_calendar_alarm_reg,
|
||||
bin2bcd(timeptr->tm_mon + 1));
|
||||
}
|
||||
|
||||
return rtc_calendar_alarm_reg;
|
||||
}
|
||||
|
||||
/* No need to parse the alarm mask as a zero sub-field is valid for the alarm time counter. */
|
||||
static inline uint32_t alarm_time_to_bcd(const struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_time_alarm_reg = 0;
|
||||
|
||||
RTC_TIME_ALARM_REG_SET_FIELD(S, rtc_time_alarm_reg, bin2bcd(timeptr->tm_sec)); /*[0, 59]*/
|
||||
RTC_TIME_ALARM_REG_SET_FIELD(M, rtc_time_alarm_reg, bin2bcd(timeptr->tm_min)); /*[0, 59]*/
|
||||
RTC_TIME_ALARM_REG_SET_FIELD(HR, rtc_time_alarm_reg, bin2bcd(timeptr->tm_hour)); /*[0, 23]*/
|
||||
|
||||
return rtc_time_alarm_reg;
|
||||
}
|
||||
|
||||
static void bcd_to_alarm_calendar(struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_calendar_alarm_reg = RTC->RTC_CALENDAR_ALARM_REG;
|
||||
|
||||
timeptr->tm_mday = bcd2bin(RTC_CALENDAR_ALARM_REG_GET_FIELD(D, rtc_calendar_alarm_reg));
|
||||
timeptr->tm_mon = bcd2bin(RTC_CALENDAR_ALARM_REG_GET_FIELD(M, rtc_calendar_alarm_reg)) - 1;
|
||||
|
||||
timeptr->tm_yday = timeptr->tm_wday = timeptr->tm_isdst = timeptr->tm_year = -1;
|
||||
}
|
||||
|
||||
static void bcd_to_alarm_time(struct rtc_time *timeptr)
|
||||
{
|
||||
uint32_t rtc_time_alarm_reg = RTC->RTC_TIME_ALARM_REG;
|
||||
|
||||
timeptr->tm_sec = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(S, rtc_time_alarm_reg));
|
||||
timeptr->tm_min = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(M, rtc_time_alarm_reg));
|
||||
timeptr->tm_hour = bcd2bin(RTC_TIME_ALARM_REG_GET_FIELD(HR, rtc_time_alarm_reg));
|
||||
|
||||
timeptr->tm_nsec = 0;
|
||||
}
|
||||
|
||||
static uint32_t tm_to_rtc_alarm_mask(uint16_t mask)
|
||||
{
|
||||
uint32_t rtc_alarm_enable_reg = 0;
|
||||
|
||||
if (mask & RTC_ALARM_TIME_MASK_SECOND) {
|
||||
rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_SEC_EN_Msk;
|
||||
}
|
||||
if (mask & RTC_ALARM_TIME_MASK_MINUTE) {
|
||||
rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MIN_EN_Msk;
|
||||
}
|
||||
if (mask & RTC_ALARM_TIME_MASK_HOUR) {
|
||||
rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_HOUR_EN_Msk;
|
||||
}
|
||||
if (mask & RTC_ALARM_TIME_MASK_MONTH) {
|
||||
rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MNTH_EN_Msk;
|
||||
}
|
||||
if (mask & RTC_ALARM_TIME_MASK_MONTHDAY) {
|
||||
rtc_alarm_enable_reg |= RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_DATE_EN_Msk;
|
||||
}
|
||||
|
||||
return rtc_alarm_enable_reg;
|
||||
}
|
||||
|
||||
static uint16_t rtc_to_tm_alarm_mask(uint32_t rtc_alarm_enable_reg)
|
||||
{
|
||||
uint16_t mask = 0;
|
||||
|
||||
if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_SEC_EN_Msk) {
|
||||
mask |= RTC_ALARM_TIME_MASK_SECOND;
|
||||
}
|
||||
if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MIN_EN_Msk) {
|
||||
mask |= RTC_ALARM_TIME_MASK_MINUTE;
|
||||
}
|
||||
if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_HOUR_EN_Msk) {
|
||||
mask |= RTC_ALARM_TIME_MASK_HOUR;
|
||||
}
|
||||
if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_MNTH_EN_Msk) {
|
||||
mask |= RTC_ALARM_TIME_MASK_MONTH;
|
||||
}
|
||||
if (rtc_alarm_enable_reg & RTC_RTC_ALARM_ENABLE_REG_RTC_ALARM_DATE_EN_Msk) {
|
||||
mask |= RTC_ALARM_TIME_MASK_MONTHDAY;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_alarm_set_time(const struct device *dev, uint16_t id, uint16_t mask,
|
||||
const struct rtc_time *timeptr)
|
||||
{
|
||||
int ret = 0;
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
uint32_t rtc_time_alarm_reg;
|
||||
uint32_t rtc_calendar_alarm_reg;
|
||||
uint32_t rtc_alarm_enable_reg;
|
||||
uint32_t rtc_status_reg;
|
||||
|
||||
if (id >= RTC_ALARMS_COUNT) {
|
||||
LOG_ERR("Alarm id is out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mask & ~RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS) {
|
||||
LOG_ERR("Invalid alarm mask");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((timeptr == NULL) && (mask != 0)) {
|
||||
LOG_ERR("No pointer is provided to set alarm");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!data->is_rtc_configured) {
|
||||
LOG_WRN("RTC is not initialized yet");
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
|
||||
rtc_alarm_enable_reg = RTC->RTC_ALARM_ENABLE_REG;
|
||||
|
||||
/* Disable alarm to obtain coherency and/or when the alarm mask is empty */
|
||||
RTC->RTC_ALARM_ENABLE_REG = 0;
|
||||
RTC->RTC_INTERRUPT_DISABLE_REG = RTC_RTC_INTERRUPT_DISABLE_REG_RTC_ALRM_INT_DIS_Msk;
|
||||
|
||||
if (mask) {
|
||||
/* Store current counter values as it might happen requested alrm is not valid */
|
||||
rtc_time_alarm_reg = RTC->RTC_TIME_ALARM_REG;
|
||||
rtc_calendar_alarm_reg = RTC->RTC_CALENDAR_ALARM_REG;
|
||||
|
||||
RTC->RTC_TIME_ALARM_REG = alarm_time_to_bcd(timeptr);
|
||||
RTC->RTC_CALENDAR_ALARM_REG = alarm_calendar_to_bcd(timeptr, mask);
|
||||
|
||||
rtc_status_reg = RTC->RTC_STATUS_REG;
|
||||
if (!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_CAL_ALM_Msk) ||
|
||||
!(rtc_status_reg & RTC_RTC_STATUS_REG_RTC_VALID_TIME_ALM_Msk)) {
|
||||
RTC->RTC_TIME_ALARM_REG = rtc_time_alarm_reg;
|
||||
RTC->RTC_CALENDAR_ALARM_REG = rtc_calendar_alarm_reg;
|
||||
RTC->RTC_ALARM_ENABLE_REG = rtc_alarm_enable_reg;
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
RTC->RTC_ALARM_ENABLE_REG = tm_to_rtc_alarm_mask(mask);
|
||||
}
|
||||
|
||||
RTC->RTC_INTERRUPT_ENABLE_REG = RTC_RTC_INTERRUPT_ENABLE_REG_RTC_ALRM_INT_EN_Msk;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_alarm_get_time(const struct device *dev, uint16_t id, uint16_t *mask,
|
||||
struct rtc_time *timeptr)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
|
||||
if (id >= RTC_ALARMS_COUNT) {
|
||||
LOG_ERR("Alarm id is out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((timeptr == NULL) || (mask == NULL)) {
|
||||
LOG_ERR("No pointer is provided to store the requested alarm time/mask");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!data->is_rtc_configured) {
|
||||
LOG_WRN("RTC is not initialized yet");
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
|
||||
bcd_to_alarm_calendar(timeptr);
|
||||
bcd_to_alarm_time(timeptr);
|
||||
*mask = rtc_to_tm_alarm_mask(RTC->RTC_ALARM_ENABLE_REG);
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_alarm_is_pending(const struct device *dev, uint16_t id)
|
||||
{
|
||||
unsigned int key;
|
||||
int status;
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
|
||||
if (id >= RTC_ALARMS_COUNT) {
|
||||
LOG_ERR("Alarm id is out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Globally disable interrupts as the status flag can be updated within ISR */
|
||||
key = DA1469X_IRQ_DISABLE();
|
||||
status = data->is_alarm_pending;
|
||||
/* After reading, the alarm status should be cleared. */
|
||||
data->is_alarm_pending = 0;
|
||||
DA1469X_IRQ_ENABLE(key);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_alarm_set_callback(const struct device *dev, uint16_t id,
|
||||
rtc_alarm_callback callback, void *user_data)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
|
||||
if (id >= RTC_ALARMS_COUNT) {
|
||||
LOG_ERR("Alarm id is out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
|
||||
data->alarm_cb = callback;
|
||||
data->alarm_user_data = user_data;
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_alarm_get_supported_fields(const struct device *dev, uint16_t id,
|
||||
uint16_t *mask)
|
||||
{
|
||||
if (id >= RTC_ALARMS_COUNT) {
|
||||
LOG_ERR("Alarm id is out of range");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mask == NULL) {
|
||||
LOG_ERR("Pointer to store the mask value is missed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*mask = (uint16_t)RTC_SMARTBOND_SUPPORTED_ALARM_FIELDS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_RTC_UPDATE)
|
||||
static int rtc_smartbond_update_set_callback(const struct device *dev, rtc_update_callback callback,
|
||||
void *user_data)
|
||||
{
|
||||
struct rtc_smartbond_data *data = dev->data;
|
||||
|
||||
k_mutex_lock(&data->lock, K_FOREVER);
|
||||
|
||||
data->update_cb = callback;
|
||||
data->update_user_data = user_data;
|
||||
|
||||
if (data->update_cb) {
|
||||
/* Enable asserting the RTC interrupt line when the second counter rolls over. */
|
||||
RTC->RTC_INTERRUPT_ENABLE_REG = RTC_RTC_INTERRUPT_ENABLE_REG_RTC_SEC_INT_EN_Msk;
|
||||
} else {
|
||||
RTC->RTC_INTERRUPT_DISABLE_REG = RTC_RTC_INTERRUPT_DISABLE_REG_RTC_SEC_INT_DIS_Msk;
|
||||
}
|
||||
|
||||
k_mutex_unlock(&data->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct rtc_driver_api rtc_smartbond_driver_api = {
|
||||
.get_time = rtc_smartbond_get_time,
|
||||
.set_time = rtc_smartbond_set_time,
|
||||
#if defined(CONFIG_RTC_ALARM)
|
||||
.alarm_get_time = rtc_smartbond_alarm_get_time,
|
||||
.alarm_set_time = rtc_smartbond_alarm_set_time,
|
||||
.alarm_is_pending = rtc_smartbond_alarm_is_pending,
|
||||
.alarm_set_callback = rtc_smartbond_alarm_set_callback,
|
||||
.alarm_get_supported_fields = rtc_smartbond_alarm_get_supported_fields,
|
||||
#endif
|
||||
#if defined(CONFIG_RTC_UPDATE)
|
||||
.update_set_callback = rtc_smartbond_update_set_callback,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void rtc_smartbond_100HZ_clock_cfg(void)
|
||||
{
|
||||
const struct device * const dev = DEVICE_DT_GET(DT_NODELABEL(osc));
|
||||
uint32_t lp_clk_rate;
|
||||
uint32_t clk_rtcdiv_reg;
|
||||
|
||||
if (!device_is_ready(dev)) {
|
||||
__ASSERT_MSG_INFO("Clock device is not ready");
|
||||
}
|
||||
|
||||
if (clock_control_get_rate(dev, (clock_control_subsys_t)SMARTBOND_CLK_LP_CLK,
|
||||
&lp_clk_rate) < 0) {
|
||||
__ASSERT_MSG_INFO("Cannot extract LP clock rate");
|
||||
}
|
||||
|
||||
clk_rtcdiv_reg = CRG_TOP->CLK_RTCDIV_REG;
|
||||
CLK_RTCDIV_REG_SET_FIELD(DENOM, clk_rtcdiv_reg, RTC_DIV_DENOM_1000);
|
||||
CLK_RTCDIV_REG_SET_FIELD(INT, clk_rtcdiv_reg, lp_clk_rate / 100);
|
||||
CLK_RTCDIV_REG_SET_FIELD(FRAC, clk_rtcdiv_reg, (lp_clk_rate % 100) * 10);
|
||||
CRG_TOP->CLK_RTCDIV_REG = clk_rtcdiv_reg;
|
||||
}
|
||||
|
||||
static int rtc_smartbond_init(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
/* Wakeup device from RTC events (alarm/roll over) */
|
||||
#if CONFIG_PM
|
||||
bool is_xtal32m_enabled = DT_NODE_HAS_STATUS(DT_NODELABEL(xtal32m), okay);
|
||||
int pdc_idx = da1469x_pdc_add(MCU_PDC_TRIGGER_RTC_ALARM, MCU_PDC_MASTER_M33,
|
||||
is_xtal32m_enabled ? MCU_PDC_EN_XTAL : 0);
|
||||
|
||||
__ASSERT(pdc_idx >= 0, "Failed to add RTC PDC entry");
|
||||
da1469x_pdc_set(pdc_idx);
|
||||
da1469x_pdc_ack(pdc_idx);
|
||||
#endif
|
||||
|
||||
rtc_smartbond_100HZ_clock_cfg();
|
||||
|
||||
/* Timer and calendar counters will not reset after SW reset */
|
||||
RTC->RTC_KEEP_RTC_REG |= RTC_RTC_KEEP_RTC_REG_RTC_KEEP_Msk;
|
||||
|
||||
#if defined(CONFIG_RTC_ALARM) || defined(CONFIG_RTC_UPDATE)
|
||||
IRQ_CONNECT(SMARTBOND_IRQN, SMARTBOND_IRQ_PRIO, smartbond_rtc_isr,
|
||||
DEVICE_DT_INST_GET(0), 0);
|
||||
irq_enable(SMARTBOND_IRQN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SMARTBOND_RTC_INIT(inst) \
|
||||
BUILD_ASSERT((inst) == 0, "multiple instances are not supported"); \
|
||||
\
|
||||
static struct rtc_smartbond_data rtc_smartbond_data_ ## inst; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(0, rtc_smartbond_init, NULL, \
|
||||
&rtc_smartbond_data_ ## inst, NULL, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_RTC_INIT_PRIORITY, \
|
||||
&rtc_smartbond_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SMARTBOND_RTC_INIT)
|
Loading…
Add table
Add a link
Reference in a new issue