From 884b790a6560e13975395d1982aa5e6634e7842c Mon Sep 17 00:00:00 2001 From: Philip Chen Date: Sun, 10 Sep 2017 10:31:19 -0700 Subject: chip/stm32/clock: Incorporate RTC date register The current stm32 rtc driver only uses RTC_TR and RTC_SSR. So we son't be able to use rtc for applications which need time > 24 hours. To support such applications, this patch adds operations for RTC date register (RTC_DR). BUG=b:63908519 CQ-DEPEND=CL:666985 BRANCH=none TEST=manually with 'ectool rtcset/rtcset' and '/sys/class/rtc/rtc0', verify the conversion between calendar time and Unix epoch time works. Change-Id: Iacd5468502e4417a70880d7239ca5e03353d9469 Signed-off-by: Philip Chen Reviewed-on: https://chromium-review.googlesource.com/659337 Commit-Ready: Philip Chen Tested-by: Philip Chen Reviewed-by: Matthias Kaehlcke Reviewed-by: Shawn N --- chip/stm32/clock-f.c | 195 ++++++++++++++++++++++++++++++++------------- chip/stm32/clock-f.h | 31 +++---- chip/stm32/clock-stm32f0.c | 20 +++-- chip/stm32/clock-stm32f4.c | 4 +- 4 files changed, 170 insertions(+), 80 deletions(-) (limited to 'chip') diff --git a/chip/stm32/clock-f.c b/chip/stm32/clock-f.c index 4da745d1bd..893e64bb86 100644 --- a/chip/stm32/clock-f.c +++ b/chip/stm32/clock-f.c @@ -15,6 +15,7 @@ #include "host_command.h" #include "hwtimer.h" #include "registers.h" +#include "rtc.h" #include "system.h" #include "task.h" #include "timer.h" @@ -24,82 +25,149 @@ #define CPUTS(outstr) cputs(CC_CLOCK, outstr) #define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args) +/* Convert decimal to BCD */ +static uint8_t u8_to_bcd(uint8_t val) +{ + /* Fast division by 10 (when lacking HW div) */ + uint32_t quot = ((uint32_t)val * 0xCCCD) >> 19; + uint32_t rem = val - quot * 10; + + return rem | (quot << 4); +} /* Convert between RTC regs in BCD and seconds */ -uint32_t rtc_to_sec(uint32_t rtc) +static uint32_t rtc_tr_to_sec(uint32_t rtc_tr) { uint32_t sec; /* convert the hours field */ - sec = (((rtc & 0x300000) >> 20) * 10 + ((rtc & 0xf0000) >> 16)) * 3600; + sec = (((rtc_tr & 0x300000) >> 20) * 10 + + ((rtc_tr & 0xf0000) >> 16)) * 3600; /* convert the minutes field */ - sec += (((rtc & 0x7000) >> 12) * 10 + ((rtc & 0xf00) >> 8)) * 60; + sec += (((rtc_tr & 0x7000) >> 12) * 10 + ((rtc_tr & 0xf00) >> 8)) * 60; /* convert the seconds field */ - sec += ((rtc & 0x70) >> 4) * 10 + (rtc & 0xf); - + sec += ((rtc_tr & 0x70) >> 4) * 10 + (rtc_tr & 0xf); return sec; } -uint32_t sec_to_rtc(uint32_t sec) + +static uint32_t sec_to_rtc_tr(uint32_t sec) { - uint32_t rtc; + uint32_t rtc_tr; + uint8_t hour; + uint8_t min; + sec %= SECS_PER_DAY; /* convert the hours field */ - rtc = ((sec / 36000) << 20) | (((sec / 3600) % 10) << 16); + hour = sec / 3600; + rtc_tr = u8_to_bcd(hour) << 16; /* convert the minutes field */ - rtc |= (((sec % 3600) / 600) << 12) | (((sec % 600) / 60) << 8); + sec -= hour * 3600; + min = sec / 60; + rtc_tr |= u8_to_bcd(min) << 8; /* convert the seconds field */ - rtc |= (((sec % 60) / 10) << 4) | (sec % 10); + sec -= min * 60; + rtc_tr |= u8_to_bcd(sec); + + return rtc_tr; +} + +#ifdef CONFIG_HOSTCMD_RTC +static uint32_t rtc_dr_to_sec(uint32_t rtc_dr) +{ + struct calendar_date time; + uint32_t sec; + + time.year = (((rtc_dr & 0xf00000) >> 20) * 10 + + ((rtc_dr & 0xf0000) >> 16)); + time.month = (((rtc_dr & 0x1000) >> 12) * 10 + + ((rtc_dr & 0xf00) >> 8)); + time.day = ((rtc_dr & 0x30) >> 4) * 10 + (rtc_dr & 0xf); + + sec = date_to_sec(time); + + return sec; +} + +static uint32_t sec_to_rtc_dr(uint32_t sec) +{ + struct calendar_date time; + uint32_t rtc_dr; + + time = sec_to_date(sec); + + rtc_dr = u8_to_bcd(time.year) << 16; + rtc_dr |= u8_to_bcd(time.month) << 8; + rtc_dr |= u8_to_bcd(time.day); + + return rtc_dr; +} +#endif - return rtc; +uint32_t rtc_to_sec(const struct rtc_time_reg *rtc) +{ + uint32_t sec = 0; +#ifdef CONFIG_HOSTCMD_RTC + sec = rtc_dr_to_sec(rtc->rtc_dr); +#endif + return sec + (rtcss_to_us(rtc->rtc_ssr) / SECOND) + + rtc_tr_to_sec(rtc->rtc_tr); } -/* Return time diff between two rtc readings */ -int32_t get_rtc_diff(uint32_t rtc0, uint32_t rtc0ss, - uint32_t rtc1, uint32_t rtc1ss) +void sec_to_rtc(uint32_t sec, struct rtc_time_reg *rtc) +{ + rtc->rtc_dr = 0; +#ifdef CONFIG_HOSTCMD_RTC + rtc->rtc_dr = sec_to_rtc_dr(sec); +#endif + rtc->rtc_tr = sec_to_rtc_tr(sec); + rtc->rtc_ssr = 0; +} + +/* Return sub-10-sec time diff between two rtc readings */ +int32_t get_rtc_diff(const struct rtc_time_reg *rtc0, + const struct rtc_time_reg *rtc1) { int32_t diff; - /* Note: this only looks at the diff mod 10 seconds */ - diff = ((rtc1 & 0xf) * SECOND + - rtcss_to_us(rtc1ss)) - - ((rtc0 & 0xf) * SECOND + - rtcss_to_us(rtc0ss)); + /* Note: This only looks at the diff mod 10 seconds */ + diff = ((rtc1->rtc_tr & 0xf) * SECOND + + rtcss_to_us(rtc1->rtc_ssr)) - + ((rtc0->rtc_tr & 0xf) * SECOND + + rtcss_to_us(rtc0->rtc_ssr)); - return (diff < 0) ? (diff + 10*SECOND) : diff; + return (diff < 0) ? (diff + 10 * SECOND) : diff; } -void rtc_read(uint32_t *rtc, uint32_t *rtcss) +void rtc_read(struct rtc_time_reg *rtc) { - /* Read current time synchronously */ + /* + * Read current time synchronously. Each register must be read + * twice with identical values because glitches may occur for reads + * close to the RTCCLK edge. + */ do { - *rtc = STM32_RTC_TR; - /* - * RTC_SSR must be read twice with identical values because - * glitches may occur for reads close to the RTCCLK edge. - */ + rtc->rtc_dr = STM32_RTC_DR; + do { - *rtcss = STM32_RTC_SSR; - } while (*rtcss != STM32_RTC_SSR); - } while (*rtc != STM32_RTC_TR); -} + rtc->rtc_tr = STM32_RTC_TR; -uint32_t rtc_read_sec(void) -{ - uint32_t rtc = 0; - uint32_t rtcss = 0; + do { + rtc->rtc_ssr = STM32_RTC_SSR; + } while (rtc->rtc_ssr != STM32_RTC_SSR); - rtc_read(&rtc, &rtcss); + } while (rtc->rtc_tr != STM32_RTC_TR); - return rtc_to_sec(rtc) + (rtcss_to_us(rtcss) / SECOND); + } while (rtc->rtc_dr != STM32_RTC_DR); } void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us, - uint32_t *rtc, uint32_t *rtcss) + struct rtc_time_reg *rtc) { uint32_t alarm_sec, alarm_us; /* Alarm must be within 1 day (86400 seconds) */ - ASSERT((delay_s + delay_us / SECOND) < 86400); + + ASSERT((delay_s + delay_us / SECOND) < SECS_PER_DAY); rtc_unlock_regs(); @@ -109,13 +177,13 @@ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us, ; STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF; - rtc_read(rtc, rtcss); + rtc_read(rtc); /* Calculate alarm time */ - alarm_sec = rtc_to_sec(*rtc) + delay_s; - alarm_us = rtcss_to_us(*rtcss) + delay_us; + alarm_sec = rtc_tr_to_sec(rtc->rtc_tr) + delay_s; + alarm_us = rtcss_to_us(rtc->rtc_ssr) + delay_us; alarm_sec = alarm_sec + alarm_us / SECOND; - alarm_us = alarm_us % 1000000; + alarm_us = alarm_us % SECOND; /* * If seconds is greater than 1 day, subtract by 1 day to deal with * 24-hour rollover. @@ -124,7 +192,7 @@ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us, alarm_sec -= 86400; /* Set alarm time */ - STM32_RTC_ALRMAR = sec_to_rtc(alarm_sec); + STM32_RTC_ALRMAR = sec_to_rtc_tr(alarm_sec); STM32_RTC_ALRMASSR = us_to_rtcss(alarm_us); /* Check for match on hours, minutes, seconds, and subsecond */ STM32_RTC_ALRMAR |= 0xc0000000; @@ -140,12 +208,23 @@ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us, uint32_t get_rtc_alarm(void) { + struct rtc_time_reg now; + uint32_t now_sec; + uint32_t alarm_sec; + if (!(STM32_RTC_CR & STM32_RTC_CR_ALRAE)) return 0; - return rtc_to_sec(STM32_RTC_ALRMAR) - rtc_read_sec(); + + rtc_read(&now); + + now_sec = rtc_tr_to_sec(now.rtc_tr); + alarm_sec = rtc_tr_to_sec(STM32_RTC_ALRMAR & 0x3fffff); + + return ((alarm_sec < now_sec) ? SECS_PER_DAY : 0) + + (alarm_sec - now_sec); } -void reset_rtc_alarm(uint32_t *rtc, uint32_t *rtcss) +void reset_rtc_alarm(struct rtc_time_reg *rtc) { rtc_unlock_regs(); @@ -158,16 +237,16 @@ void reset_rtc_alarm(uint32_t *rtc, uint32_t *rtcss) STM32_EXTI_PR = EXTI_RTC_ALR_EVENT; /* Read current time */ - rtc_read(rtc, rtcss); + rtc_read(rtc); rtc_lock_regs(); } void __rtc_alarm_irq(void) { - uint32_t rtc, rtcss; + struct rtc_time_reg rtc; - reset_rtc_alarm(&rtc, &rtcss); + reset_rtc_alarm(&rtc); } DECLARE_IRQ(STM32_IRQ_RTC_ALARM, __rtc_alarm_irq, 1); @@ -220,8 +299,10 @@ DECLARE_HOOK(HOOK_SYSJUMP, reset_flash_cache, HOOK_PRIO_DEFAULT); void print_system_rtc(enum console_channel ch) { uint32_t sec; + struct rtc_time_reg rtc; - sec = rtc_read_sec(); + rtc_read(&rtc); + sec = rtc_to_sec(&rtc); cprintf(ch, "RTC: 0x%08x (%d.00 s)\n", sec, sec); } @@ -250,7 +331,7 @@ DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc, static int command_rtc_alarm_test(int argc, char **argv) { int s = 1, us = 0; - uint32_t rtc, rtcss; + struct rtc_time_reg rtc; char *e; ccprintf("Setting RTC alarm\n"); @@ -265,10 +346,9 @@ static int command_rtc_alarm_test(int argc, char **argv) us = strtoi(argv[2], &e, 10); if (*e) return EC_ERROR_PARAM2; - } - set_rtc_alarm(s, us, &rtc, &rtcss); + set_rtc_alarm(s, us, &rtc); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test, @@ -284,8 +364,10 @@ DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test, static int system_rtc_get_value(struct host_cmd_handler_args *args) { struct ec_response_rtc *r = args->response; + struct rtc_time_reg rtc; - r->time = rtc_read_sec(); + rtc_read(&rtc); + r->time = rtc_to_sec(&rtc); args->response_size = sizeof(*r); return EC_RES_SUCCESS; @@ -307,11 +389,10 @@ DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_VALUE, static int system_rtc_set_alarm(struct host_cmd_handler_args *args) { - uint32_t rtc; - uint32_t rtcss; + struct rtc_time_reg rtc; const struct ec_params_rtc *p = args->params; - set_rtc_alarm(p->time, 0, &rtc, &rtcss); + set_rtc_alarm(p->time, 0, &rtc); return EC_RES_SUCCESS; } DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_ALARM, diff --git a/chip/stm32/clock-f.h b/chip/stm32/clock-f.h index 2273089804..ffeca7518a 100644 --- a/chip/stm32/clock-f.h +++ b/chip/stm32/clock-f.h @@ -32,41 +32,44 @@ static inline void rtc_unlock_regs(void) STM32_RTC_WPR = 0x53; } +struct rtc_time_reg { + uint32_t rtc_ssr; /* subseconds */ + uint32_t rtc_tr; /* hours, minutes, seconds */ + uint32_t rtc_dr; /* years, months, dates, week days */ +}; + /* Convert between RTC regs in BCD and seconds */ -uint32_t rtc_to_sec(uint32_t rtc); +uint32_t rtc_to_sec(const struct rtc_time_reg *rtc); /* Convert between seconds and RTC regs */ -uint32_t sec_to_rtc(uint32_t sec); +void sec_to_rtc(uint32_t sec, struct rtc_time_reg *rtc); -/* Calculate microseconds from rtc clocks. */ +/* Calculate microseconds from rtc sub-second register. */ int32_t rtcss_to_us(uint32_t rtcss); -/* Calculate rtc clocks from microseconds. */ +/* Calculate rtc sub-second register value from microseconds. */ uint32_t us_to_rtcss(int32_t us); -/* Return time diff between two rtc readings */ -int32_t get_rtc_diff(uint32_t rtc0, uint32_t rtc0ss, - uint32_t rtc1, uint32_t rtc1ss); +/* Return sub-10-sec time diff between two rtc readings */ +int32_t get_rtc_diff(const struct rtc_time_reg *rtc0, + const struct rtc_time_reg *rtc1); /* Read RTC values */ -void rtc_read(uint32_t *rtc, uint32_t *rtcss); +void rtc_read(struct rtc_time_reg *rtc); /* Set RTC value */ void rtc_set(uint32_t sec); -/* Return RTC value in seconds */ -uint32_t rtc_read_sec(void); - /* Set RTC wakeup */ void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us, - uint32_t *rtc, uint32_t *rtcss); + struct rtc_time_reg *rtc); /* Clear RTC wakeup */ -void reset_rtc_alarm(uint32_t *rtc, uint32_t *rtcss); +void reset_rtc_alarm(struct rtc_time_reg *rtc); /* * Return the remaining seconds before the RTC alarm goes off. - * Returns 0 if alarm is not set. + * Sub-seconds are ignored. Returns 0 if alarm is not set. */ uint32_t get_rtc_alarm(void); diff --git a/chip/stm32/clock-stm32f0.c b/chip/stm32/clock-stm32f0.c index 14b2dc50b2..c63f773b9a 100644 --- a/chip/stm32/clock-stm32f0.c +++ b/chip/stm32/clock-stm32f0.c @@ -249,10 +249,10 @@ defined(CHIP_VARIANT_STM32F070) #ifdef CONFIG_HIBERNATE void __enter_hibernate(uint32_t seconds, uint32_t microseconds) { - uint32_t rtc, rtcss; + struct rtc_time_reg rtc; if (seconds || microseconds) - set_rtc_alarm(seconds, microseconds, &rtc, &rtcss); + set_rtc_alarm(seconds, microseconds, &rtc); /* interrupts off now */ asm volatile("cpsid i"); @@ -304,7 +304,7 @@ void __idle(void) { timestamp_t t0; int next_delay, margin_us, rtc_diff; - uint32_t rtc0, rtc0ss, rtc1, rtc1ss; + struct rtc_time_reg rtc0, rtc1; while (1) { asm volatile("cpsid i"); @@ -323,7 +323,7 @@ void __idle(void) CPU_SCB_SYSCTRL |= 0x4; set_rtc_alarm(0, next_delay - STOP_MODE_LATENCY, - &rtc0, &rtc0ss); + &rtc0); asm("wfi"); CPU_SCB_SYSCTRL &= ~0x4; @@ -337,8 +337,8 @@ void __idle(void) config_hispeed_clock(); /* fast forward timer according to RTC counter */ - reset_rtc_alarm(&rtc1, &rtc1ss); - rtc_diff = get_rtc_diff(rtc0, rtc0ss, rtc1, rtc1ss); + reset_rtc_alarm(&rtc1); + rtc_diff = get_rtc_diff(&rtc0, &rtc1); t0.val = t0.val + rtc_diff; force_time(t0); @@ -412,8 +412,12 @@ void rtc_init(void) rtc_lock_regs(); } +#if defined(CONFIG_CMD_RTC) || defined(CONFIG_HOSTCMD_RTC) void rtc_set(uint32_t sec) { + struct rtc_time_reg rtc; + + sec_to_rtc(sec, &rtc); rtc_unlock_regs(); /* Disable alarm */ @@ -427,12 +431,14 @@ void rtc_set(uint32_t sec) /* Set clock prescalars */ STM32_RTC_PRER = (RTC_PREDIV_A << 16) | RTC_PREDIV_S; - STM32_RTC_TR = sec_to_rtc(sec); + STM32_RTC_TR = rtc.rtc_tr; + STM32_RTC_DR = rtc.rtc_dr; /* Start RTC timer */ STM32_RTC_ISR &= ~STM32_RTC_ISR_INIT; rtc_lock_regs(); } +#endif #if defined(CONFIG_LOW_POWER_IDLE) && defined(CONFIG_COMMON_RUNTIME) #ifdef CONFIG_CMD_IDLE_STATS diff --git a/chip/stm32/clock-stm32f4.c b/chip/stm32/clock-stm32f4.c index 64f3d331e2..eacb3d59e0 100644 --- a/chip/stm32/clock-stm32f4.c +++ b/chip/stm32/clock-stm32f4.c @@ -34,12 +34,12 @@ #define RTC_PREDIV_S (RTC_FREQ - 1) #define US_PER_RTC_TICK (1000000 / RTC_FREQ) -int32_t rtcss_to_us(uint32_t rtcss) +int32_t rtc_ssr_to_us(uint32_t rtcss) { return ((RTC_PREDIV_S - rtcss) * US_PER_RTC_TICK); } -uint32_t us_to_rtcss(int32_t us) +uint32_t us_to_rtc_ssr(int32_t us) { return (RTC_PREDIV_S - (us / US_PER_RTC_TICK)); } -- cgit v1.2.1