1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/stm32/periph/rtc_all.c
2024-03-29 10:19:42 +01:00

449 lines
13 KiB
C

/*
* Copyright (C) 2015 Lari Lehtomäki
* 2016 Laksh Bhatia
* 2016-2017 OTA keys S.A.
* 2017 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_stm32
* @{
* @file
* @brief Low-level RTC driver implementation
*
* @author Lari Lehtomäki <lari@lehtomaki.fi>
* @author Laksh Bhatia <bhatialaksh3@gmail.com>
* @author Vincent Dupont <vincent@otakeys.com>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/
#include <time.h>
#include "cpu.h"
#include "stmclk.h"
#include "periph/rtc.h"
/* map some CPU specific register names */
#if defined (CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
#define EN_REG (RCC->CSR)
#define EN_BIT (RCC_CSR_RTCEN)
#define CLKSEL_MASK (RCC_CSR_RTCSEL)
#define CLKSEL_LSE (RCC_CSR_RTCSEL_LSE)
#define CLKSEL_LSI (RCC_CSR_RTCSEL_LSI)
#else
#define EN_REG (RCC->BDCR)
#define EN_BIT (RCC_BDCR_RTCEN)
#define CLKSEL_MASK (RCC_BDCR_RTCSEL_0 | RCC_BDCR_RTCSEL_1)
#define CLKSEL_LSE (RCC_BDCR_RTCSEL_0)
#define CLKSEL_LSI (RCC_BDCR_RTCSEL_1)
#endif
/* map some EXTI register names */
#if defined(CPU_FAM_STM32L4) || defined(CPU_FAM_STM32WB) || \
defined(CPU_FAM_STM32G4)
#define EXTI_REG_RTSR (EXTI->RTSR1)
#define EXTI_REG_FTSR (EXTI->FTSR1)
#define EXTI_REG_PR (EXTI->PR1)
#define EXTI_REG_IMR (EXTI->IMR1)
#elif defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32U5)
#define EXTI_REG_RTSR (EXTI->RTSR1)
#define EXTI_REG_FTSR (EXTI->FTSR1)
#define EXTI_REG_PR (EXTI->RPR1)
#define EXTI_REG_IMR (EXTI->IMR1)
#elif defined(CPU_FAM_STM32L5)
#define EXTI_REG_IMR (EXTI->IMR1)
#else
#define EXTI_REG_RTSR (EXTI->RTSR)
#define EXTI_REG_FTSR (EXTI->FTSR)
#define EXTI_REG_PR (EXTI->PR)
#define EXTI_REG_IMR (EXTI->IMR)
#endif
/* map some RTC register names and bitfield */
#if defined(CPU_FAM_STM32G4) || defined(CPU_FAM_STM32G0)
#define RTC_REG_ISR RTC->ICSR
#define RTC_REG_SR RTC->SR
#define RTC_REG_SCR RTC->SCR
#define RTC_ISR_RSF RTC_ICSR_RSF
#define RTC_ISR_INIT RTC_ICSR_INIT
#define RTC_ISR_INITF RTC_ICSR_INITF
#define RTC_ISR_ALRAWF RTC_ICSR_ALRAWF
#define RTC_ISR_ALRAF RTC_SR_ALRAF
#elif defined(CPU_FAM_STM32L5)
#define RTC_REG_ISR RTC->ICSR
#define RTC_REG_SR RTC->SR
#define RTC_REG_SCR RTC->SCR
#define RTC_ISR_RSF RTC_ICSR_RSF
#define RTC_ISR_INIT RTC_ICSR_INIT
#define RTC_ISR_INITF RTC_ICSR_INITF
#elif defined(CPU_FAM_STM32U5)
#define RTC_REG_ISR RTC->ICSR
#define RTC_REG_SR RTC->SR
#define RTC_REG_SCR RTC->SCR
#define RTC_ISR_RSF RTC_ICSR_RSF
#define RTC_ISR_INIT RTC_ICSR_INIT
#define RTC_ISR_INITF RTC_ICSR_INITF
#define RTC_ISR_ALRAF RTC_SR_ALRAF
#else
#define RTC_REG_ISR RTC->ISR
#endif
/* interrupt line name mapping */
#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) || \
defined(CPU_FAM_STM32L5) || defined(CPU_FAM_STM32U5)
#define IRQN (RTC_IRQn)
#define ISR_NAME isr_rtc
#elif defined(CPU_FAM_STM32G0)
#define IRQN (RTC_TAMP_IRQn)
#define ISR_NAME isr_rtc_tamp
#else
#define IRQN (RTC_Alarm_IRQn)
#define ISR_NAME isr_rtc_alarm
#endif
/* EXTI bitfield mapping */
#if defined(CPU_FAM_STM32L4)
#define EXTI_IMR_BIT (EXTI_IMR1_IM18)
#define EXTI_FTSR_BIT (EXTI_FTSR1_FT18)
#define EXTI_RTSR_BIT (EXTI_RTSR1_RT18)
#define EXTI_PR_BIT (EXTI_PR1_PIF18)
#elif defined(CPU_FAM_STM32L5)
#define EXTI_IMR_BIT (EXTI_IMR1_IM17)
#elif defined(CPU_FAM_STM32WB) || defined(CPU_FAM_STM32G4)
#define EXTI_IMR_BIT (EXTI_IMR1_IM17)
#define EXTI_FTSR_BIT (EXTI_FTSR1_FT17)
#define EXTI_RTSR_BIT (EXTI_RTSR1_RT17)
#define EXTI_PR_BIT (EXTI_PR1_PIF17)
#elif defined(CPU_FAM_STM32G0) || defined(CPU_FAM_STM32U5)
#define EXTI_IMR_BIT (EXTI_IMR1_IM11)
#define EXTI_FTSR_BIT (EXTI_FTSR1_FT11)
#define EXTI_RTSR_BIT (EXTI_RTSR1_RT11)
#define EXTI_PR_BIT (EXTI_RPR1_RPIF11)
#else
#if defined(CPU_FAM_STM32L0)
#define EXTI_IMR_BIT (EXTI_IMR_IM17)
#else
#define EXTI_IMR_BIT (EXTI_IMR_MR17)
#endif
#define EXTI_FTSR_BIT (EXTI_FTSR_TR17)
#define EXTI_RTSR_BIT (EXTI_RTSR_TR17)
#define EXTI_PR_BIT (EXTI_PR_PR17)
#endif
/* write protection values */
#define WPK1 (0xCA)
#define WPK2 (0x53)
/* define TR, DR, and ALRMAR position and masks */
#define TR_H_MASK (RTC_TR_HU | RTC_TR_HT)
#define TR_M_MASK (RTC_TR_MNU | RTC_TR_MNT)
#define TR_S_MASK (RTC_TR_SU | RTC_TR_ST)
#define DR_Y_MASK (RTC_DR_YU | RTC_DR_YT)
#define DR_M_MASK (RTC_DR_MU | RTC_DR_MT)
#define DR_D_MASK (RTC_DR_DU | RTC_DR_DT)
#define ALRM_D_MASK (RTC_ALRMAR_DU | RTC_ALRMAR_DT)
#define ALRM_H_MASK (RTC_ALRMAR_HU | RTC_ALRMAR_HT)
#define ALRM_M_MASK (RTC_ALRMAR_MNU | RTC_ALRMAR_MNT)
#define ALRM_S_MASK (RTC_ALRMAR_SU | RTC_ALRMAR_ST)
#ifndef RTC_DR_YU_Pos
#define RTC_DR_YU_Pos (16U)
#endif
#ifndef RTC_DR_MU_Pos
#define RTC_DR_MU_Pos (8U)
#endif
#ifndef RTC_DR_DU_Pos
#define RTC_DR_DU_Pos (0U)
#endif
#ifndef RTC_TR_HU_Pos
#define RTC_TR_HU_Pos (16U)
#endif
#ifndef RTC_TR_MNU_Pos
#define RTC_TR_MNU_Pos (8U)
#endif
#ifndef RTC_TR_SU_Pos
#define RTC_TR_SU_Pos (0U)
#endif
#ifndef RTC_ALRMAR_DU_Pos
#define RTC_ALRMAR_DU_Pos (24U)
#endif
#ifndef RTC_ALRMAR_HU_Pos
#define RTC_ALRMAR_HU_Pos (16U)
#endif
#ifndef RTC_ALRMAR_MNU_Pos
#define RTC_ALRMAR_MNU_Pos (8U)
#endif
#ifndef RTC_ALRMAR_SU_Pos
#define RTC_ALRMAR_SU_Pos (0U)
#endif
/* figure out sync and async prescaler */
#if IS_ACTIVE(CONFIG_BOARD_HAS_LSE)
#define PRE_SYNC (255)
#define PRE_ASYNC (127)
#elif (CLOCK_LSI == 40000)
#define PRE_SYNC (319)
#define PRE_ASYNC (124)
#elif (CLOCK_LSI == 37000)
#define PRE_SYNC (295)
#define PRE_ASYNC (124)
#elif (CLOCK_LSI == 32000)
#define PRE_SYNC (249)
#define PRE_ASYNC (127)
#else
#error "rtc: unable to determine RTC SYNC and ASYNC prescalers from LSI value"
#endif
/* struct tm counts years since 1900 but RTC has only two-digit year, hence the offset */
#define YEAR_OFFSET (RIOT_EPOCH - 1900)
/* Use a magic number to determine the initial RTC source. This will be used
to know if a reset of the RTC is required at initialization. */
#if IS_ACTIVE(CONFIG_BOARD_HAS_LSE)
#define MAGIC_CLCK_NUMBER (0x1970)
#else
#define MAGIC_CLCK_NUMBER (0x1971)
#endif
static struct {
rtc_alarm_cb_t cb; /**< callback called from RTC interrupt */
void *arg; /**< argument passed to the callback */
} isr_ctx;
static uint32_t val2bcd(int val, int shift, uint32_t mask)
{
uint32_t bcdhigh = 0;
while (val >= 10) {
bcdhigh++;
val -= 10;
}
return ((((bcdhigh << 4) | val) << shift) & mask);
}
static int bcd2val(uint32_t val, int shift, uint32_t mask)
{
int tmp = (int)((val & mask) >> shift);
return (((tmp >> 4) * 10) + (tmp & 0x0f));
}
void rtc_unlock(void)
{
/* enable backup clock domain */
stmclk_dbp_unlock();
/* unlock RTC */
RTC->WPR = WPK1;
RTC->WPR = WPK2;
/* enter RTC init mode */
RTC_REG_ISR |= RTC_ISR_INIT;
while (!(RTC_REG_ISR & RTC_ISR_INITF)) {}
}
void rtc_lock(void)
{
/* exit RTC init mode */
RTC_REG_ISR &= ~RTC_ISR_INIT;
while (RTC_REG_ISR & RTC_ISR_INITF) {}
/* lock RTC device */
RTC->WPR = 0xff;
/* disable backup clock domain */
stmclk_dbp_lock();
}
void rtc_init(void)
{
stmclk_dbp_unlock();
#if defined(CPU_FAM_STM32L0) || defined(CPU_FAM_STM32L1)
/* Compare the stored magic number with the current one. If it's different
it means the clock source has changed and thus a RTC reset is
required. */
if (RTC->BKP0R != MAGIC_CLCK_NUMBER) {
RCC->CSR |= RCC_CSR_RTCRST;
RCC->CSR &= ~RCC_CSR_RTCRST;
RTC->BKP0R = MAGIC_CLCK_NUMBER; /* Store the new magic number */
}
#endif
stmclk_dbp_lock();
/* enable low frequency clock */
stmclk_enable_lfclk();
/* select input clock and enable the RTC */
stmclk_dbp_unlock();
#if defined(CPU_FAM_STM32L5)
periph_clk_en(APB1, RCC_APB1ENR1_RTCAPBEN);
#elif defined(CPU_FAM_STM32G0)
periph_clk_en(APB1, RCC_APBENR1_RTCAPBEN);
#elif defined(CPU_FAM_STM32U5)
periph_clk_en(APB3, RCC_APB3ENR_RTCAPBEN);
#endif
EN_REG &= ~(CLKSEL_MASK);
#if IS_ACTIVE(CONFIG_BOARD_HAS_LSE)
EN_REG |= (CLKSEL_LSE | EN_BIT);
#else
EN_REG |= (CLKSEL_LSI | EN_BIT);
#endif
rtc_unlock();
/* reset configuration */
RTC->CR = 0;
RTC_REG_ISR = RTC_ISR_INIT;
/* configure prescaler (RTC PRER) */
RTC->PRER = (PRE_SYNC | (PRE_ASYNC << 16));
rtc_lock();
/* configure the EXTI channel, as RTC interrupts are routed through it.
* Needs to be configured to trigger on rising edges. */
EXTI_REG_IMR |= EXTI_IMR_BIT;
#if !defined(CPU_FAM_STM32L5)
EXTI_REG_FTSR &= ~(EXTI_FTSR_BIT);
EXTI_REG_RTSR |= EXTI_RTSR_BIT;
EXTI_REG_PR = EXTI_PR_BIT;
#endif
/* enable global RTC interrupt */
NVIC_EnableIRQ(IRQN);
}
int rtc_set_time(struct tm *time)
{
/* normalize input */
rtc_tm_normalize(time);
rtc_unlock();
RTC->DR = (val2bcd((time->tm_year - YEAR_OFFSET), RTC_DR_YU_Pos, DR_Y_MASK) |
val2bcd(time->tm_mon + 1, RTC_DR_MU_Pos, DR_M_MASK) |
val2bcd(time->tm_mday, RTC_DR_DU_Pos, DR_D_MASK));
RTC->TR = (val2bcd(time->tm_hour, RTC_TR_HU_Pos, TR_H_MASK) |
val2bcd(time->tm_min, RTC_TR_MNU_Pos, TR_M_MASK) |
val2bcd(time->tm_sec, RTC_TR_SU_Pos, TR_S_MASK));
rtc_lock();
while (!(RTC_REG_ISR & RTC_ISR_RSF)) {}
return 0;
}
int rtc_get_time(struct tm *time)
{
/* save current time */
uint32_t tr = RTC->TR;
uint32_t dr = RTC->DR;
time->tm_year = bcd2val(dr, RTC_DR_YU_Pos, DR_Y_MASK) + YEAR_OFFSET;
time->tm_mon = bcd2val(dr, RTC_DR_MU_Pos, DR_M_MASK) - 1;
time->tm_mday = bcd2val(dr, RTC_DR_DU_Pos, DR_D_MASK);
time->tm_hour = bcd2val(tr, RTC_TR_HU_Pos, TR_H_MASK);
time->tm_min = bcd2val(tr, RTC_TR_MNU_Pos, TR_M_MASK);
time->tm_sec = bcd2val(tr, RTC_TR_SU_Pos, TR_S_MASK);
return 0;
}
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
{
/* normalize input */
rtc_tm_normalize(time);
/* disable existing alarm (if enabled) */
rtc_clear_alarm();
rtc_unlock();
/* save callback and argument */
isr_ctx.cb = cb;
isr_ctx.arg = arg;
/* set wakeup time */
RTC->ALRMAR = (val2bcd(time->tm_mday, RTC_ALRMAR_DU_Pos, ALRM_D_MASK) |
val2bcd(time->tm_hour, RTC_ALRMAR_HU_Pos, ALRM_H_MASK) |
val2bcd(time->tm_min, RTC_ALRMAR_MNU_Pos, ALRM_M_MASK) |
val2bcd(time->tm_sec, RTC_ALRMAR_SU_Pos, ALRM_S_MASK));
/* Enable Alarm A */
#if !defined(CPU_FAM_STM32L5)
RTC_REG_ISR &= ~(RTC_ISR_ALRAF);
#else
RTC_REG_SCR = RTC_SCR_CALRAF;
#endif
RTC->CR |= (RTC_CR_ALRAE | RTC_CR_ALRAIE);
rtc_lock();
return 0;
}
int rtc_get_alarm(struct tm *time)
{
uint32_t dr = RTC->DR;
uint32_t alrm = RTC->ALRMAR;
time->tm_year = bcd2val(dr, RTC_DR_YU_Pos, DR_Y_MASK) + YEAR_OFFSET;
time->tm_mon = bcd2val(dr, RTC_DR_MU_Pos, DR_M_MASK) - 1;
time->tm_mday = bcd2val(alrm, RTC_ALRMAR_DU_Pos, ALRM_D_MASK);
time->tm_hour = bcd2val(alrm, RTC_ALRMAR_HU_Pos, ALRM_H_MASK);
time->tm_min = bcd2val(alrm, RTC_ALRMAR_MNU_Pos, ALRM_M_MASK);
time->tm_sec = bcd2val(alrm, RTC_ALRMAR_SU_Pos, ALRM_S_MASK);
return 0;
}
void rtc_clear_alarm(void)
{
rtc_unlock();
RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE);
#if !defined(CPU_FAM_STM32L5) && !defined(CPU_FAM_STM32U5)
while (!(RTC_REG_ISR & RTC_ISR_ALRAWF)) {}
#else
RTC_REG_SCR = RTC_SCR_CALRAF;
#endif
isr_ctx.cb = NULL;
isr_ctx.arg = NULL;
rtc_lock();
}
void rtc_poweron(void)
{
stmclk_dbp_unlock();
EN_REG |= EN_BIT;
stmclk_dbp_lock();
}
void rtc_poweroff(void)
{
stmclk_dbp_unlock();
EN_REG &= ~EN_BIT;
stmclk_dbp_lock();
}
void ISR_NAME(void)
{
#if !defined(CPU_FAM_STM32L5) && !defined(CPU_FAM_STM32G0) && !defined(CPU_FAM_STM32U5)
if (RTC_REG_ISR & RTC_ISR_ALRAF) {
if (isr_ctx.cb != NULL) {
isr_ctx.cb(isr_ctx.arg);
}
RTC_REG_ISR &= ~RTC_ISR_ALRAF;
}
EXTI_REG_PR = EXTI_PR_BIT; /* only clear the associated bit */
#else
if (RTC_REG_SR & RTC_SR_ALRAF) {
if (isr_ctx.cb != NULL) {
isr_ctx.cb(isr_ctx.arg);
}
/* RTC registers are write access protected, DBP bit must be set to enable access */
stmclk_dbp_unlock();
RTC_REG_SCR = RTC_SCR_CALRAF;
/* Lock to avoid parasitic write access */
stmclk_dbp_lock();
}
#endif
cortexm_isr_end();
}