summaryrefslogtreecommitdiff
path: root/chip/stm32
diff options
context:
space:
mode:
authorPhilip Chen <philipchen@google.com>2017-09-10 10:31:19 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-09-27 19:03:03 -0700
commit884b790a6560e13975395d1982aa5e6634e7842c (patch)
treeb97eb50e1ef321787e95e424f53a052cb2a04898 /chip/stm32
parent49958cf5c229c2719304eef1fc8c0c40e8620ac4 (diff)
downloadchrome-ec-884b790a6560e13975395d1982aa5e6634e7842c.tar.gz
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 <philipchen@google.com> Reviewed-on: https://chromium-review.googlesource.com/659337 Commit-Ready: Philip Chen <philipchen@chromium.org> Tested-by: Philip Chen <philipchen@chromium.org> Reviewed-by: Matthias Kaehlcke <mka@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org>
Diffstat (limited to 'chip/stm32')
-rw-r--r--chip/stm32/clock-f.c195
-rw-r--r--chip/stm32/clock-f.h31
-rw-r--r--chip/stm32/clock-stm32f0.c20
-rw-r--r--chip/stm32/clock-stm32f4.c4
4 files changed, 170 insertions, 80 deletions
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));
}