244 lines
5.0 KiB
C
244 lines
5.0 KiB
C
/*
|
|
* Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include "common.h"
|
|
#include "rtc.h"
|
|
#include "register.h"
|
|
#include "FreeRTOS.h"
|
|
#include "mailbox-api.h"
|
|
#include "soc.h"
|
|
#include "string.h"
|
|
#include "interrupt.h"
|
|
#include "suspend.h"
|
|
#include "rtc_register.h"
|
|
#include "timers.h"
|
|
#include "interrupt_control_eclic.h"
|
|
|
|
#define RTC_ALARM_MAX \
|
|
((uint32_t)((uint64_t)((TickType_t)~(TickType_t)0) * 1000 / configTICK_RATE_HZ / 1000))
|
|
|
|
#undef TAG
|
|
#define TAG "RTC"
|
|
|
|
#ifdef RTC_DEBUG
|
|
#define RTC_DBG(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define RTC_DBG(...)
|
|
#endif
|
|
|
|
static TimerHandle_t xAlarmTimer;
|
|
|
|
#ifdef CONFIG_RTC_STORAGE_FORMAT_GRAY
|
|
static uint32_t prvGrayToBinary(uint32_t gray)
|
|
{
|
|
uint32_t bcd = gray;
|
|
int size = sizeof(bcd) * 8;
|
|
int i;
|
|
|
|
for (i = 0; (1 << i) < size; i++)
|
|
bcd ^= bcd >> (1 << i);
|
|
|
|
return bcd;
|
|
}
|
|
#endif
|
|
|
|
static void vRTCInterruptHandler(void)
|
|
{
|
|
uint32_t buf[4] = { 0 };
|
|
uint32_t reg_val;
|
|
|
|
reg_val = REG32(RTC_DIG_INT_STATUS) & RTC_INT_ALM0_IRQ;
|
|
/* Clear alarm0 int status */
|
|
if (reg_val)
|
|
REG32(RTC_DIG_INT_CLR) = RTC_INT_CLR_ALM0_IRQ;
|
|
|
|
printf("[%s]: rtc alarm fired\n", TAG);
|
|
|
|
buf[0] = RTC_WAKEUP;
|
|
STR_Wakeup_src_Queue_Send_FromISR(buf);
|
|
}
|
|
#ifdef CONFIG_RTC_USE_RESET
|
|
static uint32_t prvGetRebootMode(void)
|
|
{
|
|
uint32_t reg_val;
|
|
uint32_t reboot_mode;
|
|
|
|
reg_val = REG32(SYSCTRL_SEC_STATUS_REG31);
|
|
reboot_mode = ((reg_val >> 12) & 0xf);
|
|
|
|
return reboot_mode;
|
|
}
|
|
|
|
static void prvResetRtc(void)
|
|
{
|
|
uint32_t reg_val;
|
|
|
|
printf("[%s]: reset rtc\n", TAG);
|
|
/* Reset RTC */
|
|
reg_val = (1 << 0);
|
|
REG32(RESETCTRL_RESET4) = reg_val;
|
|
/* Mask RTC reset to prevent RTC being reset in the next reboot */
|
|
reg_val = REG32(RESETCTRL_RESET4_MASK);
|
|
reg_val |= (1 << 0);
|
|
REG32(RESETCTRL_RESET4_MASK) = reg_val;
|
|
}
|
|
#endif
|
|
static int prvGetRtc(uint32_t *val)
|
|
{
|
|
if (!REG32(VRTC_STICKY_REG))
|
|
return -1;
|
|
|
|
*(val) = REG32(VRTC_STICKY_REG);
|
|
return 0;
|
|
}
|
|
|
|
static void prvSetRtc(uint32_t val)
|
|
{
|
|
REG32(VRTC_STICKY_REG) = val;
|
|
}
|
|
|
|
void vStoreRtc(void)
|
|
{
|
|
uint32_t reg_val;
|
|
|
|
reg_val = REG32(RTC_DIG_REAL_TIME);
|
|
#ifdef CONFIG_RTC_STORAGE_FORMAT_GRAY
|
|
reg_val = prvGrayToBinary(reg_val);
|
|
#endif
|
|
REG32(VRTC_STICKY_REG) = reg_val;
|
|
}
|
|
|
|
void *MboxSetRTC(void *msg)
|
|
{
|
|
unsigned int val = *(uint32_t *)msg;
|
|
|
|
RTC_DBG("[%s]: %s val=0x%x\n", TAG, __func__, val);
|
|
prvSetRtc(val);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *MboxGetRTC(void *msg)
|
|
{
|
|
uint32_t val = 0;
|
|
|
|
prvGetRtc(&val);
|
|
memset(msg, 0, MBOX_BUF_LEN);
|
|
*(uint32_t *)msg = val;
|
|
|
|
RTC_DBG("[%s]: %s val=0x%x\n", TAG, __func__, val);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void vRtcEnableIrq(void)
|
|
{
|
|
u32 alarm, time, val, enable = 0;
|
|
|
|
enable = (REG32(RTC_DIG_CTRL) & RTC_CTRL_ALM0_EN) &&
|
|
!(REG32(RTC_DIG_INT_MASK) & RTC_CTRL_ALM0_MASK);
|
|
|
|
alarm = REG32(RTC_DIG_ALARM0_REG);
|
|
time = REG32(RTC_DIG_REAL_TIME);
|
|
#ifdef CONFIG_RTC_STORAGE_FORMAT_GRAY
|
|
alarm = prvGrayToBinary(alarm);
|
|
time = prvGrayToBinary(time);
|
|
#endif
|
|
if (enable && (alarm > time)) {
|
|
val = alarm - time;
|
|
EnableIrq(RTC_IRQ);
|
|
printf("[%s]: alarm val=%d S\n", TAG, val);
|
|
if (xAlarmTimer != NULL) {
|
|
alarm = (val + 1);
|
|
if (alarm > RTC_ALARM_MAX) {
|
|
printf("[%s]: alarm %u exceeds max\n", TAG, val);
|
|
alarm = RTC_ALARM_MAX;
|
|
}
|
|
xTimerChangePeriod(xAlarmTimer, pdMS_TO_TICKS((uint64_t)alarm * 1000), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void vRtcDisableIrq(void)
|
|
{
|
|
int val, irq_num;
|
|
uint32_t reg_val;
|
|
|
|
if (xAlarmTimer != NULL)
|
|
xTimerStop(xAlarmTimer, 0);
|
|
|
|
val = GetIrqInner(RTC_IRQ);
|
|
if (val > 0)
|
|
DisableIrq(RTC_IRQ);
|
|
|
|
irq_num = RTC_IRQ % 32;
|
|
reg_val = REG32(RTC_IRQCTRL_IRQ_LATCH) >> irq_num & 0x1;
|
|
if (reg_val)
|
|
REG32(RTC_IRQCTRL_IRQ_LATCH_CLR) |= (0x1 << irq_num);
|
|
}
|
|
|
|
static void prvRtcAlarmTimerHandler(TimerHandle_t xTimer)
|
|
{
|
|
(void)xTimer;
|
|
static int rtc_irq, status;
|
|
int irq_num;
|
|
uint32_t reg_val;
|
|
|
|
status = REG32(RTC_DIG_INT_STATUS) & RTC_INT_ALM0_IRQ;
|
|
if (status) {
|
|
printf("warning: rtc irq lost! trigger it manually!\n");
|
|
irq_num = RTC_IRQ % 32;
|
|
reg_val = REG32(RTC_IRQCTRL_IRQ_LATCH) >> irq_num & 0x1;
|
|
printf("[%s]: irqctrl status: 0x%x !!\n", TAG, reg_val);
|
|
rtc_irq = GetIrqInner(RTC_IRQ);
|
|
if (rtc_irq)
|
|
eclic_set_pending(rtc_irq);
|
|
}
|
|
}
|
|
|
|
static void prvRtcAlarmTimerInit(void)
|
|
{
|
|
xAlarmTimer = xTimerCreate("RtcAlarmTimer", pdMS_TO_TICKS(1000),
|
|
pdFALSE, NULL, prvRtcAlarmTimerHandler);
|
|
}
|
|
|
|
void vRtcInit(void)
|
|
{
|
|
int ret;
|
|
#ifdef CONFIG_RTC_USE_RESET
|
|
uint32_t reboot_mode;
|
|
#endif
|
|
|
|
RTC_DBG("[%s]: init rtc\n", TAG);
|
|
|
|
ret = RegisterIrq(RTC_IRQ, 6, vRTCInterruptHandler);
|
|
if (ret) {
|
|
printf("RTC_irq Register err, ret = %d\n", ret);
|
|
return;
|
|
}
|
|
/* RTC is a level interrupt, configuring it in level mode is more reasonable */
|
|
ret = GetIrqInner(RTC_IRQ);
|
|
if (ret <= 0) {
|
|
printf("[%s]: GetIrqInner err, ret = %d\n", TAG, ret);
|
|
UnRegisterIrq(RTC_IRQ);
|
|
return;
|
|
}
|
|
eclic_set_level_trig(ret);
|
|
|
|
xInstallRemoteMessageCallbackFeedBack(REE2AO_CHANNEL, MBX_CMD_SET_RTC, MboxSetRTC, 0);
|
|
|
|
xInstallRemoteMessageCallbackFeedBack(REE2AO_CHANNEL, MBX_CMD_GET_RTC, MboxGetRTC, 1);
|
|
#ifdef CONFIG_RTC_USE_RESET
|
|
reboot_mode = prvGetRebootMode();
|
|
if (reboot_mode == COLD_REBOOT)
|
|
prvResetRtc();
|
|
#endif
|
|
prvRtcAlarmTimerInit();
|
|
}
|