/* * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. * * SPDX-License-Identifier: MIT */ #include #include #include #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(); }