mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-07 16:11:43 +00:00
stm32f0: add RTC alarm functionality
Implement RTC alarm, with resolution 50us, for stm32f0. This is useful for using low power modes and waking up after set period of time. BUG=chrome-os-partner:31226, chrome-os-partner:28335 BRANCH=none TEST=tested on samus_pd with CONFIG_CMD_RTC_ALARM defined and used rtc_alarm console command to test various timeout periods. Change-Id: Ibabd8662cfbea654c7de387669f7be83af4fd79d Signed-off-by: Alec Berg <alecaberg@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/218322 Reviewed-by: Todd Broch <tbroch@chromium.org>
This commit is contained in:
committed by
chrome-internal-fetch
parent
0616b24162
commit
2be0577fe0
@@ -12,6 +12,8 @@
|
||||
#include "cpu.h"
|
||||
#include "hooks.h"
|
||||
#include "registers.h"
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "util.h"
|
||||
|
||||
/* use 48Mhz USB-synchronized High-speed oscillator */
|
||||
@@ -20,6 +22,153 @@
|
||||
/* use PLL at 38.4MHz as system clock. */
|
||||
#define PLL_CLOCK 38400000
|
||||
|
||||
/*
|
||||
* RTC clock frequency (connected to LSI clock)
|
||||
*
|
||||
* TODO(crosbug.com/p/12281): Calibrate LSI frequency on a per-chip basis. The
|
||||
* LSI on any given chip can be between 30 kHz to 60 kHz. Without calibration,
|
||||
* LSI frequency may be off by as much as 50%. Fortunately, we don't do any
|
||||
* high-precision delays based solely on LSI.
|
||||
*/
|
||||
/*
|
||||
* Set synchronous clock freq to HSI/2 (20kHz) to maximize subsecond
|
||||
* resolution. Set asynchronous clock to 1 Hz.
|
||||
*/
|
||||
#define RTC_FREQ (40000 / 2) /* Hz */
|
||||
#define RTC_PREDIV_S (RTC_FREQ - 1)
|
||||
#define RTC_PREDIV_A 1
|
||||
#define US_PER_RTC_TICK (1000000 / RTC_FREQ)
|
||||
|
||||
/* Lock and unlock RTC write access */
|
||||
static inline void rtc_lock_regs(void)
|
||||
{
|
||||
STM32_RTC_WPR = 0xff;
|
||||
}
|
||||
static inline void rtc_unlock_regs(void)
|
||||
{
|
||||
STM32_RTC_WPR = 0xca;
|
||||
STM32_RTC_WPR = 0x53;
|
||||
}
|
||||
|
||||
/* Convert between RTC regs in BCD and seconds */
|
||||
static inline uint32_t rtc_to_sec(uint32_t rtc)
|
||||
{
|
||||
uint32_t sec;
|
||||
|
||||
/* convert the hours field */
|
||||
sec = (((rtc & 0x300000) >> 20) * 10 + ((rtc & 0xf0000) >> 16)) * 3600;
|
||||
/* convert the minutes field */
|
||||
sec += (((rtc & 0x7000) >> 12) * 10 + ((rtc & 0xf00) >> 8)) * 60;
|
||||
/* convert the seconds field */
|
||||
sec += ((rtc & 0x70) >> 4) * 10 + (rtc & 0xf);
|
||||
|
||||
return sec;
|
||||
}
|
||||
static inline uint32_t sec_to_rtc(uint32_t sec)
|
||||
{
|
||||
uint32_t rtc;
|
||||
|
||||
/* convert the hours field */
|
||||
rtc = ((sec / 36000) << 20) | (((sec / 3600) % 10) << 16);
|
||||
/* convert the minutes field */
|
||||
rtc |= (((sec % 3600) / 600) << 12) | (((sec % 600) / 60) << 8);
|
||||
/* convert the seconds field */
|
||||
rtc |= (((sec % 60) / 10) << 4) | (sec % 10);
|
||||
|
||||
return rtc;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Return time diff between two rtc readings */
|
||||
static inline int32_t get_rtc_diff(uint32_t rtc0, uint32_t rtc0ss,
|
||||
uint32_t rtc1, uint32_t rtc1ss)
|
||||
{
|
||||
int32_t diff;
|
||||
|
||||
/* Note: this only looks at the diff mod 10 seconds */
|
||||
diff = ((rtc1 & 0xf) * SECOND +
|
||||
(RTC_PREDIV_S - rtc1ss) * US_PER_RTC_TICK) -
|
||||
((rtc0 & 0xf) * SECOND +
|
||||
(RTC_PREDIV_S - rtc0ss) * US_PER_RTC_TICK);
|
||||
|
||||
return (diff < 0) ? (diff + 10*SECOND) : diff;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void rtc_read(uint32_t *rtc, uint32_t *rtcss)
|
||||
{
|
||||
/* Read current time synchronously */
|
||||
do {
|
||||
*rtc = STM32_RTC_TR;
|
||||
*rtcss = STM32_RTC_SSR;
|
||||
} while (*rtc != STM32_RTC_TR);
|
||||
}
|
||||
|
||||
void set_rtc_alarm(uint32_t delay_s, uint32_t delay_us,
|
||||
uint32_t *rtc, uint32_t *rtcss)
|
||||
{
|
||||
uint32_t alarm_sec, alarm_us;
|
||||
|
||||
/* Alarm must be within 1 day (86400 seconds) */
|
||||
ASSERT((delay_s + delay_us / SECOND) < 86400);
|
||||
|
||||
rtc_unlock_regs();
|
||||
|
||||
/* Make sure alarm is disabled */
|
||||
STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
|
||||
while (!(STM32_RTC_ISR & STM32_RTC_ISR_ALRAWF))
|
||||
;
|
||||
STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
|
||||
|
||||
rtc_read(rtc, rtcss);
|
||||
|
||||
/* Calculate alarm time */
|
||||
alarm_sec = rtc_to_sec(*rtc) + delay_s;
|
||||
alarm_us = (RTC_PREDIV_S - *rtcss) * US_PER_RTC_TICK + delay_us;
|
||||
alarm_sec = (alarm_sec + alarm_us / SECOND) % 86400;
|
||||
alarm_us = alarm_us % 1000000;
|
||||
|
||||
/* Set alarm time */
|
||||
STM32_RTC_ALRMAR = sec_to_rtc(alarm_sec);
|
||||
STM32_RTC_ALRMASSR = RTC_PREDIV_S - (alarm_us / US_PER_RTC_TICK);
|
||||
/* Check for match on hours, minutes, seconds, and subsecond */
|
||||
STM32_RTC_ALRMAR |= 0xc0000000;
|
||||
STM32_RTC_ALRMASSR |= 0x0f000000;
|
||||
|
||||
/* Enable alarm and alarm interrupt */
|
||||
STM32_EXTI_PR = EXTI_RTC_ALR_EVENT;
|
||||
STM32_EXTI_IMR |= EXTI_RTC_ALR_EVENT;
|
||||
STM32_RTC_CR |= STM32_RTC_CR_ALRAE;
|
||||
|
||||
rtc_lock_regs();
|
||||
}
|
||||
|
||||
void reset_rtc_alarm(uint32_t *rtc, uint32_t *rtcss)
|
||||
{
|
||||
rtc_unlock_regs();
|
||||
|
||||
/* Disable alarm */
|
||||
STM32_RTC_CR &= ~STM32_RTC_CR_ALRAE;
|
||||
STM32_RTC_ISR &= ~STM32_RTC_ISR_ALRAF;
|
||||
|
||||
/* Disable RTC alarm interrupt */
|
||||
STM32_EXTI_IMR &= ~EXTI_RTC_ALR_EVENT;
|
||||
STM32_EXTI_PR = EXTI_RTC_ALR_EVENT;
|
||||
|
||||
/* Read current time */
|
||||
rtc_read(rtc, rtcss);
|
||||
|
||||
rtc_lock_regs();
|
||||
}
|
||||
|
||||
void __rtc_alarm_irq(void)
|
||||
{
|
||||
uint32_t rtc, rtcss;
|
||||
|
||||
reset_rtc_alarm(&rtc, &rtcss);
|
||||
}
|
||||
DECLARE_IRQ(STM32_IRQ_RTC_WAKEUP, __rtc_alarm_irq, 1);
|
||||
|
||||
int clock_get_freq(void)
|
||||
{
|
||||
return CPU_CLOCK;
|
||||
@@ -98,4 +247,61 @@ void clock_init(void)
|
||||
#else
|
||||
#error "CPU_CLOCK must be either 48MHz or 38.4MHz"
|
||||
#endif
|
||||
|
||||
rtc_unlock_regs();
|
||||
|
||||
/* Enter RTC initialize mode */
|
||||
STM32_RTC_ISR |= STM32_RTC_ISR_INIT;
|
||||
while (!(STM32_RTC_ISR & STM32_RTC_ISR_INITF))
|
||||
;
|
||||
|
||||
/* Set clock prescalars */
|
||||
STM32_RTC_PRER = (RTC_PREDIV_A << 16) | RTC_PREDIV_S;
|
||||
|
||||
/* Start RTC timer */
|
||||
STM32_RTC_ISR &= ~STM32_RTC_ISR_INIT;
|
||||
while (STM32_RTC_ISR & STM32_RTC_ISR_INITF)
|
||||
;
|
||||
|
||||
/* Enable RTC alarm interrupt */
|
||||
STM32_RTC_CR |= STM32_RTC_CR_ALRAIE | STM32_RTC_CR_BYPSHAD;
|
||||
STM32_EXTI_RTSR |= EXTI_RTC_ALR_EVENT;
|
||||
task_enable_irq(STM32_IRQ_RTC_WAKEUP);
|
||||
|
||||
rtc_lock_regs();
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Console commands */
|
||||
|
||||
#ifdef CONFIG_CMD_RTC_ALARM
|
||||
static int command_rtc_alarm_test(int argc, char **argv)
|
||||
{
|
||||
int s = 1, us = 0;
|
||||
uint32_t rtc, rtcss;
|
||||
char *e;
|
||||
|
||||
ccprintf("Setting RTC alarm\n");
|
||||
|
||||
if (argc > 1) {
|
||||
s = strtoi(argv[1], &e, 10);
|
||||
if (*e)
|
||||
return EC_ERROR_PARAM1;
|
||||
|
||||
}
|
||||
if (argc > 2) {
|
||||
us = strtoi(argv[2], &e, 10);
|
||||
if (*e)
|
||||
return EC_ERROR_PARAM2;
|
||||
|
||||
}
|
||||
|
||||
set_rtc_alarm(s, us, &rtc, &rtcss);
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test,
|
||||
"[seconds [microseconds]]",
|
||||
"Test alarm",
|
||||
NULL);
|
||||
#endif /* CONFIG_CMD_RTC_ALARM */
|
||||
|
||||
|
||||
@@ -620,16 +620,26 @@ typedef volatile struct timer_ctlr timer_ctlr_t;
|
||||
#define STM32_RTC_TR REG32(STM32_RTC_BASE + 0x00)
|
||||
#define STM32_RTC_DR REG32(STM32_RTC_BASE + 0x04)
|
||||
#define STM32_RTC_CR REG32(STM32_RTC_BASE + 0x08)
|
||||
#define STM32_RTC_CR_BYPSHAD (1 << 5)
|
||||
#define STM32_RTC_CR_ALRAE (1 << 8)
|
||||
#define STM32_RTC_CR_ALRAIE (1 << 12)
|
||||
#define STM32_RTC_ISR REG32(STM32_RTC_BASE + 0x0C)
|
||||
#define STM32_RTC_ISR_ALRAWF (1 << 0)
|
||||
#define STM32_RTC_ISR_RSF (1 << 5)
|
||||
#define STM32_RTC_ISR_INITF (1 << 6)
|
||||
#define STM32_RTC_ISR_INIT (1 << 7)
|
||||
#define STM32_RTC_ISR_ALRAF (1 << 8)
|
||||
#define STM32_RTC_PRER REG32(STM32_RTC_BASE + 0x10)
|
||||
#define STM32_RTC_WUTR REG32(STM32_RTC_BASE + 0x14)
|
||||
#define STM32_RTC_CALIBR REG32(STM32_RTC_BASE + 0x18)
|
||||
#define STM32_RTC_ALRMAR REG32(STM32_RTC_BASE + 0x1C)
|
||||
#define STM32_RTC_ALRMBR REG32(STM32_RTC_BASE + 0x20)
|
||||
#define STM32_RTC_WPR REG32(STM32_RTC_BASE + 0x24)
|
||||
#define STM32_RTC_SSR REG32(STM32_RTC_BASE + 0x28)
|
||||
#define STM32_RTC_TSTR REG32(STM32_RTC_BASE + 0x30)
|
||||
#define STM32_RTC_TSDR REG32(STM32_RTC_BASE + 0x34)
|
||||
#define STM32_RTC_TAFCR REG32(STM32_RTC_BASE + 0x40)
|
||||
#define STM32_RTC_ALRMASSR REG32(STM32_RTC_BASE + 0x44)
|
||||
#define STM32_RTC_BACKUP(n) REG32(STM32_RTC_BASE + 0x50 + 4 * (n))
|
||||
|
||||
#define STM32_BKP_DATA(n) STM32_RTC_BACKUP(n)
|
||||
@@ -815,6 +825,10 @@ typedef volatile struct stm32_spi_regs stm32_spi_regs_t;
|
||||
#define STM32_EXTI_SWIER REG32(STM32_EXTI_BASE + 0x10)
|
||||
#define STM32_EXTI_PR REG32(STM32_EXTI_BASE + 0x14)
|
||||
|
||||
#if defined(CHIP_FAMILY_STM32F0)
|
||||
#define EXTI_RTC_ALR_EVENT (1 << 17)
|
||||
#endif
|
||||
|
||||
/* --- ADC --- */
|
||||
|
||||
#if defined(CHIP_VARIANT_STM32TS60)
|
||||
|
||||
Reference in New Issue
Block a user