MAX_CARLINK_A270S/MXC_A27-PCB4.5-270T/ArkmicroFiles/libcpu-amt630hv100/source/rtc.c

485 lines
11 KiB
C
Raw Permalink Normal View History

2025-01-21 16:49:37 +08:00
#include <stdio.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "errno.h"
/* RTC registers */
#define RTC_CTL 0x00 /*control register*/
#define RTC_ANAWEN 0x04 /*analog block write enable register*/
#define RTC_ANACTL 0x08 /*analog block control register*/
#define RTC_IM 0x0c /*interrupt mode register*/
#define RTC_STA 0x10 /*rtc status register*/
#define RTC_ALMDAT 0x14 /*alarm data register*/
#define RTC_DONT 0x18 /*delay on timer register*/
#define RTC_RAM 0x1c /*ram bit register*/
#define RTC_CNTL 0x20 /*rtc counter register*/
#define RTC_CNTH 0x24 /*rtc sec counter register*/
//RTC_CTL register fields defination
#define CTL_CTL3_VALUE(x) (x<<23)
#define CTL_CTL2_VALUE(x) (x<<22)
#define CTL_BIAS_TRM_VALUE(x) (x<<21)
#define CTL_SOFT_SEL_VALUE(x) (x<<20)
#define CTL_CTL1_VALUE(x) (x<<19)
#define CTL_CTL0_VALUE(x) (x<<18)
#define CTL_SOFT_STR_VALUE(x) (x<<17)
#define CTL_OSC_EN_VALUE(x) (x<<16)
#define CTL_CTL3_SET (1<<15)
#define CTL_CTL2_SET (1<<14)
#define CTL_BIAS_TRM_SET (1<<13)
#define CTL_SOFT_SEL_SET (1<<12)
#define CTL_CTL1_SET (1<<11)
#define CTL_CTL0_SET (1<<10)
#define CTL_SOFT_STR_SET (1<<9)
#define CTL_OSC_EN_SET (1<<8)
#define CTL_ALM_DATA_WEN (1<<3)
#define CTL_PERIOD_INT_EN (1<<2)
#define CTL_ALARM_INT_EN (1<<1)
#define CTL_RESET (1<<0)
//RTC_ANAWEN register fields defination
#define ANA_CNT_WEN (1<<7)
#define ANA_RAM_WEN (1<<6)
#define ANA_DELAY_TIMER_WEN (1<<5)
#define ANA_CLR_PWR_DET_WEN (1<<4)
#define ANA_DELAY_POWER_ON_WEN (1<<3)
#define ANA_FORCE_POWER_OFF_WEN (1<<2)
#define ANA_FORCE_POWER_ON_WEN (1<<1)
#define ANA_RTC_WEN (1<<0)
//RTC_ANACTL register fields defination
#define ANACTL_CLR_PWR (1<<4)
#define ANACTL_DELAY_POWER_ON (1<<3)
#define ANACTL_FORCE_POWER_OFF (1<<2)
#define ANACTL_FORCE_POWER_ON (1<<1)
#define ANACTL_COUNTER_EN (1<<0)
/* STATUS_REG */
#define STA_PWR_DET (1<<6)
#define STA_DELAY_ON (1<<5)
#define STA_FORCE_OFF (1<<4)
#define STA_FORCE_ON (1<<3)
#define STA_RCT_BUSY (1<<2)
#define STA_PERIOD_INT (1<<1)
#define STA_ALARM_INT (1<<0)
/* 2020-01-01 Wednesday */
static struct rtc_time default_tm = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = 120,
.tm_wday = 3,
.tm_yday = 1,
};
static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const unsigned short rtc_ydays[2][13] = {
/* Normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
/*
* The number of days in the month.
*/
int rtc_month_days(unsigned int month, unsigned int year)
{
return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
}
/*
* The number of days since January 1. (0 to 365)
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
return rtc_ydays[is_leap_year(year)][month] + day-1;
}
/*
* rtc_time_to_tm - Converts time to rtc_time.
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
void rtc_time_to_tm(uint32_t time, struct rtc_time *tm)
{
unsigned int month, year;
unsigned long secs;
int days;
/* time must be positive */
days = time / 86400;
secs = time - (unsigned int) days * 86400;
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
year = 1970 + days / 365;
days -= (year - 1970) * 365
+ LEAPS_THRU_END_OF(year - 1)
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
days += 365 + is_leap_year(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
for (month = 0; month < 11; month++) {
int newdays;
newdays = days - rtc_month_days(month, year);
if (newdays < 0)
break;
days = newdays;
}
tm->tm_mon = month;
tm->tm_mday = days + 1;
tm->tm_hour = secs / 3600;
secs -= tm->tm_hour * 3600;
tm->tm_min = secs / 60;
tm->tm_sec = secs - tm->tm_min * 60;
}
/*
* Does the rtc_time represent a valid date/time?
*/
int rtc_valid_tm(struct rtc_time *tm)
{
if (tm->tm_year < 70
|| ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
|| ((unsigned)tm->tm_hour) >= 24
|| ((unsigned)tm->tm_min) >= 60
|| ((unsigned)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
}
/*
* mktime - Converts date to seconds.
* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* A leap second can be indicated by calling this function with sec as
* 60 (allowable under ISO 8601). The leap second is treated the same
* as the following second since they don't exist in UNIX time.
*
* An encoding of midnight at the end of the day as 24:00:00 - ie. midnight
* tomorrow - (allowable under ISO 8601) is supported.
*/
uint32_t mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)
{
unsigned int mon = mon0, year = year0;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((uint32_t)
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours - midnight tomorrow handled here */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
/*
* rtc_tm_to_time - Converts rtc_time to time.
* Convert Gregorian date to seconds since 01-01-1970 00:00:00.
*/
uint32_t rtc_tm_to_time(struct rtc_time *tm)
{
return mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
static __INLINE void rtc_clear_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_STA);
val &= ~CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_STA);
}
static __INLINE void rtc_enable_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_CTL);
if (!(val & CTL_ALARM_INT_EN)) {
rtc_clear_interrupt();
val |= CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_CTL);
}
}
static __INLINE void rtc_disable_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_CTL);
if (val & CTL_ALARM_INT_EN) {
val &= ~CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_CTL);
}
}
static void rtc_wait_not_busy(void)
{
int status, count = 0;
/* Assuming BUSY may stay active for 80 msec) */
for (count = 0; count < 0x1000; count++) {
status = readl(REGS_RTC_BASE + RTC_STA);
if ((status & STA_RCT_BUSY) == 0)
break;
/* check status busy, after each msec */
vTaskDelay(pdMS_TO_TICKS(1));
}
}
static void rtc_isr(void *para)
{
unsigned int irq_data;
irq_data = readl(REGS_RTC_BASE + RTC_STA);
if ((irq_data & CTL_ALARM_INT_EN)) {
rtc_clear_interrupt();
return;
} else
return;
}
static void rtc_update_time(unsigned int time)
{
unsigned int val;
int timeout = 100000;
val = readl(REGS_RTC_BASE + RTC_ANAWEN);
writel(val | ANA_RTC_WEN, REGS_RTC_BASE + RTC_ANAWEN);
val = readl(REGS_RTC_BASE + RTC_ANACTL);
writel(val | ANACTL_COUNTER_EN, REGS_RTC_BASE + RTC_ANACTL);
//wait rtc_busy;
rtc_wait_not_busy();
val = readl(REGS_RTC_BASE + RTC_ANAWEN);
writel(val | ANA_CNT_WEN, REGS_RTC_BASE + RTC_ANAWEN);
writel(time, REGS_RTC_BASE + RTC_CNTH);
//wait rtc_busy;
rtc_wait_not_busy();
while(readl(REGS_RTC_BASE + RTC_CNTH) != time) {
if (timeout-- == 0)
break;
taskYIELD();
}
}
/*
* rtc_read_time - set the time
* @tm: holds date and time
*
* This function read time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_read_time(struct rtc_time *tm)
{
unsigned int time;
/* we don't report wday/yday/isdst ... */
rtc_wait_not_busy();
time = readl(REGS_RTC_BASE + RTC_CNTH);
rtc_time_to_tm(time, tm);
return 0;
}
/*
* rtc_set_time - set the time
* @tm: holds date and time
*
* This function set time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_set_time(struct rtc_time *tm)
{
long unsigned int time;
if (rtc_valid_tm(tm) < 0)
return -EINVAL;
/* convert tm to seconds. */
time = rtc_tm_to_time(tm);
rtc_update_time(time);
return 0;
}
#if 0
static void rtc_update_alarm_time(unsigned int time)
{
unsigned int val;
int timeout = 100000;
val = readl(REGS_RTC_BASE + RTC_CTL);
writel(val | CTL_ALM_DATA_WEN, REGS_RTC_BASE + RTC_CTL);
writel(time, REGS_RTC_BASE + RTC_ALMDAT);
//wait rtc_busy;
rtc_wait_not_busy();
while(readl(REGS_RTC_BASE + RTC_ALMDAT) != time) {
if (timeout-- == 0)
break;
taskYIELD();
}
}
/*
* rtc_read_alarm - read the alarm time
* @alm: holds alarm date and time
*
* This function read alarm time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_read_alarm(struct rtc_wkalrm *alm)
{
unsigned int time;
rtc_wait_not_busy();
time = readl(REGS_RTC_BASE + RTC_ALMDAT);
rtc_time_to_tm(time, &alm->time);
alm->enabled = readl(REGS_RTC_BASE + RTC_CTL) & CTL_ALARM_INT_EN;
return 0;
}
/*
* rtc_set_alarm - set the alarm time
* @alm: holds alarm date and time
*
* This function set alarm time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_set_alarm(struct rtc_wkalrm *alm)
{
long unsigned int time;
if (rtc_valid_tm(&alm->time) < 0)
return -EINVAL;
/* convert tm to seconds. */
time = rtc_tm_to_time(&alm->time);
rtc_update_alarm_time(time);
if (alm->enabled)
rtc_enable_interrupt();
else
rtc_disable_interrupt();
return 0;
}
#endif
static int alarm_irq_enable(unsigned int enabled)
{
int ret = 0;
rtc_clear_interrupt();
switch (enabled) {
case 0:
/* alarm off */
rtc_disable_interrupt();
break;
case 1:
/* alarm on */
rtc_enable_interrupt();
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
int rtc_init(void)
{
struct rtc_time tm;
writel(0, REGS_RTC_BASE + RTC_CTL);
writel(CTL_SOFT_STR_SET|CTL_SOFT_STR_VALUE(1), REGS_RTC_BASE + RTC_CTL);
writel(CTL_OSC_EN_SET|CTL_OSC_EN_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL0_SET|CTL_CTL0_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL1_SET|CTL_CTL1_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL2_SET|CTL_CTL2_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL3_SET|CTL_CTL3_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_BIAS_TRM_SET|CTL_BIAS_TRM_VALUE(1)|CTL_CTL3_VALUE(0), REGS_RTC_BASE + RTC_CTL);
udelay(1000);
writel(CTL_OSC_EN_SET|CTL_OSC_EN_VALUE(1), REGS_RTC_BASE + RTC_CTL);
udelay(1000);
request_irq(RTC_PRD_IRQn, 0, rtc_isr, NULL);
alarm_irq_enable(0);
rtc_read_time(&tm);
if (tm.tm_year == 70) {
rtc_set_time(&default_tm);
}
return 0;
}
int iGetLocalTime(SystemTime_t *tm)
{
rtc_read_time(tm);
tm->tm_year += 1900;
tm->tm_mon += 1;
return 0;
}
void vSetLocalTime(SystemTime_t *tm)
{
tm->tm_year -= 1900;
tm->tm_mon -= 1;
rtc_set_time(tm);
}