760 lines
18 KiB
C
760 lines
18 KiB
C
/**
|
|
* @file sdrv_rtc.c
|
|
*
|
|
* Copyright (c) 2022 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*
|
|
* Description: sec_rtc driver.
|
|
*
|
|
* Revision History:
|
|
* -----------------
|
|
*/
|
|
|
|
#include <debug.h>
|
|
#include <irq.h>
|
|
#include <irq_num.h>
|
|
|
|
#include "sdrv_rtc.h"
|
|
#include "sdrv_rtc_reg.h"
|
|
#include "udelay/udelay.h"
|
|
|
|
#define RTC_CLK_HZ (32 * 1024ul)
|
|
#define RTC_CLK_SHIFT __builtin_ctz(RTC_CLK_HZ)
|
|
#define RTC_TICK_TO_SECOND(t) ((t) >> RTC_CLK_SHIFT)
|
|
#define RTC_SECOND_TO_TICK(s) ((s) << RTC_CLK_SHIFT)
|
|
#define RTC_TICK_TO_MILLISECOND(t) ((uint64_t)(t) * 1000 / 32768)
|
|
#define RTC_MILLISECOND_TO_TICK(s) ((uint64_t)(s) * 32768 / 1000)
|
|
|
|
#define DELAY_US 240
|
|
|
|
#define SECONDS_PER_MINUTE (60ul)
|
|
#define MINUTES_PER_HOUR (60ul)
|
|
#define HOURS_PER_DAY (24ul)
|
|
#define SECONDS_PER_HOUR (SECONDS_PER_MINUTE * MINUTES_PER_HOUR)
|
|
#define SECONDS_PER_DAY (SECONDS_PER_HOUR * HOURS_PER_DAY)
|
|
|
|
#define LEAP_YEAR_DAYS (366ul)
|
|
#define COMMON_YEAR_DAYS (365ul)
|
|
|
|
#define TM_START_YEAR (1900ul)
|
|
#define EPOCH_START_YEAR (1970ul)
|
|
static const uint32_t month_days[12] = {
|
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
};
|
|
|
|
/**
|
|
* @brief check RTC time is valid.
|
|
*
|
|
* @param [in] tm RTC time.
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
static status_t sdrv_check_rtc_time(struct rtc_time *tm)
|
|
{
|
|
if (tm->tm_subsec > 999u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_sec > 61u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_min > 59u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_hour > 23u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if ((tm->tm_mday < 1u) || (tm->tm_mday > 31u)) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_mon > 11u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_wday > 6u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
if (tm->tm_year > 365u) {
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Simple algorithm to convert RTC time to Epoch seconds.
|
|
*
|
|
* Epoch seconds starts from 1970-1-1, 00:00:01
|
|
*
|
|
* @param [in] tm RTC time.
|
|
* @return uint64_t Number of seconds since Epoch.
|
|
*/
|
|
uint64_t rtc_to_epoch(struct rtc_time *tm)
|
|
{
|
|
uint64_t epoch_sec = 0;
|
|
uint32_t year = tm->tm_year + TM_START_YEAR;
|
|
|
|
if (sdrv_check_rtc_time(tm) < 0) {
|
|
ssdk_printf(SSDK_WARNING, "%s invalid rtc time!\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
for (uint32_t num = EPOCH_START_YEAR; num < year; num++) {
|
|
epoch_sec += SECONDS_PER_DAY *
|
|
(is_leap_year(num) ? LEAP_YEAR_DAYS : COMMON_YEAR_DAYS);
|
|
}
|
|
|
|
for (uint32_t num = 0; num < tm->tm_mon; num++) {
|
|
epoch_sec += month_days[num] * SECONDS_PER_DAY;
|
|
|
|
if (is_leap_year(year) && (num == 1)) {
|
|
epoch_sec += SECONDS_PER_DAY;
|
|
}
|
|
}
|
|
|
|
epoch_sec += (tm->tm_mday - 1) * SECONDS_PER_DAY;
|
|
epoch_sec += tm->tm_hour * SECONDS_PER_HOUR;
|
|
epoch_sec += tm->tm_min * SECONDS_PER_MINUTE;
|
|
epoch_sec += tm->tm_sec;
|
|
|
|
return epoch_sec;
|
|
}
|
|
|
|
/**
|
|
* @brief Simple algorithm to convert epoch seconds to RTC time.
|
|
*
|
|
* Epoch seconds starts from 1970-1-1, 00:00:01
|
|
*
|
|
* @param [out] tm
|
|
* @param [in] epoch_sec
|
|
*/
|
|
status_t epoch_to_rtc(struct rtc_time *tm, uint64_t epoch_sec)
|
|
{
|
|
uint32_t year = EPOCH_START_YEAR;
|
|
uint32_t month = 0;
|
|
uint32_t days = epoch_sec / SECONDS_PER_DAY;
|
|
uint32_t seconds;
|
|
|
|
tm->tm_wday = (4 + days) % 7;
|
|
|
|
while (days >= COMMON_YEAR_DAYS) {
|
|
if (is_leap_year(year)) {
|
|
if (days >= LEAP_YEAR_DAYS) {
|
|
days -= LEAP_YEAR_DAYS;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
days -= COMMON_YEAR_DAYS;
|
|
}
|
|
|
|
year++;
|
|
}
|
|
|
|
tm->tm_year = year - TM_START_YEAR;
|
|
|
|
while (days >= 28) {
|
|
if (is_leap_year(year) && (month == 1)) {
|
|
if (days >= 29) {
|
|
days -= 29;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (days >= month_days[month]) {
|
|
days -= month_days[month];
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
month++;
|
|
}
|
|
|
|
tm->tm_mon = month;
|
|
tm->tm_mday = days + 1;
|
|
|
|
seconds = epoch_sec % SECONDS_PER_DAY;
|
|
tm->tm_hour = seconds / 3600;
|
|
tm->tm_min = (seconds % 3600) / 60;
|
|
tm->tm_sec = (seconds % 3600) % 60;
|
|
|
|
return sdrv_check_rtc_time(tm);
|
|
}
|
|
|
|
/**
|
|
* @brief Simple algorithm to convert RTC time to Epoch milliseconds.
|
|
*
|
|
* Epoch seconds starts from 1970-1-1, 00:00:00.001
|
|
*
|
|
* @param [in] tm RTC time.
|
|
* @return uint64_t number of milliseconds since epoch.
|
|
*/
|
|
uint64_t rtc_to_epoch_ms(struct rtc_time *tm)
|
|
{
|
|
uint64_t epoch_millisec = 0;
|
|
uint32_t year = tm->tm_year + TM_START_YEAR;
|
|
|
|
if (sdrv_check_rtc_time(tm) < 0) {
|
|
ssdk_printf(SSDK_WARNING, "%s invalid rtc time!\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
for (uint32_t num = EPOCH_START_YEAR; num < year; num++) {
|
|
epoch_millisec += (uint64_t)SECONDS_PER_DAY * 1000 *
|
|
(is_leap_year(num) ? LEAP_YEAR_DAYS : COMMON_YEAR_DAYS);
|
|
}
|
|
|
|
for (uint32_t num = 0; num < tm->tm_mon; num++) {
|
|
epoch_millisec += (uint64_t)month_days[num] * SECONDS_PER_DAY * 1000;
|
|
|
|
if (is_leap_year(year) && (num == 1)) {
|
|
epoch_millisec += SECONDS_PER_DAY * 1000;
|
|
}
|
|
}
|
|
|
|
epoch_millisec += (uint64_t)(tm->tm_mday - 1) * SECONDS_PER_DAY * 1000;
|
|
epoch_millisec += (uint64_t)tm->tm_hour * SECONDS_PER_HOUR * 1000;
|
|
epoch_millisec += (uint64_t)tm->tm_min * SECONDS_PER_MINUTE * 1000;
|
|
epoch_millisec += (uint64_t)tm->tm_sec * 1000;
|
|
epoch_millisec += (uint64_t)tm->tm_subsec;
|
|
|
|
return epoch_millisec;
|
|
}
|
|
|
|
/**
|
|
* @brief Simple algorithm to convert epoch milliseconds to RTC time.
|
|
*
|
|
* Epoch milliseconds starts from 1970-1-1, 00:00:00.001
|
|
*
|
|
* @param [out] tm
|
|
* @param [in] epoch_sec
|
|
*/
|
|
status_t epoch_ms_to_rtc(struct rtc_time *tm, uint64_t epoch_millisec)
|
|
{
|
|
uint32_t year = EPOCH_START_YEAR;
|
|
uint32_t month = 0;
|
|
uint32_t days = epoch_millisec / SECONDS_PER_DAY / 1000;
|
|
uint32_t seconds;
|
|
|
|
tm->tm_wday = (4 + days) % 7;
|
|
|
|
while (days >= COMMON_YEAR_DAYS) {
|
|
if (is_leap_year(year)) {
|
|
if (days >= LEAP_YEAR_DAYS) {
|
|
days -= LEAP_YEAR_DAYS;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
days -= COMMON_YEAR_DAYS;
|
|
}
|
|
|
|
year++;
|
|
}
|
|
|
|
tm->tm_year = year - TM_START_YEAR;
|
|
|
|
while (days >= 28) {
|
|
if (is_leap_year(year) && (month == 1)) {
|
|
if (days >= 29) {
|
|
days -= 29;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (days >= month_days[month]) {
|
|
days -= month_days[month];
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
month++;
|
|
}
|
|
|
|
tm->tm_mon = month;
|
|
tm->tm_mday = days + 1;
|
|
|
|
seconds = (epoch_millisec % (SECONDS_PER_DAY * 1000)) / 1000;
|
|
tm->tm_hour = seconds / 3600;
|
|
tm->tm_min = (seconds % 3600) / 60;
|
|
tm->tm_sec = (seconds % 3600) % 60;
|
|
tm->tm_subsec = epoch_millisec % 1000;
|
|
|
|
return sdrv_check_rtc_time(tm);
|
|
}
|
|
|
|
static status_t _rtc_handler(uint32_t source, void *arg)
|
|
{
|
|
sdrv_rtc_t *dev = arg;
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
sdrv_rtc_violation_e vio_status;
|
|
|
|
/* disable overflow/disable violation intr */
|
|
if (source == dev->violation_intr) {
|
|
vio_status = sdrv_rtc_get_violation_status(dev);
|
|
|
|
if (dev->vio_mask & vio_status) {
|
|
sdrv_rtc_violation_intr_enable(dev, (sdrv_rtc_violation_e)(dev->vio_mask & vio_status), false);
|
|
}
|
|
}
|
|
else if (source == dev->wakeup_intr) {
|
|
sec_rtc_wakeup_enable(rtc,
|
|
BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE | BM_WAKEUP_CTRL_REQ_ENABLE,
|
|
false);
|
|
}
|
|
|
|
if (dev->cb)
|
|
dev->cb(source, arg);
|
|
|
|
/* overflow/disable violation intr */
|
|
if (source == dev->violation_intr) {
|
|
sdrv_rtc_clear_violation_status(dev, vio_status);
|
|
|
|
if (dev->vio_mask & vio_status) {
|
|
sdrv_rtc_violation_intr_enable(dev, (sdrv_rtc_violation_e)(dev->vio_mask & vio_status), true);
|
|
}
|
|
}
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv rtc enable.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] enable enable or disable
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_enable(sdrv_rtc_t *dev, bool enable)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
int ret;
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
ret = sec_rtc_local_enable(rtc, enable);
|
|
|
|
if (ret < 0) {
|
|
ssdk_printf(SSDK_ERR, "%s sec rtc enable faild!\n", __func__);
|
|
return SDRV_RTC_STATUS_LOCK;
|
|
}
|
|
|
|
udelay(DELAY_US);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv enable rtc wake up.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] type sdrv rtc wake up type
|
|
* @param [in] enable enable or disable
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_wakeup_enable(sdrv_rtc_t *dev,
|
|
sdrv_rtc_wakeup_enable_type_e type, bool enable)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
uint8_t vtype = 0;
|
|
|
|
switch (type) {
|
|
case SDRV_WKUP_ENABLE:
|
|
vtype = BM_WAKEUP_CTRL_ENABLE;
|
|
break;
|
|
|
|
case SDRV_WKUP_ENABLE_IRQ:
|
|
vtype = BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE;
|
|
break;
|
|
|
|
case SDRV_WKUP_ENABLE_REQ:
|
|
vtype = BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_REQ_ENABLE;
|
|
break;
|
|
|
|
case SDRV_WKUP_ENABLE_ALL:
|
|
vtype = BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE |
|
|
BM_WAKEUP_CTRL_REQ_ENABLE;
|
|
break;
|
|
|
|
default:
|
|
vtype = BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE |
|
|
BM_WAKEUP_CTRL_REQ_ENABLE;
|
|
break;
|
|
}
|
|
|
|
sec_rtc_wakeup_enable(rtc, vtype, enable);
|
|
|
|
return SDRV_STATUS_OK;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv get rtc wake up status.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @return 1 wakeup, 0 not wakeup.
|
|
*/
|
|
bool sdrv_rtc_get_wakeup_status(sdrv_rtc_t *dev)
|
|
{
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
return sec_rtc_get_wakeup_status(rtc);
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv clear rtc wake up status.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_clear_wakeup_status(sdrv_rtc_t *dev)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
/* rtc wakeup status has been cleared */
|
|
if (!sdrv_rtc_get_wakeup_status(dev)) {
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
|
|
if (sec_rtc_is_alarm_enable(rtc)) {
|
|
sec_rtc_wakeup_enable(rtc,
|
|
BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE | BM_WAKEUP_CTRL_REQ_ENABLE,
|
|
false);
|
|
}
|
|
|
|
sec_rtc_clear_wakeup_status(rtc);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv get rtc violation status.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @return 0 no violation, 1 overflow violation, 2 disable violation, 3 overflow & disable
|
|
*/
|
|
sdrv_rtc_violation_e sdrv_rtc_get_violation_status(sdrv_rtc_t *dev)
|
|
{
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
return (sdrv_rtc_violation_e)sec_rtc_get_violation_status(rtc);
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv clear rtc violation status.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_clear_violation_status(sdrv_rtc_t *dev,
|
|
sdrv_rtc_violation_e vio)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
sec_rtc_clear_violation_status(rtc, vio);
|
|
|
|
if (vio & SDRV_RTC_VIO_OVERFLOW) {
|
|
sec_rtc_set_cross_clk_en(rtc);
|
|
}
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief sdrv rtc violation enable.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] vio_mask sdrv rtc violation type
|
|
* @param [in] en violation enable or disable
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_violation_intr_enable(sdrv_rtc_t *dev,
|
|
sdrv_rtc_violation_e vio, bool en)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
|
|
if (en) {
|
|
sec_rtc_set_violation_enable(rtc, vio);
|
|
}
|
|
else {
|
|
sec_rtc_set_violation_disable(rtc, vio);
|
|
}
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable rtc interrupt.
|
|
* @param [in] source rtc wakeup/periodical/violation interrupt num.
|
|
* @param [in] arg pointer to interrupt arg(rtc device structure).
|
|
* @param [in] cb callback function.
|
|
*/
|
|
status_t sdrv_rtc_enable_it(uint32_t source, void *arg, rtc_cb_t cb)
|
|
{
|
|
sdrv_rtc_t *dev = arg;
|
|
dev->cb = cb;
|
|
|
|
if (source > 0) {
|
|
irq_attach(source, (irq_handler)_rtc_handler, dev);
|
|
irq_enable(source);
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
/**
|
|
* @brief disable rtc interrput.
|
|
* @param source rtc wakeup/periodical/violation interrupt num.
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_disable_it(uint32_t source)
|
|
{
|
|
if (source > 0) {
|
|
irq_detach(source);
|
|
irq_disable(source);
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
/**
|
|
* @brief rtc get time
|
|
*
|
|
* Get current rtc time.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] tm
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_get_time(sdrv_rtc_t *dev, struct rtc_time *tm)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
uint64_t millisecs = RTC_TICK_TO_MILLISECOND(sec_rtc_get_tick(rtc));
|
|
|
|
return epoch_ms_to_rtc(tm, millisecs);
|
|
}
|
|
|
|
/**
|
|
* @brief rtc set time
|
|
*
|
|
* Set current rtc time.
|
|
*
|
|
* @param [in] dev
|
|
* @param [in] tm
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_set_time(sdrv_rtc_t *dev, struct rtc_time *tm)
|
|
{
|
|
uint64_t millisecs = rtc_to_epoch_ms(tm);
|
|
int ret;
|
|
|
|
if (millisecs == 0) {
|
|
ssdk_printf(SSDK_WARNING, "%s default set rtc time 1970-1-1 00:00:00.000\n", __func__);
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
ret = sec_rtc_local_enable(rtc, 0);
|
|
|
|
if (ret < 0) {
|
|
ssdk_printf(SSDK_ERR, "%s sec rtc disable failed!\n", __func__);
|
|
return SDRV_RTC_STATUS_LOCK;
|
|
}
|
|
|
|
/* writing RTC_L/H, extra waitting 2*240us */
|
|
udelay(DELAY_US);
|
|
|
|
sec_rtc_set_tick(rtc, RTC_MILLISECOND_TO_TICK(millisecs));
|
|
sec_rtc_set_cross_clk_en(rtc);
|
|
|
|
ret = sec_rtc_local_enable(rtc, 1);
|
|
|
|
if (ret < 0) {
|
|
ssdk_printf(SSDK_ERR, "%s sec rtc enable failed!\n", __func__);
|
|
return SDRV_RTC_STATUS_LOCK;
|
|
}
|
|
|
|
udelay(DELAY_US);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief rtc get alarm
|
|
*
|
|
* Get rtc alarm time, rtc will trigger interrupt when rtc time arrives at alarm time.
|
|
*
|
|
* @param [in] dev
|
|
* @param [in] alrm
|
|
* @return 0
|
|
*/
|
|
status_t sdrv_rtc_get_alarm(sdrv_rtc_t *dev, struct rtc_wkalrm *alrm)
|
|
{
|
|
status_t ret = SDRV_STATUS_FAIL;
|
|
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
uint64_t millisecs = RTC_TICK_TO_MILLISECOND(sec_rtc_get_alarm_tick(rtc));
|
|
|
|
ret = epoch_ms_to_rtc(&alrm->tm, millisecs);
|
|
alrm->enable = sec_rtc_is_alarm_enable(rtc);
|
|
alrm->pending = sec_rtc_get_wakeup_status(rtc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief rtc set alarm
|
|
*
|
|
* Set rtc alarm time, rtc will trigger interrupt when rtc time arrives at alarm time.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] alrm
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_set_alarm(sdrv_rtc_t *dev, struct rtc_wkalrm *alrm)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
uint64_t millisecs = rtc_to_epoch_ms(&alrm->tm);
|
|
|
|
if (millisecs == 0) {
|
|
ssdk_printf(SSDK_ERR, "%s sec rtc set sec failed!\n", __func__);
|
|
return SDRV_RTC_STATUS_INVALID_RTC_TIME;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
sec_rtc_set_alarm_tick(rtc, RTC_MILLISECOND_TO_TICK(millisecs));
|
|
sec_rtc_set_cross_clk_en(rtc);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable rtc alarm.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] enable enable or disable
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_enable_alarm(sdrv_rtc_t *dev, bool enable)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
|
|
sec_rtc_wakeup_enable(rtc,
|
|
BM_WAKEUP_CTRL_ENABLE | BM_WAKEUP_CTRL_IRQ_ENABLE | BM_WAKEUP_CTRL_REQ_ENABLE,
|
|
enable);
|
|
|
|
if (!enable) {
|
|
sec_rtc_clear_wakeup_status(rtc);
|
|
}
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Read rtc general purpose register.
|
|
*
|
|
* Data in general purpose register will not be lost until RTC power down.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] gp_num sdrv general purpose register num
|
|
* @return general purpose register value
|
|
*/
|
|
uint32_t sdrv_rtc_read_general_purpose_reg(sdrv_rtc_t *dev,
|
|
sdrv_general_purpose_e gp_num)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
|
|
return sec_rtc_read_general_purpose_reg(rtc, gp_num);
|
|
}
|
|
|
|
/**
|
|
* @brief Write rtc general purpose register.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @param [in] gp_num sdrv general purpose register num
|
|
* @param [in] value general purpose register value
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_write_general_purpose_reg(sdrv_rtc_t *dev,
|
|
sdrv_general_purpose_e gp_num, uint32_t value)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
sec_rtc_write_general_purpose_reg(rtc, gp_num, value);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief rtc lock.
|
|
*
|
|
* Once set rtc lock, rtc enable can not be changed until next power on reset.
|
|
*
|
|
* @param [in] dev sdrv rtc controller
|
|
* @return 0 success, otherwise failed.
|
|
*/
|
|
status_t sdrv_rtc_lock(sdrv_rtc_t *dev)
|
|
{
|
|
if (dev->base == 0) {
|
|
return SDRV_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
Rtc *rtc = (Rtc *)dev->base;
|
|
sec_rtc_lock(rtc);
|
|
|
|
return SDRV_STATUS_OK;
|
|
}
|